changeset 13104:e1e5462b7d81

[gaim-migrate @ 15466] Rework lots of date parsing. I either introduced a whole lot of bugs, or I've made sure all dates are localized properly now. Only time will tell which it is... ;) committer: Tailor Script <tailor@pidgin.im>
author Richard Laager <rlaager@wiktel.com>
date Thu, 02 Feb 2006 19:50:51 +0000
parents a6811e213977
children e347b2217b1b
files plugins/ChangeLog.API plugins/history.c plugins/perl/common/Util.xs plugins/perl/common/module.h plugins/perl/common/typemap plugins/timestamp.c plugins/timestamp_format.c src/debug.c src/gtkconv.c src/gtkdebug.c src/gtklog.c src/gtkpounce.c src/internal.h src/log.c src/protocols/irc/msgs.c src/protocols/jabber/iq.c src/protocols/msn/msn.c src/protocols/novell/novell.c src/protocols/oscar/icq.c src/protocols/oscar/oscar.c src/protocols/yahoo/yahoo_profile.c src/util.c src/util.h
diffstat 23 files changed, 313 insertions(+), 257 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/ChangeLog.API	Thu Feb 02 19:39:12 2006 +0000
+++ b/plugins/ChangeLog.API	Thu Feb 02 19:50:51 2006 +0000
@@ -95,6 +95,8 @@
 	  may now pass NULL if you're not interested in a specific formatting.
 	* Smiley Themes: Backslashes must be backslash-escaped.
 	* Plugins: Depedencies are now honored when unloading plugins.
+	* gaim_markup_extract_info_field(): Added format_cb parameter.
+	* gaim_str_to_time(): Added support for parsing the MM/DD/YYYY format.
 
 	Removed:
 	* gaim_gtk_sound_{get,set}_mute() (replaced by the /gaim/gtk/sound/mute
@@ -148,7 +150,8 @@
 	  now, so this would always be TRUE now.
 	* gaim_blist_node_action_new(); use gaim_menu_action_new() instead
 	* gaim_date()
-	* gaim_date_full()
+	* gaim_date_full(): See gaim_date_format_full()
+	* gaim_strftime(): See gaim_utf8_strftime()
 
 	Added:
 	* gaim_prefs_disconnect_by_handle()
@@ -245,6 +248,12 @@
 	* gaim_url_fetch_request()
 	* gaim_menu_action_new()
 	* gaim_menu_action_free()
+	* GaimInfoFieldFormatCallback
+	* gaim_utf8_strftime()
+	* gaim_date_format_short()
+	* gaim_date_format_long()
+	* gaim_date_format_full()
+	* gaim_time_format()
 
 	Signals - Changed:  (See the Doxygen docs for details on all signals.)
 	* Signal propagation now stops after a handler returns a non-NULL value.
--- a/plugins/history.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/plugins/history.c	Thu Feb 02 19:50:51 2006 +0000
@@ -40,8 +40,6 @@
 	char *history;
 	GaimGtkConversation *gtkconv;
 	GtkIMHtmlOptions options = GTK_IMHTML_NO_COLOURS;
-	time_t tm;
-	char day[64];
 	char *header;
 	char *protocol;
 
@@ -116,9 +114,8 @@
 	gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml),
 							      gaim_account_get_protocol_name(((GaimLog*)logs->data)->account));
 
-	tm = ((GaimLog *)logs->data)->time;
-	gaim_strftime(day, sizeof(day), "%c", localtime(&tm));
-	header = g_strdup_printf("<b>Conversation with %s on %s:</b><br>", alias, day);
+	header = g_strdup_printf("<b>Conversation with %s on %s:</b><br>", alias,
+							 gaim_date_format_full(((GaimLog *)logs->data)->time));
 	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), header, options);
 	g_free(header);
 
--- a/plugins/perl/common/Util.xs	Thu Feb 02 19:39:12 2006 +0000
+++ b/plugins/perl/common/Util.xs	Thu Feb 02 19:50:51 2006 +0000
@@ -51,6 +51,18 @@
 	const char *path
 	int mode
 
+const char *
+gaim_date_format_full(time)
+	time_t time
+
+const char *
+gaim_date_format_long(tm)
+	const struct tm *tm
+
+const char *
+gaim_date_format_short(tm)
+	const struct tm *tm
+
 gboolean
 gaim_email_is_valid(address)
 	const char *address
@@ -67,7 +79,7 @@
 gaim_home_dir()
 
 gboolean
-gaim_markup_extract_info_field(str, len, dest, start_token, skip, end_token, check_value, no_value_token, display_name, is_link, link_prefix)
+gaim_markup_extract_info_field(str, len, dest, start_token, skip, end_token, check_value, no_value_token, display_name, is_link, link_prefix, format_cb)
 	const char *str
 	int len
 	GString *dest
@@ -79,6 +91,7 @@
 	const char *display_name
 	gboolean is_link
 	const char *link_prefix
+	Gaim::Util::InfoFieldFormatCallback format_cb
 
 gboolean
 gaim_markup_find_tag(needle, haystack, start, end, attributes)
@@ -188,13 +201,6 @@
 gaim_strdup_withhtml(src)
 	const gchar *src
 
-size_t
-gaim_strftime(s, max, format, tm)
-	char *s
-	size_t max
-	const char *format
-	const struct tm *tm
-
 gchar *
 gaim_strreplace(string, delimiter, replacement)
 	const char *string
@@ -215,6 +221,10 @@
 	int sec
 
 const char *
+gaim_time_format(tm)
+	const struct tm *tm
+
+const char *
 gaim_unescape_filename(str)
 	const char *str
 
@@ -242,6 +252,9 @@
 const char *
 gaim_user_dir()
 
+const char *
+gaim_utf8_strftime(const char *format, const struct tm *tm);
+
 void
 gaim_util_set_user_dir(dir)
 	const char *dir
--- a/plugins/perl/common/module.h	Thu Feb 02 19:39:12 2006 +0000
+++ b/plugins/perl/common/module.h	Thu Feb 02 19:50:51 2006 +0000
@@ -247,6 +247,7 @@
 typedef GaimStringref *			Gaim__Stringref;
 
 /* util.h */
+typedef GaimInfoFieldFormatCallback	Gaim__Util__InfoFieldFormatCallback;
 typedef GaimMenuAction *		Gaim__Menu__Action;
 
 /* value.h */
--- a/plugins/perl/common/typemap	Thu Feb 02 19:39:12 2006 +0000
+++ b/plugins/perl/common/typemap	Thu Feb 02 19:50:51 2006 +0000
@@ -155,6 +155,7 @@
 
 Gaim::StoredImage			T_GaimObj
 Gaim::Stringref				T_GaimObj
+Gaim::Util::InfoFieldFormatCallback	T_PTR
 Gaim::Value				T_GaimObj
 
 Gaim::Xfer				T_GaimObj
--- a/plugins/timestamp.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/plugins/timestamp.c	Thu Feb 02 19:50:51 2006 +0000
@@ -45,7 +45,7 @@
 	GaimConversation *c = (GaimConversation *)data;
 	GaimGtkConversation *conv = GAIM_GTK_CONVERSATION(c);
 	GtkTextIter iter;
-	char mdate[7];
+	const char *mdate;
 	int is_conversation_active;
 	time_t tim = time(NULL);
 
@@ -63,7 +63,7 @@
 		GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml));
 		gtk_text_buffer_get_end_iter(buffer, &iter);
 		gaim_conversation_set_data(c, "timestamp-conv-active", GINT_TO_POINTER(FALSE));
-		strftime(mdate, sizeof(mdate), "\n%H:%M", localtime(&tim));
+		mdate = gaim_utf8_strftime("\n%H:%M", localtime(&tim));
 		gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
 		gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(imhtml), &iter, &y, &height);
 		if(((y + height) - (rect.y + rect.height)) > height
--- a/plugins/timestamp_format.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/plugins/timestamp_format.c	Thu Feb 02 19:50:51 2006 +0000
@@ -3,6 +3,7 @@
 #include "debug.h"
 #include "log.h"
 #include "plugin.h"
+#include "util.h"
 #include "version.h"
 
 #include "gtkconv.h"
@@ -55,8 +56,6 @@
                                  gboolean force,
                                  const char *dates)
 {
-	char buf[64];
-
 	g_return_val_if_fail(conv != NULL, NULL);
 	g_return_val_if_fail(tm != NULL, NULL);
 	g_return_val_if_fail(dates != NULL, NULL);
@@ -67,18 +66,13 @@
 	    (time(NULL) > (mktime((struct tm *)tm) + 20*60)))
 	{
 		if (force)
-			strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
+			return g_strdup(gaim_utf8_strftime("%Y-%m-%d %H:%M:%S", tm));
 		else
-			strftime(buf, sizeof(buf), "%x %X", tm);
-
-		return g_strdup(buf);
+			return g_strdup(gaim_date_format_long(tm));
 	}
 
 	if (force)
-	{
-		strftime(buf, sizeof(buf), "%H:%M:%S", tm);
-		return g_strdup(buf);
-	}
+		return g_strdup(gaim_utf8_strftime("%Y-%m-%d %H:%M:%S", tm));
 
 	return NULL;
 }
@@ -111,11 +105,7 @@
 	if (log->type == GAIM_LOG_SYSTEM)
 	{
 		if (force)
-		{
-			char buf[64];
-			strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
-			return g_strdup(buf);
-		}
+			return g_strdup(gaim_utf8_strftime("%Y-%m-%d %H:%M:%S", tm));
 		else
 			return NULL;
 	}
--- a/src/debug.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/debug.c	Thu Feb 02 19:50:51 2006 +0000
@@ -25,6 +25,7 @@
 #include "debug.h"
 #include "internal.h"
 #include "prefs.h"
+#include "util.h"
 
 static GaimDebugUiOps *debug_ui_ops = NULL;
 
@@ -57,10 +58,10 @@
 		if ((category != NULL) &&
 			(gaim_prefs_exists("/core/debug/timestamps")) &&
 			(gaim_prefs_get_bool("/core/debug/timestamps"))) {
-			gchar mdate[64];
+			const char *mdate;
 
 			time_t mtime = time(NULL);
-			strftime(mdate, sizeof(mdate), "%H:%M:%S", localtime(&mtime));
+			mdate = gaim_utf8_strftime("%H:%M:%S", localtime(&mtime));
 			ts_s = g_strdup_printf("(%s) ", mdate);
 		} else {
 			ts_s = g_strdup("");
--- a/src/gtkconv.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/gtkconv.c	Thu Feb 02 19:50:51 2006 +0000
@@ -4666,14 +4666,10 @@
 	                                  conv, &tm);
 	if (mdate == NULL)
 	{
-		char buf[64];
-
 		if (time(NULL) > mtime + 20*60) /* show date if older than 20 minutes */
-			strftime(buf, sizeof(buf), "%x %X", &tm);
+			mdate = g_strdup(gaim_date_format_long(&tm));
 		else
-			strftime(buf, sizeof(buf), "%X", &tm);
-
-		mdate = g_strdup(buf);
+			mdate = g_strdup(gaim_time_format(&tm));
 	}
 
 	if(gc)
--- a/src/gtkdebug.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/gtkdebug.c	Thu Feb 02 19:50:51 2006 +0000
@@ -212,8 +212,6 @@
 	DebugWindow *win = (DebugWindow *)user_data;
 	FILE *fp;
 	char *tmp;
-	time_t now = time(NULL);
-	char date[64];
 
 	if ((fp = g_fopen(filename, "w+")) == NULL) {
 		gaim_notify_error(win, NULL, _("Unable to open file."), NULL);
@@ -221,8 +219,7 @@
 	}
 
 	tmp = gtk_imhtml_get_text(GTK_IMHTML(win->text), NULL, NULL);
-	strftime(date, sizeof(date), "%c", localtime(&now));
-	fprintf(fp, "Gaim Debug Log : %s\n", date);
+	fprintf(fp, "Gaim Debug Log : %s\n", gaim_date_format_full(time(NULL)));
 	fprintf(fp, "%s", tmp);
 	g_free(tmp);
 
@@ -958,10 +955,10 @@
 	 * not NULL.  Why the hell do we do that?  --Mark
 	 */
 	if ((category != NULL) && (timestamps)) {
-		gchar mdate[64];
+		const char *mdate;
 
 		time_t mtime = time(NULL);
-		strftime(mdate, sizeof(mdate), "%H:%M:%S", localtime(&mtime));
+		mdate = gaim_utf8_strftime("%H:%M:%S", localtime(&mtime));
 		ts_s = g_strdup_printf("(%s) ", mdate);
 	} else {
 		ts_s = g_strdup("");
--- a/src/gtklog.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/gtklog.c	Thu Feb 02 19:50:51 2006 +0000
@@ -114,17 +114,10 @@
 		if (read && *read && gaim_strcasestr(read, search_term)) {
 			GtkTreeIter iter;
 			GaimLog *log = logs->data;
-			char title[64];
-			char *title_utf8; /* temporary variable for utf8 conversion */
-
-			gaim_strftime(title, sizeof(title), "%c", localtime(&log->time));
-			title_utf8 = gaim_utf8_try_convert(title);
-			strncpy(title, title_utf8, sizeof(title));
-			g_free(title_utf8);
 
 			gtk_tree_store_append (lv->treestore, &iter, NULL);
 			gtk_tree_store_set(lv->treestore, &iter,
-					   0, title,
+					   0, gaim_date_format_full(log->time),
 					   1, log, -1);
 		}
 		g_free(read);
@@ -181,7 +174,6 @@
 	GdkCursor *cursor;
 	GaimLogReadFlags flags;
 	char *read = NULL;
-	char time[64];
 
 	if (!gtk_tree_selection_get_selected(sel, &model, &iter))
 		return;
@@ -206,20 +198,12 @@
 
 	if (log->type != GAIM_LOG_SYSTEM) {
 		char *title;
-		char *title_utf8; /* temporary variable for utf8 conversion */
-
-		gaim_strftime(time, sizeof(time), "%c", localtime(&log->time));
-
 		if (log->type == GAIM_LOG_CHAT)
-			title = g_strdup_printf(_("Conversation in %s on %s"), log->name, time);
+			title = g_strdup_printf(_("<span size='larger' weight='bold'>Conversation in %s on %s</span>"),
+									log->name, gaim_date_format_full(log->time));
 		else
-			title = g_strdup_printf(_("Conversation with %s on %s"), log->name, time);
-
-		title_utf8 = gaim_utf8_try_convert(title);
-		g_free(title);
-
-		title = g_strdup_printf("<span size='larger' weight='bold'>%s</span>", title_utf8);
-		g_free(title_utf8);
+			title = g_strdup_printf(_("<span size='larger' weight='bold'>Conversation with %s on %s</span>"),
+									log->name, gaim_date_format_full(log->time));
 
 		gtk_label_set_markup(GTK_LABEL(viewer->label), title);
 		g_free(title);
@@ -259,28 +243,18 @@
      /* Logs are made from trees in real life.
         This is a tree made from logs */
 {
-	char month[30];
-	char title[64];
+	const char *month;
 	char prev_top_month[30] = "";
-	char *utf8_tmp; /* temporary variable for utf8 conversion */
 	GtkTreeIter toplevel, child;
 	GList *logs = lv->logs;
 
 	while (logs != NULL) {
 		GaimLog *log = logs->data;
 
-		gaim_strftime(month, sizeof(month), "%B %Y", localtime(&log->time));
-		gaim_strftime(title, sizeof(title), "%c", localtime(&log->time));
+		month = gaim_utf8_strftime(_("%B %Y"), localtime(&log->time));
 
-		/* do utf8 conversions */
-		utf8_tmp = gaim_utf8_try_convert(month);
-		strncpy(month, utf8_tmp, sizeof(month));
-		g_free(utf8_tmp);
-		utf8_tmp = gaim_utf8_try_convert(title);
-		strncpy(title, utf8_tmp, sizeof(title));
-		g_free(utf8_tmp);
-
-		if (strncmp(month, prev_top_month, sizeof(month)) != 0) {
+		if (strcmp(month, prev_top_month) != 0)
+		{
 			/* top level */
 			gtk_tree_store_append(lv->treestore, &toplevel, NULL);
 			gtk_tree_store_set(lv->treestore, &toplevel, 0, month, 1, NULL, -1);
@@ -290,7 +264,10 @@
 
 		/* sub */
 		gtk_tree_store_append(lv->treestore, &child, &toplevel);
-		gtk_tree_store_set(lv->treestore, &child, 0, title, 1, log, -1);
+		gtk_tree_store_set(lv->treestore, &child,
+						   0, gaim_date_format_full(log->time),
+						   1, log,
+						   -1);
 
 		logs = logs->next;
 	}
--- a/src/gtkpounce.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/gtkpounce.c	Thu Feb 02 19:50:51 2006 +0000
@@ -1431,8 +1431,6 @@
 
 	if (gaim_pounce_action_is_enabled(pounce, "popup-notify"))
 	{
-		time_t now = time(NULL);
-		char date[64];
 		char *tmp;
 		const char *name_shown;
 		const char *reason;
@@ -1473,12 +1471,11 @@
 		if ((name_shown = gaim_account_get_alias(account)) == NULL)
 			name_shown = gaim_account_get_username(account);
 
-		strftime(date, sizeof(date), "%c", localtime(&now));
 		if (reason == NULL)
-			gaim_notify_info(NULL, name_shown, tmp, date);
+			gaim_notify_info(NULL, name_shown, tmp, gaim_date_format_full(time(NULL)));
 		else
 		{
-			char *tmp2 = g_strdup_printf("%s\n\n%s", reason, date);
+			char *tmp2 = g_strdup_printf("%s\n\n%s", reason, gaim_date_format_full(time(NULL)));
 			gaim_notify_info(NULL, name_shown, tmp, tmp2);
 			g_free(tmp2);
 		}
--- a/src/internal.h	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/internal.h	Thu Feb 02 19:50:51 2006 +0000
@@ -162,6 +162,12 @@
 #	endif
 #endif
 
+/* Safer ways to work with static buffers. When using non-static
+ * buffers, either use g_strdup_* functions (preferred) or use
+ * g_strlcpy/g_strlcpy directly. */
+#define gaim_strlcpy(dest, src) g_strlcpy(dest, src, sizeof(dest))
+#define gaim_strlcat(dest, src) g_strlcat(dest, src, sizeof(dest))
+
 #define GAIM_WEBSITE "http://gaim.sourceforge.net/"
 
 #ifndef _WIN32
--- a/src/log.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/log.c	Thu Feb 02 19:50:51 2006 +0000
@@ -585,30 +585,26 @@
 	date = gaim_signal_emit_return_1(gaim_log_get_handle(),
 	                          "log-timestamp",
 	                          log, &tm);
-	if (date == NULL)
-	{
-		char buf[64];
+	if (date != NULL)
+		return date;
 
-		if (log->type == GAIM_LOG_SYSTEM || time(NULL) > when + 20*60)
-			strftime(buf, sizeof(buf), "%x %X", &tm);
-		else
-			strftime(buf, sizeof(buf), "%X", &tm);
-
-		date = g_strdup(buf);
-	}
-
-	return date;
+	if (log->type == GAIM_LOG_SYSTEM || time(NULL) > when + 20*60)
+		return g_strdup(gaim_date_format_long(&tm));
+	else
+		return g_strdup(gaim_time_format(&tm));
 }
 
 void gaim_log_common_writer(GaimLog *log, const char *ext)
 {
-	char date[64];
 	GaimLogCommonLoggerData *data = log->logger_data;
 
 	if (data == NULL)
 	{
 		/* This log is new */
-		char *dir, *filename, *path;
+		char *dir;
+		const char *date;
+		char *filename;
+		char *path;
 
 		dir = gaim_log_get_log_dir(log->type, log->name, log->account);
 		if (dir == NULL)
@@ -616,7 +612,7 @@
 
 		gaim_build_dir (dir, S_IRUSR | S_IWUSR | S_IXUSR);
 
-		strftime(date, sizeof(date), "%Y-%m-%d.%H%M%S", localtime(&log->time));
+		date = gaim_utf8_strftime("%Y-%m-%d.%H%M%S", localtime(&log->time));
 
 		filename = g_strdup_printf("%s%s", date, ext ? ext : "");
 
@@ -825,7 +821,6 @@
 			     GaimMessageFlags type,
 			     const char *from, time_t time, const char *message)
 {
-	char *date;
 	char *xhtml = NULL;
 
 	if (!log->logger_data) {
@@ -833,18 +828,18 @@
 		 * creating a new file there would result in empty files in the case
 		 * that you open a convo with someone, but don't say anything.
 		 */
-		char buf[64];
+		const char *date;
 		char *dir = gaim_log_get_log_dir(log->type, log->name, log->account);
 		FILE *file;
 
 		if (dir == NULL)
 			return;
 
-		strftime(buf, sizeof(buf), "%Y-%m-%d.%H%M%S.xml", localtime(&log->time));
+		date = gaim_utf8_strftime("%Y-%m-%d.%H%M%S.xml", localtime(&log->time));
 
 		gaim_build_dir (dir, S_IRUSR | S_IWUSR | S_IXUSR);
 
-		char *filename = g_build_filename(dir, buf, NULL);
+		char *filename = g_build_filename(dir, date, NULL);
 		g_free(dir);
 
 		log->logger_data = g_fopen(filename, "a");
@@ -857,7 +852,7 @@
 		fprintf(log->logger_data, "<?xml version='1.0' encoding='UTF-8' ?>\n"
 			"<?xml-stylesheet href='file:///usr/src/web/htdocs/log-stylesheet.xsl' type='text/xml' ?>\n");
 
-		strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&log->time));
+		date = gaim_utf8_strftime("%Y-%m-%d %H:%M:%S", localtime(&log->time));
 		fprintf(log->logger_data, "<conversation time='%s' screenname='%s' protocol='%s'>\n",
 			date, log->name, prpl);
 	}
@@ -926,9 +921,9 @@
 	gsize written = 0;
 
 	if(!data) {
-		char buf[64];
 		const char *prpl =
 			GAIM_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
+		const char *date;
 		gaim_log_common_writer(log, ".html");
 
 		data = log->logger_data;
@@ -937,16 +932,17 @@
 		if(!data->file)
 			return 0;
 
-		strftime(buf, sizeof(buf), "%c", localtime(&log->time));
+		date = gaim_date_format_full(log->time);
+
 		written += fprintf(data->file, "<html><head>");
 		written += fprintf(data->file, "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">");
 		written += fprintf(data->file, "<title>");
 		written += fprintf(data->file, "Conversation with %s at %s on %s (%s)",
-			log->name, buf, gaim_account_get_username(log->account), prpl);
+			log->name, date, gaim_account_get_username(log->account), prpl);
 		written += fprintf(data->file, "</title></head><body>");
 		written += fprintf(data->file,
 			"<h3>Conversation with %s at %s on %s (%s)</h3>\n",
-			log->name, buf, gaim_account_get_username(log->account), prpl);
+			log->name, date, gaim_account_get_username(log->account), prpl);
 	}
 
 	/* if we can't write to the file, give up before we hurt ourselves */
@@ -1064,7 +1060,6 @@
 		 * creating a new file there would result in empty files in the case
 		 * that you open a convo with someone, but don't say anything.
 		 */
-		char buf[64];
 		const char *prpl =
 			GAIM_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL);
 		gaim_log_common_writer(log, ".txt");
@@ -1075,9 +1070,9 @@
 		if(!data->file)
 			return 0;
 
-		strftime(buf, sizeof(buf), "%c", localtime(&log->time));
 		written += fprintf(data->file, "Conversation with %s at %s on %s (%s)\n",
-			log->name, buf, gaim_account_get_username(log->account), prpl);
+			log->name, gaim_date_format_full(log->time),
+			gaim_account_get_username(log->account), prpl);
 	}
 
 	/* if we can't write to the file, give up before we hurt ourselves */
--- a/src/protocols/irc/msgs.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/protocols/irc/msgs.c	Thu Feb 02 19:50:51 2006 +0000
@@ -241,7 +241,7 @@
 		gchar *timex = gaim_str_seconds_to_string(irc->whois.idle);
 		g_string_append_printf(info, _("<b>Idle for:</b> %s<br>"), timex);
 		g_free(timex);
-		g_string_append_printf(info, _("<b>%s:</b> %s"), _("Online since"), ctime(&irc->whois.signon));
+		g_string_append_printf(info, _("<b>%s:</b> %s"), _("Online since"), gaim_date_format_full(irc->whois.signon));
 	}
 	if (!strcmp(irc->whois.nick, "Paco-Paco")) {
 		g_string_append_printf(info, _("<br><b>Defining adjective:</b> Glorious<br>"));
--- a/src/protocols/jabber/iq.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/protocols/jabber/iq.c	Thu Feb 02 19:50:51 2006 +0000
@@ -167,7 +167,6 @@
 {
 	const char *type, *from, *id;
 	JabberIq *iq;
-	char buf[1024];
 	xmlnode *query;
 	time_t now_t;
 	struct tm *now;
@@ -180,7 +179,7 @@
 	id = xmlnode_get_attrib(packet, "id");
 
 	if(type && !strcmp(type, "get")) {
-		char *utf8;
+		const char *date;
 
 		iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:time");
 		jabber_iq_set_id(iq, id);
@@ -188,20 +187,14 @@
 
 		query = xmlnode_get_child(iq->node, "query");
 
-		strftime(buf, sizeof(buf), "%Y%m%dT%T", now);
-		xmlnode_insert_data(xmlnode_new_child(query, "utc"), buf, -1);
+		date = gaim_utf8_strftime("%Y%m%dT%T", now);
+		xmlnode_insert_data(xmlnode_new_child(query, "utc"), date, -1);
 
-		strftime(buf, sizeof(buf), "%Z", now);
-		if((utf8 = gaim_utf8_try_convert(buf))) {
-			xmlnode_insert_data(xmlnode_new_child(query, "tz"), utf8, -1);
-			g_free(utf8);
-		}
+		date = gaim_utf8_strftime("%Z", now);
+		xmlnode_insert_data(xmlnode_new_child(query, "tz"), date, -1);
 
-		strftime(buf, sizeof(buf), "%d %b %Y %T", now);
-		if((utf8 = gaim_utf8_try_convert(buf))) {
-			xmlnode_insert_data(xmlnode_new_child(query, "display"), utf8, -1);
-			g_free(utf8);
-		}
+		date = gaim_utf8_strftime("%d %b %Y %T", now);
+		xmlnode_insert_data(xmlnode_new_child(query, "display"), date, -1);
 
 		jabber_iq_send(iq);
 	}
--- a/src/protocols/msn/msn.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/protocols/msn/msn.c	Thu Feb 02 19:50:51 2006 +0000
@@ -1422,6 +1422,15 @@
 
 #endif
 
+static char *msn_info_date_reformat(const char *field, size_t len)
+{
+	char *tmp = g_strndup(field, len);
+	time_t t = gaim_str_to_time(tmp, FALSE);
+
+	g_free(tmp);
+	return g_strdup(gaim_date_format_short(localtime(&t)));
+}
+
 static void
 msn_got_info(void *data, const char *url_text, size_t len)
 {
@@ -1512,14 +1521,14 @@
 
 	/* Extract their Name and put it in */
 	found = gaim_markup_extract_info_field(stripped, stripped_len, s,
-			"\nName\n", 0, "\t", 0, "Undisclosed", _("Name"), 0, NULL);
+			"\nName\n", 0, "\t", 0, "Undisclosed", _("Name"), 0, NULL, NULL);
 
 	if (found)
 		has_info = TRUE;
 
 	/* Extract their Age and put it in */
 	found = gaim_markup_extract_info_field(stripped, stripped_len, s,
-			"\tAge\n", 0, "\n", 0, "Undisclosed", _("Age"), 0, NULL);
+			"\tAge\n", 0, "\n", 0, "Undisclosed", _("Age"), 0, NULL, NULL);
 
 	if (found)
 		has_info = TRUE;
@@ -1527,7 +1536,7 @@
 	/* Extract their Gender and put it in */
 	found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 			"\nGender\n", 0, "\t", 0, "Undisclosed", _("Gender"), 0,
-			NULL);
+			NULL, NULL);
 
 	if (found)
 		has_info = TRUE;
@@ -1535,7 +1544,7 @@
 	/* Extract their MaritalStatus and put it in */
 	found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 			"\tMarital Status\n", 0, "\n", 0, "Undisclosed",
-			_("Marital Status"), 0, NULL);
+			_("Marital Status"), 0, NULL, NULL);
 
 	if (found)
 		has_info = TRUE;
@@ -1543,7 +1552,7 @@
 	/* Extract their Location and put it in */
 	found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 			"\nLocation\n", 0, "\n", 0, "Undisclosed", _("Location"), 0,
-			NULL);
+			NULL, NULL);
 
 	if (found)
 		has_info = TRUE;
@@ -1551,7 +1560,7 @@
 	/* Extract their Occupation and put it in */
 	found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 			" Occupation\n", 6, "\n", 0, "Undisclosed", _("Occupation"),
-			0, NULL);
+			0, NULL, NULL);
 
 	if (found)
 		has_info = TRUE;
@@ -1567,20 +1576,20 @@
 	/* Check if they have A Little About Me */
 	found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 			" A Little About Me \n\n", 0, "Favorite Things", '\n', NULL,
-			_("A Little About Me"), 0, NULL);
+			_("A Little About Me"), 0, NULL, NULL);
 
 	if (!found)
 	{
 		found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 				" A Little About Me \n\n", 0, "Hobbies and Interests", '\n',
-				NULL, _("A Little About Me"), 0, NULL);
+				NULL, _("A Little About Me"), 0, NULL, NULL);
 	}
 
 	if (!found)
 	{
 		found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 				" A Little About Me \n\n", 0, "Favorite Quote", '\n', NULL,
-				_("A Little About Me"), 0, NULL);
+				_("A Little About Me"), 0, NULL, NULL);
 	}
 
 	if (!found)
@@ -1588,14 +1597,14 @@
 		found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 				" A Little About Me \n\n", 0, "My Homepage \n\nTake a look",
 				'\n',
-				NULL, _("A Little About Me"), 0, NULL);
+				NULL, _("A Little About Me"), 0, NULL, NULL);
 	}
 
 	if (!found)
 	{
 		gaim_markup_extract_info_field(stripped, stripped_len, s,
 				" A Little About Me \n\n", 0, "last updated", '\n', NULL,
-				_("A Little About Me"), 0, NULL);
+				_("A Little About Me"), 0, NULL, NULL);
 	}
 
 	if (found)
@@ -1604,27 +1613,27 @@
 	/* Check if they have Favorite Things */
 	found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 			" Favorite Things \n\n", 0, "Hobbies and Interests", '\n', NULL,
-			_("Favorite Things"), 0, NULL);
+			_("Favorite Things"), 0, NULL, NULL);
 
 	if (!found)
 	{
 		found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 				" Favorite Things \n\n", 0, "Favorite Quote", '\n', NULL,
-				_("Favorite Things"), 0, NULL);
+				_("Favorite Things"), 0, NULL, NULL);
 	}
 
 	if (!found)
 	{
 		found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 				" Favorite Things \n\n", 0, "My Homepage \n\nTake a look", '\n',
-				NULL, _("Favorite Things"), 0, NULL);
+				NULL, _("Favorite Things"), 0, NULL, NULL);
 	}
 
 	if (!found)
 	{
 		gaim_markup_extract_info_field(stripped, stripped_len, s,
 				" Favorite Things \n\n", 0, "last updated", '\n', NULL,
-				_("Favorite Things"), 0, NULL);
+				_("Favorite Things"), 0, NULL, NULL);
 	}
 
 	if (found)
@@ -1633,20 +1642,20 @@
 	/* Check if they have Hobbies and Interests */
 	found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 			" Hobbies and Interests \n\n", 0, "Favorite Quote", '\n', NULL,
-			_("Hobbies and Interests"), 0, NULL);
+			_("Hobbies and Interests"), 0, NULL, NULL);
 
 	if (!found)
 	{
 		found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 				" Hobbies and Interests \n\n", 0, "My Homepage \n\nTake a look",
-				'\n', NULL, _("Hobbies and Interests"), 0, NULL);
+				'\n', NULL, _("Hobbies and Interests"), 0, NULL, NULL);
 	}
 
 	if (!found)
 	{
 		gaim_markup_extract_info_field(stripped, stripped_len, s,
 				" Hobbies and Interests \n\n", 0, "last updated", '\n', NULL,
-				_("Hobbies and Interests"), 0, NULL);
+				_("Hobbies and Interests"), 0, NULL, NULL);
 	}
 
 	if (found)
@@ -1655,13 +1664,13 @@
 	/* Check if they have Favorite Quote */
 	found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 			"Favorite Quote \n\n", 0, "My Homepage \n\nTake a look", '\n', NULL,
-			_("Favorite Quote"), 0, NULL);
+			_("Favorite Quote"), 0, NULL, NULL);
 
 	if (!found)
 	{
 		gaim_markup_extract_info_field(stripped, stripped_len, s,
 				"Favorite Quote \n\n", 0, "last updated", '\n', NULL,
-				_("Favorite Quote"), 0, NULL);
+				_("Favorite Quote"), 0, NULL, NULL);
 	}
 
 	if (found)
@@ -1670,7 +1679,7 @@
 	/* Extract the last updated date and put it in */
 	found = gaim_markup_extract_info_field(stripped, stripped_len, s,
 			" last updated:", 1, "\n", 0, NULL, _("Last Updated"), 0,
-			NULL);
+			NULL, msn_info_date_reformat);
 
 	if (found)
 		has_info = TRUE;
--- a/src/protocols/novell/novell.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/protocols/novell/novell.c	Thu Feb 02 19:50:51 2006 +0000
@@ -1903,7 +1903,7 @@
 	gmt = nm_event_get_gmt(event);
 	title = _("Invitation to Conversation");
 	primary = g_strdup_printf(_("Invitation from: %s\n\nSent: %s"),
-							  name, asctime(localtime(&gmt)));
+							  name, gaim_date_format_full(gmt));
 	secondary = _("Would you like to join the conversation?");
 
 	/* Set up parms list for the callbacks
--- a/src/protocols/oscar/icq.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/protocols/oscar/icq.c	Thu Feb 02 19:50:51 2006 +0000
@@ -354,7 +354,8 @@
 	aim_frame_t *fr;
 	aim_snacid_t snacid;
 	int bslen, xmllen;
-	char *xml, timestr[30];
+	char *xml;
+	const char *timestr;
 	time_t t;
 	struct tm *tm;
 
@@ -366,7 +367,7 @@
 
 	time(&t);
 	tm = gmtime(&t);
-	strftime(timestr, 30, "%a, %d %b %Y %T %Z", tm);
+	timestr = gaim_utf8_strftime("%a, %d %b %Y %T %Z", tm);
 
 	/* The length of xml included the null terminating character */
 	xmllen = 225 + strlen(name) + strlen(msg) + strlen(sess->sn) + strlen(alias) + strlen(timestr) + 1;
--- a/src/protocols/oscar/oscar.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/protocols/oscar/oscar.c	Thu Feb 02 19:50:51 2006 +0000
@@ -619,7 +619,7 @@
 static gchar *oscar_caps_to_string(guint caps)
 {
 	GString *str;
-	gchar *tmp;
+	const gchar *tmp;
 	guint bit = 1;
 
 	str = g_string_new("");
@@ -5166,12 +5166,12 @@
 
 	if (userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) {
 		time_t t = userinfo->onlinesince - od->timeoffset;
-		oscar_string_append(gc->account, str, "\n<br>", _("Online Since"), ctime(&t));
+		oscar_string_append(gc->account, str, "\n<br>", _("Online Since"), gaim_date_format_full(t));
 	}
 
 	if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) {
 		time_t t = userinfo->membersince - od->timeoffset;
-		oscar_string_append(gc->account, str, "\n<br>", _("Member Since"), ctime(&t));
+		oscar_string_append(gc->account, str, "\n<br>", _("Member Since"), gaim_date_format_full(t));
 	}
 
 	if (userinfo->capabilities != 0) {
@@ -6055,53 +6055,6 @@
 	return 1;
 }
 
-#if 0
-/*
- * Update, 2003-11-09:
- * Joseph S. Myers, a gcc dude, fixed this for gcc 3.4!  Rock on!
- *
- * It may not be my place to do this, but...
- * I feel pretty strongly that the "last 2 digits" warning is ridiculously 
- * stupid, and should not exist for % switches (%x in our case) that request 
- * a year in the preferred representation for the current locale.  For that 
- * reason I've chosen to not use this workaround (n., see kluge).
- *
- * I have a date.  I want to show it to the user in the "preferred" way.  
- * Whether that displays a 2 digit year is perfectly fine--after all, it's 
- * what the locale wanted.
- * 
- * If I have a necessity for a full representation of the year in the current 
- * locale, then I'll use a switch that returns a full representation of the 
- * year.
- *
- * If you think the preferred locale should show 4 digits instead of 2 digits 
- * (because you're anal, or whatever), then change the f***ing locale.
- *
- * I guess the bottom line is--I'm trying to show a date to the user how they 
- * prefer to see it, why the hell does gcc want me to change that?
- *
- * See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=3190
- * See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=8714
- */
-
-/*
- * This function was recommended by the STRFTIME(3) man page to remove the
- * "last 2 digits" warning.
- */
-static size_t my_strftime(char *s, size_t max, const char  *fmt,
-			const struct tm *tm)
-{
-	return strftime(s, max, fmt, tm);
-}
-
-/*
- * Before even realizing this was here, I went and did the same thing in util.c.
- *
- * Use gaim_strftime()
- */
-
-#endif
-
 static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
 {
 	GaimConnection *gc = sess->aux_data;
@@ -6158,13 +6111,12 @@
 	if (info->gender != 0)
 		oscar_string_append(gc->account, str, "\n<br>", _("Gender"), info->gender == 1 ? _("Female") : _("Male"));
 	if ((info->birthyear > 1900) && (info->birthmonth > 0) && (info->birthday > 0)) {
-		char date[30];
 		struct tm tm;
 		tm.tm_mday = (int)info->birthday;
 		tm.tm_mon = (int)info->birthmonth-1;
 		tm.tm_year = (int)info->birthyear-1900;
-		gaim_strftime(date, sizeof(date), "%x", &tm);
-		oscar_string_append(gc->account, str, "\n<br>", _("Birthday"), date);
+		oscar_string_append(gc->account, str, "\n<br>", _("Birthday"),
+				    gaim_date_format_short(&tm));
 	}
 	if ((info->age > 0) && (info->age < 255)) {
 		char age[5];
--- a/src/protocols/yahoo/yahoo_profile.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/protocols/yahoo/yahoo_profile.c	Thu Feb 02 19:50:51 2006 +0000
@@ -647,6 +647,15 @@
 	},
 };
 
+static char *yahoo_info_date_reformat(const char *field, size_t len)
+{
+	char *tmp = g_strndup(field, len);
+	time_t t = gaim_str_to_time(tmp, FALSE);
+
+	g_free(tmp);
+	return g_strdup(gaim_date_format_short(localtime(&t)));
+}
+
 static char *yahoo_remove_nonbreaking_spaces(char *str)
 {
 	char *p;
@@ -994,7 +1003,7 @@
 	 * true, since the Yahoo! ID will always be there */
 	if (!gaim_markup_extract_info_field(stripped, stripped_len, s,
 			strings->yahoo_id_string, 10, "\n", 0,
-			NULL, _("Yahoo! ID"), 0, NULL))
+			NULL, _("Yahoo! ID"), 0, NULL, NULL))
 		;
 #endif
 
@@ -1018,42 +1027,42 @@
 	/* extract their Email address and put it in */
 	found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 			strings->my_email_string, 1, " ", 0,
-			strings->private_string, _("Email"), 0, NULL);
+			strings->private_string, _("Email"), 0, NULL, NULL);
 
 	/* extract the Nickname if it exists */
 	found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 			"Nickname:", 1, "\n", '\n',
-			NULL, _("Nickname"), 0, NULL);
+			NULL, _("Nickname"), 0, NULL, NULL);
 
 	/* extract their RealName and put it in */
 	found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 			strings->realname_string, 1, "\n", '\n',
-			NULL, _("Realname"), 0, NULL);
+			NULL, _("Realname"), 0, NULL, NULL);
 
 	/* extract their Location and put it in */
 	found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 			strings->location_string, 2, "\n", '\n',
-			NULL, _("Location"), 0, NULL);
+			NULL, _("Location"), 0, NULL, NULL);
 
 	/* extract their Age and put it in */
 	found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 			strings->age_string, 3, "\n", '\n',
-			NULL, _("Age"), 0, NULL);
+			NULL, _("Age"), 0, NULL, NULL);
 
 	/* extract their MaritalStatus and put it in */
 	found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 			strings->maritalstatus_string, 3, "\n", '\n',
-			strings->no_answer_string, _("Marital Status"), 0, NULL);
+			strings->no_answer_string, _("Marital Status"), 0, NULL, NULL);
 
 	/* extract their Gender and put it in */
 	found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 			strings->gender_string, 3, "\n", '\n',
-			strings->no_answer_string, _("Gender"), 0, NULL);
+			strings->no_answer_string, _("Gender"), 0, NULL, NULL);
 
 	/* extract their Occupation and put it in */
 	found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 			strings->occupation_string, 2, "\n", '\n',
-			NULL, _("Occupation"), 0, NULL);
+			NULL, _("Occupation"), 0, NULL, NULL);
 
 	/* Hobbies, Latest News, and Favorite Quote are a bit different, since
 	 * the values can contain embedded newlines... but any or all of them
@@ -1066,15 +1075,15 @@
 
 	if (!gaim_markup_extract_info_field(stripped, stripped_len, s,
 			strings->hobbies_string, 1, strings->latest_news_string,
-			'\n', "\n", _("Hobbies"), 0, NULL))
+			'\n', "\n", _("Hobbies"), 0, NULL, NULL))
 	{
 		if (!gaim_markup_extract_info_field(stripped, stripped_len, s,
 				strings->hobbies_string, 1, strings->favorite_quote_string,
-				'\n', "\n", _("Hobbies"), 0, NULL))
+				'\n', "\n", _("Hobbies"), 0, NULL, NULL))
 		{
 			found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 					strings->hobbies_string, 1, strings->links_string,
-					'\n', "\n", _("Hobbies"), 0, NULL);
+					'\n', "\n", _("Hobbies"), 0, NULL, NULL);
 		}
 		else
 			found = TRUE;
@@ -1084,18 +1093,18 @@
 
 	if (!gaim_markup_extract_info_field(stripped, stripped_len, s,
 			strings->latest_news_string, 1, strings->favorite_quote_string,
-			'\n', "\n", _("Latest News"), 0, NULL))
+			'\n', "\n", _("Latest News"), 0, NULL, NULL))
 	{
 		found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 				strings->latest_news_string, 1, strings->links_string,
-				'\n', "\n", _("Latest News"), 0, NULL);
+				'\n', "\n", _("Latest News"), 0, NULL, NULL);
 	}
 	else
 		found = TRUE;
 
 	found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 			strings->favorite_quote_string, 1, strings->links_string,
-			'\n', "\n", _("Favorite Quote"), 0, NULL);
+			'\n', "\n", _("Favorite Quote"), 0, NULL, NULL);
 
 	/* Home Page will either be "No home page specified",
 	 * or "Home Page: " and a link.
@@ -1109,7 +1118,7 @@
 		{
 			found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 					strings->home_page_string, 1, "\n", 0, NULL,
-					_("Home Page"), 1, NULL);
+					_("Home Page"), 1, NULL, NULL);
 		}
 	}
 
@@ -1124,16 +1133,16 @@
 	{
 		if (gaim_markup_extract_info_field(stripped, stripped_len, s,
 				strings->cool_link_1_string, 1, "\n", 0, NULL,
-				_("Cool Link 1"), 1, NULL))
+				_("Cool Link 1"), 1, NULL, NULL))
 		{
 			found = TRUE;
 			if (gaim_markup_extract_info_field(stripped, stripped_len, s,
 					strings->cool_link_2_string, 1, "\n", 0, NULL,
-					_("Cool Link 2"), 1, NULL))
+					_("Cool Link 2"), 1, NULL, NULL))
 			{
 				gaim_markup_extract_info_field(stripped, stripped_len, s,
 						strings->cool_link_3_string, 1, "\n", 0, NULL,
-						_("Cool Link 3"), 1, NULL);
+						_("Cool Link 3"), 1, NULL, NULL);
 			}
 		}
 	}
@@ -1141,12 +1150,12 @@
 	/* see if Member Since is there, and if so, extract it. */
 	found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 			"Member Since:", 1, last_updated_utf8_string,
-			'\n', NULL, _("Member Since"), 0, NULL);
+			'\n', NULL, _("Member Since"), 0, NULL, yahoo_info_date_reformat);
 
 	/* extract the Last Updated date and put it in */
 	found |= gaim_markup_extract_info_field(stripped, stripped_len, s,
 			last_updated_utf8_string, 1, " ", '\n', NULL,
-			_("Last Update"), 0, NULL);
+			_("Last Update"), 0, NULL, yahoo_info_date_reformat);
 	} /* if (profile_state == PROFILE_STATE_DEFAULT) */
 
 	if(!found)
--- a/src/util.c	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/util.c	Thu Feb 02 19:50:51 2006 +0000
@@ -484,6 +484,46 @@
 /**************************************************************************
  * Date/Time Functions
  **************************************************************************/
+const char *
+gaim_utf8_strftime(const char *format, const struct tm *tm)
+{
+	static char buf[128];
+	char *utf8;
+
+	strftime(buf, sizeof(buf), format, tm);
+
+	if ((utf8 = gaim_utf8_try_convert(buf)))
+	{
+		gaim_strlcpy(buf, utf8);
+		g_free(utf8);
+	}
+
+	return buf;
+}
+
+const char *
+gaim_date_format_short(const struct tm *tm)
+{
+	return gaim_utf8_strftime("%x", tm);
+}
+
+const char *
+gaim_date_format_long(const struct tm *tm)
+{
+	return gaim_utf8_strftime(_("%x %X"), tm);
+}
+
+const char *
+gaim_date_format_full(time_t time)
+{
+	return gaim_utf8_strftime("%c", localtime(&time));
+}
+
+const char *
+gaim_time_format(const struct tm *tm)
+{
+	return gaim_utf8_strftime("%X", tm);
+}
 
 time_t
 gaim_time_build(int year, int month, int day, int hour, int min, int sec)
@@ -507,6 +547,7 @@
 	struct tm *t;
 	char buf[32];
 	char *c;
+	int year = 0;
 	int tzoff = 0;
 
 	time(&retval);
@@ -516,25 +557,35 @@
 	c = buf;
 
 	/* 4 digit year */
-	if (!sscanf(c, "%04d", &t->tm_year)) return 0;
-	c += 4;
-	if (*c == '-')
-		c++;
-
-	t->tm_year -= 1900;
+	if (sscanf(c, "%04d", &year) && year > 1900)
+	{
+		c += 4;
+		if (*c == '-')
+			c++;
+		t->tm_year -= 1900;
+	}
 
 	/* 2 digit month */
-	if (!sscanf(c, "%02d", &t->tm_mon)) return 0;
+	if (!sscanf(c, "%02d", &t->tm_mon))
+		return 0;
 	c += 2;
-	if (*c == '-')
+	if (*c == '-' || *c == '/')
 		c++;
-
 	t->tm_mon -= 1;
 
 	/* 2 digit day */
-	if (!sscanf(c, "%02d", &t->tm_mday)) return 0;
+	if (!sscanf(c, "%02d", &t->tm_mday))
+		return 0;
 	c += 2;
-	if (*c == 'T' || *c == '.') { /* we have more than a date, keep going */
+	if (*c == '/')
+	{
+		c++;
+
+		if (!sscanf(c, "%04d", &t->tm_year))
+			return 0;
+		t->tm_year -= 1900;
+	}
+	else if (*c == 'T' || *c == '.') { /* we have more than a date, keep going */
 		c++; /* skip the "T" */
 
 		/* 2 digit hour */
@@ -585,10 +636,6 @@
 	return retval;
 }
 
-size_t gaim_strftime(char *s, size_t max, const char *format, const struct tm *tm)
-{
-	return strftime(s, max, format, tm);
-}
 
 /**************************************************************************
  * Markup Functions
@@ -753,7 +800,8 @@
 							   const char *end_token, char check_value,
 							   const char *no_value_token,
 							   const char *display_name, gboolean is_link,
-							   const char *link_prefix)
+							   const char *link_prefix,
+							   GaimInfoFieldFormatCallback format_cb)
 {
 	const char *p, *q;
 
@@ -805,7 +853,14 @@
 			if (link_prefix)
 				g_string_append(dest, link_prefix);
 
-			g_string_append_len(dest, p, q - p);
+			if (format_cb != NULL)
+			{
+				char *reformatted = format_cb(p, q - p);
+				g_string_append(dest, reformatted);
+				g_free(reformatted);
+			}
+			else
+				g_string_append_len(dest, p, q - p);
 			g_string_append(dest, "\">");
 
 			if (link_prefix)
@@ -816,7 +871,14 @@
 		}
 		else
 		{
-			g_string_append_len(dest, p, q - p);
+			if (format_cb != NULL)
+			{
+				char *reformatted = format_cb(p, q - p);
+				g_string_append(dest, reformatted);
+				g_free(reformatted);
+			}
+			else
+				g_string_append_len(dest, p, q - p);
 		}
 
 		g_string_append(dest, "<br>\n");
--- a/src/util.h	Thu Feb 02 19:39:12 2006 +0000
+++ b/src/util.h	Thu Feb 02 19:50:51 2006 +0000
@@ -45,6 +45,8 @@
 	GList *children;
 } GaimMenuAction;
 
+typedef char *(*GaimInfoFieldFormatCallback)(const char *field, size_t len);
+
 /**
  * A key-value pair.
  *
@@ -209,6 +211,62 @@
 /*@{*/
 
 /**
+ * Formats a time into the specified format.
+ *
+ * This is essentially strftime(), but it has a static buffer
+ * and handles the UTF-8 conversion for the caller.
+ */
+const char *gaim_utf8_strftime(const char *format, const struct tm *tm);
+
+/**
+ * Formats a time into the user's preferred short date format.
+ *
+ * The returned string is stored in a static buffer, so the result
+ * should be g_strdup()'d if it's going to be kept.
+ *
+ * @param time The time value to format (in local time).
+ *
+ * @return The date, formatted as per the user's settings.
+ */
+const char *gaim_date_format_short(const struct tm *tm);
+
+/**
+ * Formats a time into the user's preferred short date plus time format.
+ *
+ * The returned string is stored in a static buffer, so the result
+ * should be g_strdup()'d if it's going to be kept.
+ *
+ * @param time The time value to format (in local time).
+ *
+ * @return The timestamp, formatted as per the user's settings.
+ */
+const char *gaim_date_format_long(const struct tm *tm);
+
+/**
+ * Formats a time into the user's preferred full date and time format.
+ *
+ * The returned string is stored in a static buffer, so the result
+ * should be g_strdup()'d if it's going to be kept.
+ *
+ * @param time The time value to format (in local time).
+ *
+ * @return The date and time, formatted as per the user's settings.
+ */
+const char *gaim_date_format_full(time_t time);
+
+/**
+ * Formats a time into the user's preferred time format.
+ *
+ * The returned string is stored in a static buffer, so the result
+ * should be g_strdup()'d if it's going to be kept.
+ *
+ * @param time The time value to format (in local time).
+ *
+ * @return The time, formatted as per the user's settings.
+ */
+const char *gaim_time_format(const struct tm *tm);
+
+/**
  * Builds a time_t from the supplied information.
  *
  * @param year  The year.
@@ -224,7 +282,8 @@
 					   int min, int sec);
 
 /**
- * Parses a timestamp in jabber or ISO8601 format and returns a time_t.
+ * Parses a timestamp in jabber, ISO8601, or MM/DD/YYYY format and returns
+ * a time_t.
  *
  * @param timestamp The timestamp
  * @param utc Assume UTC if no timezone specified
@@ -233,17 +292,6 @@
  */
 time_t gaim_str_to_time(const char *timestamp, gboolean utc);
 
-/**
- * Creates a string according to a time and format string
- *
- * This function just calls strftime. The only advantage to using it
- * is that gcc won't give a warning if you use %c
- *
- * TODO: The warning is gone in gcc4, and this function can
- *       eventually be removed.
- */
-size_t gaim_strftime(char *s, size_t max, const char *format, const struct tm *tm);
-
 /*@}*/
 
 
@@ -289,6 +337,7 @@
  * @param display_name   The short descriptive name to display for this token.
  * @param is_link        TRUE if this should be a link, or FALSE otherwise.
  * @param link_prefix    The prefix for the link.
+ * @param format_cb      A callback to format the value before adding it.
  *
  * @return TRUE if successful, or FALSE otherwise.
  */
@@ -297,7 +346,8 @@
                                         const char *end_token, char check_value,
                                         const char *no_value_token,
                                         const char *display_name, gboolean is_link,
-                                        const char *link_prefix);
+                                        const char *link_prefix,
+					GaimInfoFieldFormatCallback format_cb);
 
 /**
  * Converts HTML markup to XHTML.