changeset 13119:fcde3faa1f57

[gaim-migrate @ 15481] This adds support for displaying log timestamps in their original timezone. If your OS's definition of struct tm sucks, then the log timestamps will show up in your local timezone, but converted, so the time is accurate. Yay! Anyway, this all works, as I've renamed lots of my log files locally, but currently, there's no code to save new logs in this name format. That's held up on a portability issue and backwards compatibility issue. committer: Tailor Script <tailor@pidgin.im>
author Richard Laager <rlaager@wiktel.com>
date Sat, 04 Feb 2006 20:55:52 +0000
parents 8855973b487b
children f62d14ba98f6
files plugins/history.c plugins/log_reader.c plugins/perl/common/Util.xs plugins/perl/common/typemap src/account.c src/conversation.c src/gtkdebug.c src/gtklog.c src/gtkpounce.c src/log.c src/log.h src/protocols/irc/msgs.c src/protocols/jabber/message.c src/protocols/msn/msn.c src/protocols/novell/nmevent.c src/protocols/novell/nmevent.h src/protocols/novell/novell.c src/protocols/oscar/oscar.c src/protocols/yahoo/yahoo_profile.c src/util.c src/util.h
diffstat 21 files changed, 233 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/history.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/plugins/history.c	Sat Feb 04 20:55:52 2006 +0000
@@ -115,7 +115,7 @@
 							      gaim_account_get_protocol_name(((GaimLog*)logs->data)->account));
 
 	header = g_strdup_printf("<b>Conversation with %s on %s:</b><br>", alias,
-							 gaim_date_format_full(((GaimLog *)logs->data)->time));
+							 gaim_date_format_full(localtime(&((GaimLog *)logs->data)->time)));
 	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), header, options);
 	g_free(header);
 
--- a/plugins/log_reader.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/plugins/log_reader.c	Sat Feb 04 20:55:52 2006 +0000
@@ -156,7 +156,8 @@
 					tm.tm_year -= 1900;
 					tm.tm_mon  -= 1;
 
-					log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, mktime(&tm));
+					/* XXX: Look into this later... Should we pass in a struct tm? */
+					log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, mktime(&tm), NULL);
 					log->logger = adium_logger;
 					log->logger_data = data;
 
@@ -216,7 +217,8 @@
 					data->path = filename;
 					data->type = ADIUM_TEXT;
 
-					log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, mktime(&tm));
+					/* XXX: Look into this later... Should we pass in a struct tm? */
+					log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, mktime(&tm), NULL);
 					log->logger = adium_logger;
 					log->logger_data = data;
 
@@ -730,7 +732,8 @@
 			data->text = NULL;
 			data->last_log = FALSE;
 
-			log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, msn_logger_parse_timestamp(message));
+			/* XXX: Look into this later... Should we pass in a struct tm? */
+			log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, msn_logger_parse_timestamp(message), NULL);
 			log->logger = msn_logger;
 			log->logger_data = data;
 
@@ -1244,8 +1247,9 @@
 						data->their_nickname =
 							g_strdup(their_nickname);
 
+						/* XXX: Look into this later... Should we pass in a struct tm? */
 						log = gaim_log_new(GAIM_LOG_IM,
-							sn, account, NULL, mktime(&tm));
+							sn, account, NULL, mktime(&tm), NULL);
 						log->logger = trillian_logger;
 						log->logger_data = data;
 
--- a/plugins/perl/common/Util.xs	Sat Feb 04 05:17:46 2006 +0000
+++ b/plugins/perl/common/Util.xs	Sat Feb 04 20:55:52 2006 +0000
@@ -52,8 +52,8 @@
 	int mode
 
 const char *
-gaim_date_format_full(time)
-	time_t time
+gaim_date_format_full(tm)
+	const struct tm *tm
 
 const char *
 gaim_date_format_long(tm)
@@ -182,9 +182,12 @@
 	const char *name
 
 time_t
-gaim_str_to_time(timestamp, utc)
+gaim_str_to_time(timestamp, utc = FALSE, tm = NULL, tz_off = NULL, rest = NULL)
 	const char *timestamp
 	gboolean utc
+	struct tm *tm
+	long *tz_off
+	const char **rest
 
 gchar *
 gaim_strcasereplace(string, delimiter, replacement)
--- a/plugins/perl/common/typemap	Sat Feb 04 05:17:46 2006 +0000
+++ b/plugins/perl/common/typemap	Sat Feb 04 20:55:52 2006 +0000
@@ -22,6 +22,7 @@
 const guchar *				T_PV
 char *					T_PV
 int *					T_PTR
+long *					T_PTR
 size_t *				T_PTR
 Gaim::GTK::Widget *			T_PTR
 GCallback				T_PTR
@@ -30,6 +31,7 @@
 GData *					T_PTR
 GData **				T_PTR
 const unsigned char *			T_PTR
+struct tm *				T_PTR
 const struct tm *			T_PTR
 xmlnode *				T_PTR
 const xmlnode *				T_PTR
--- a/src/account.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/account.c	Sat Feb 04 20:55:52 2006 +0000
@@ -1929,7 +1929,7 @@
 
 		account->system_log	 = gaim_log_new(GAIM_LOG_SYSTEM,
 				gaim_account_get_username(account), account, NULL,
-				(login_time != 0) ? login_time : time(NULL));
+				(login_time != 0) ? login_time : time(NULL), NULL);
 	}
 
 	return account->system_log;
--- a/src/conversation.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/conversation.c	Sat Feb 04 20:55:52 2006 +0000
@@ -211,7 +211,7 @@
 {
 	conv->logs = g_list_append(NULL, gaim_log_new(conv->type == GAIM_CONV_TYPE_CHAT ? GAIM_LOG_CHAT :
 							   GAIM_LOG_IM, conv->name, conv->account,
-							   conv, time(NULL)));
+							   conv, time(NULL), NULL));
 }
 
 
--- a/src/gtkdebug.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/gtkdebug.c	Sat Feb 04 20:55:52 2006 +0000
@@ -219,7 +219,7 @@
 	}
 
 	tmp = gtk_imhtml_get_text(GTK_IMHTML(win->text), NULL, NULL);
-	fprintf(fp, "Gaim Debug Log : %s\n", gaim_date_format_full(time(NULL)));
+	fprintf(fp, "Gaim Debug Log : %s\n", gaim_date_format_full(NULL));
 	fprintf(fp, "%s", tmp);
 	g_free(tmp);
 
--- a/src/gtklog.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/gtklog.c	Sat Feb 04 20:55:52 2006 +0000
@@ -117,7 +117,7 @@
 
 			gtk_tree_store_append (lv->treestore, &iter, NULL);
 			gtk_tree_store_set(lv->treestore, &iter,
-					   0, gaim_date_format_full(log->time),
+					   0, gaim_date_format_full(localtime(&log->time)),
 					   1, log, -1);
 		}
 		g_free(read);
@@ -200,10 +200,14 @@
 		char *title;
 		if (log->type == GAIM_LOG_CHAT)
 			title = g_strdup_printf(_("<span size='larger' weight='bold'>Conversation in %s on %s</span>"),
-									log->name, gaim_date_format_full(log->time));
+									log->name,
+									log->tm ? gaim_date_format_full(log->tm) :
+									          gaim_date_format_full(localtime(&log->time)));
 		else
 			title = g_strdup_printf(_("<span size='larger' weight='bold'>Conversation with %s on %s</span>"),
-									log->name, gaim_date_format_full(log->time));
+									log->name,
+									log->tm ? gaim_date_format_full(log->tm) :
+									          gaim_date_format_full(localtime(&log->time)));
 
 		gtk_label_set_markup(GTK_LABEL(viewer->label), title);
 		g_free(title);
@@ -251,7 +255,8 @@
 	while (logs != NULL) {
 		GaimLog *log = logs->data;
 
-		month = gaim_utf8_strftime(_("%B %Y"), localtime(&log->time));
+		month = gaim_utf8_strftime(_("%B %Y"),
+		                           log->tm ? log->tm : localtime(&log->time));
 
 		if (strcmp(month, prev_top_month) != 0)
 		{
@@ -265,7 +270,7 @@
 		/* sub */
 		gtk_tree_store_append(lv->treestore, &child, &toplevel);
 		gtk_tree_store_set(lv->treestore, &child,
-						   0, gaim_date_format_full(log->time),
+						   0, log->tm ? gaim_date_format_full(log->tm) : gaim_date_format_full(localtime(&log->time)),
 						   1, log,
 						   -1);
 
--- a/src/gtkpounce.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/gtkpounce.c	Sat Feb 04 20:55:52 2006 +0000
@@ -1472,10 +1472,12 @@
 			name_shown = gaim_account_get_username(account);
 
 		if (reason == NULL)
-			gaim_notify_info(NULL, name_shown, tmp, gaim_date_format_full(time(NULL)));
+		{
+			gaim_notify_info(NULL, name_shown, tmp, gaim_date_format_full(NULL));
+		}
 		else
 		{
-			char *tmp2 = g_strdup_printf("%s\n\n%s", reason, gaim_date_format_full(time(NULL)));
+			char *tmp2 = g_strdup_printf("%s\n\n%s", reason, gaim_date_format_full(NULL));
 			gaim_notify_info(NULL, name_shown, tmp, tmp2);
 			g_free(tmp2);
 		}
--- a/src/log.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/log.c	Sat Feb 04 20:55:52 2006 +0000
@@ -72,7 +72,7 @@
  **************************************************************************/
 
 GaimLog *gaim_log_new(GaimLogType type, const char *name, GaimAccount *account,
-					  GaimConversation *conv, time_t time)
+                      GaimConversation *conv, time_t time, const struct tm *tm)
 {
 	GaimLog *log = g_new0(GaimLog, 1);
 	log->name = g_strdup(gaim_normalize(account, name));
@@ -81,6 +81,17 @@
 	log->time = time;
 	log->type = type;
 	log->logger_data = NULL;
+	if (tm != NULL)
+	{
+		log->tm = g_new0(struct tm, 1);
+		*(log->tm) = *tm;
+
+#ifdef HAVE_STRUCT_TM_TM_ZONE
+		/* XXX: This is so wrong... */
+		if (log->tm->tm_zone != NULL)
+			log->tm->tm_zone = (const char *)g_strdup(log->tm->tm_zone);
+#endif
+	}
 	log->logger = gaim_log_logger_get();
 	if (log->logger && log->logger->create)
 		log->logger->create(log);
@@ -93,6 +104,16 @@
 	if (log->logger && log->logger->finalize)
 		log->logger->finalize(log);
 	g_free(log->name);
+
+	if (log->tm != NULL)
+	{
+#ifdef HAVE_STRUCT_TM_TM_ZONE
+		/* XXX: This is so wrong... */
+		g_free((char *)log->tm->tm_zone);
+#endif
+		g_free(log->tm);
+	}
+
 	g_free(log);
 }
 
@@ -666,9 +687,35 @@
 		{
 			GaimLog *log;
 			GaimLogCommonLoggerData *data;
-			time_t stamp = gaim_str_to_time(filename, FALSE);
+#if defined (HAVE_TM_GMTOFF) && defined (HAVE_STRUCT_TM_TM_ZONE)
+			struct tm tm;
+			long tz_off;
+			const char *rest;
+			time_t stamp = gaim_str_to_time(filename, FALSE, &tm, &tz_off, &rest);
+			char *end;
+
+			/* As zero is a valid offset, GAIM_NO_TZ_OFF means no offset was
+			 * provided. See util.h. Yes, it's kinda ugly. */
+			if (tz_off != GAIM_NO_TZ_OFF)
+				tm.tm_gmtoff = tz_off - tm.tm_gmtoff;
 
-			log = gaim_log_new(type, name, account, NULL, stamp);
+			if (rest == NULL || (end = strchr(rest, '.')) == NULL)
+			{
+				log = gaim_log_new(type, name, account, NULL, stamp, NULL);
+			}
+			else
+			{
+				char *tmp = g_strndup(rest, end - rest);
+				tm.tm_zone = tmp;
+				log = gaim_log_new(type, name, account, NULL, stamp, &tm);
+				g_free(tmp);
+			}
+#else
+			time_t stamp = gaim_str_to_time(filename, FALSE, NULL, NULL, NULL);
+
+			log = gaim_log_new(type, name, account, NULL, stamp, NULL);
+#endif
+
 			log->logger = logger;
 			log->logger_data = data = g_new0(GaimLogCommonLoggerData, 1);
 			data->path = g_build_filename(path, filename, NULL);
@@ -932,7 +979,7 @@
 		if(!data->file)
 			return 0;
 
-		date = gaim_date_format_full(log->time);
+		date = gaim_date_format_full(localtime(&log->time));
 
 		written += fprintf(data->file, "<html><head>");
 		written += fprintf(data->file, "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">");
@@ -1071,7 +1118,7 @@
 			return 0;
 
 		written += fprintf(data->file, "Conversation with %s at %s on %s (%s)\n",
-			log->name, gaim_date_format_full(log->time),
+			log->name, gaim_date_format_full(localtime(&log->time)),
 			gaim_account_get_username(log->account), prpl);
 	}
 
@@ -1234,7 +1281,7 @@
 					newlen--;
 
 				if (newlen != 0) {
-					log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, -1);
+					log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, -1, NULL);
 					log->logger = old_logger;
 					log->time = lasttime;
 					data = g_new0(struct old_logger_data, 1);
@@ -1285,7 +1332,7 @@
 
 	if (logfound) {
 		if ((newlen = ftell(file) - lastoff) != 0) {
-			log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, -1);
+			log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, -1, NULL);
 			log->logger = old_logger;
 			log->time = lasttime;
 			data = g_new0(struct old_logger_data, 1);
--- a/src/log.h	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/log.h	Sat Feb 04 20:55:52 2006 +0000
@@ -112,13 +112,20 @@
 	GaimLogType type;                     /**< The type of log this is */
 	char *name;                           /**< The name of this log */
 	GaimAccount *account;                 /**< The account this log is taking
-											   place on */
+	                                           place on */
 	GaimConversation *conv;               /**< The conversation being logged */
 	time_t time;                          /**< The time this conversation
-											   started */
+	                                           started, converted to the local timezone */
+
 	GaimLogLogger *logger;                /**< The logging mechanism this log
-											   is to use */
+	                                           is to use */
 	void *logger_data;                    /**< Data used by the log logger */
+	struct tm *tm;                        /**< The time this conversation
+	                                           started, saved with original
+	                                           timezone data, if available and
+	                                           if struct tm has the BSD
+	                                           timezone fields, else @c NULL.
+	                                           Do NOT modify anything in this struct.*/
 };
 
 /**
@@ -173,10 +180,12 @@
  * @param account     The account the conversation is occurring on
  * @param conv        The conversation being logged
  * @param time        The time this conversation started
+ * @param tm          The time this conversation started, with timezone data,
+ *                    if available and if struct tm has the BSD timezone fields.
  * @return            The new log
  */
 GaimLog *gaim_log_new(GaimLogType type, const char *name, GaimAccount *account,
-					  GaimConversation *conv, time_t time);
+                      GaimConversation *conv, time_t time, const struct tm *tm);
 
 /**
  * Frees a log
--- a/src/protocols/irc/msgs.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/protocols/irc/msgs.c	Sat Feb 04 20:55:52 2006 +0000
@@ -241,7 +241,8 @@
 		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"), gaim_date_format_full(irc->whois.signon));
+		g_string_append_printf(info, _("<b>%s:</b> %s"), _("Online since"),
+		                       gaim_date_format_full(localtime(&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/message.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/protocols/jabber/message.c	Sat Feb 04 20:55:52 2006 +0000
@@ -311,7 +311,7 @@
 				const char *timestamp = xmlnode_get_attrib(child, "stamp");
 				jm->delayed = TRUE;
 				if(timestamp)
-					jm->sent = gaim_str_to_time(timestamp, TRUE);
+					jm->sent = gaim_str_to_time(timestamp, TRUE, NULL, NULL, NULL);
 			} else if(xmlns && !strcmp(xmlns, "jabber:x:conference") &&
 					jm->type != JABBER_MESSAGE_GROUPCHAT_INVITE &&
 					jm->type != JABBER_MESSAGE_ERROR) {
--- a/src/protocols/msn/msn.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/protocols/msn/msn.c	Sat Feb 04 20:55:52 2006 +0000
@@ -1425,7 +1425,7 @@
 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);
+	time_t t = gaim_str_to_time(tmp, FALSE, NULL, NULL, NULL);
 
 	g_free(tmp);
 	return g_strdup(gaim_date_format_short(localtime(&t)));
--- a/src/protocols/novell/nmevent.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/protocols/novell/nmevent.c	Sat Feb 04 20:55:52 2006 +0000
@@ -803,13 +803,13 @@
 		return -1;
 }
 
-guint32
+time_t
 nm_event_get_gmt(NMEvent * event)
 {
 	if (event)
 		return event->gmt;
 	else
-		return (guint32)-1;
+		return (time_t)-1;
 }
 
 NMERR_T
--- a/src/protocols/novell/nmevent.h	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/protocols/novell/nmevent.h	Sat Feb 04 20:55:52 2006 +0000
@@ -170,10 +170,8 @@
  *
  * @param event	The event.
  *
- * @return		The timestamp for the event. This is the number of
- *			    seconds since 1/1/1970 (as returned by the time()
- *				system call).
+ * @return		The timestamp for the event.
  */
-guint32 nm_event_get_gmt(NMEvent * event);
+time_t nm_event_get_gmt(NMEvent * event);
 
 #endif
--- a/src/protocols/novell/novell.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/protocols/novell/novell.c	Sat Feb 04 20:55:52 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, gaim_date_format_full(gmt));
+							  name, gaim_date_format_full(localtime(&gmt)));
 	secondary = _("Would you like to join the conversation?");
 
 	/* Set up parms list for the callbacks
--- a/src/protocols/oscar/oscar.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/protocols/oscar/oscar.c	Sat Feb 04 20:55:52 2006 +0000
@@ -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"), gaim_date_format_full(t));
+		oscar_string_append(gc->account, str, "\n<br>", _("Online Since"), gaim_date_format_full(localtime(&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"), gaim_date_format_full(t));
+		oscar_string_append(gc->account, str, "\n<br>", _("Member Since"), gaim_date_format_full(localtime(&t)));
 	}
 
 	if (userinfo->capabilities != 0) {
--- a/src/protocols/yahoo/yahoo_profile.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/protocols/yahoo/yahoo_profile.c	Sat Feb 04 20:55:52 2006 +0000
@@ -650,7 +650,7 @@
 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);
+	time_t t = gaim_str_to_time(tmp, FALSE, NULL, NULL, NULL);
 
 	g_free(tmp);
 	return g_strdup(gaim_date_format_short(localtime(&t)));
--- a/src/util.c	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/util.c	Sat Feb 04 20:55:52 2006 +0000
@@ -490,6 +490,14 @@
 	static char buf[128];
 	char *utf8;
 
+	g_return_val_if_fail(format != NULL, NULL);
+
+	if (tm == NULL)
+	{
+		time_t now = time(NULL);
+		tm = localtime(&now);
+	}
+
 	/* A return value of 0 is either an error (in
 	 * which case, the contents of the buffer are
 	 * undefined) or the empty string (in which
@@ -522,9 +530,9 @@
 }
 
 const char *
-gaim_date_format_full(time_t time)
+gaim_date_format_full(const struct tm *tm)
 {
-	return gaim_utf8_strftime("%c", localtime(&time));
+	return gaim_utf8_strftime("%c", tm);
 }
 
 const char *
@@ -549,21 +557,18 @@
 }
 
 time_t
-gaim_str_to_time(const char *timestamp, gboolean utc)
+gaim_str_to_time(const char *timestamp, gboolean utc,
+                 struct tm *tm, long *tz_off, const char **rest)
 {
 	time_t retval = 0;
 	struct tm *t;
-	char buf[32];
-	char *c;
+	const char *c = timestamp;
 	int year = 0;
-	int tzoff = 0;
+	long tzoff = GAIM_NO_TZ_OFF;
 
 	time(&retval);
 	t = localtime(&retval);
 
-	snprintf(buf, sizeof(buf), "%s", timestamp);
-	c = buf;
-
 	/* 4 digit year */
 	if (sscanf(c, "%04d", &year) && year > 1900)
 	{
@@ -575,7 +580,11 @@
 
 	/* 2 digit month */
 	if (!sscanf(c, "%02d", &t->tm_mon))
+	{
+		if (rest != NULL && *c != '\0')
+			*rest = c;
 		return 0;
+	}
 	c += 2;
 	if (*c == '-' || *c == '/')
 		c++;
@@ -583,46 +592,83 @@
 
 	/* 2 digit day */
 	if (!sscanf(c, "%02d", &t->tm_mday))
+	{
+		if (rest != NULL && *c != '\0')
+			*rest = c;
 		return 0;
+	}
 	c += 2;
 	if (*c == '/')
 	{
 		c++;
 
 		if (!sscanf(c, "%04d", &t->tm_year))
-			return 0;
+		{
+			if (rest != NULL && *c != '\0')
+				*rest = c;
+				return 0;
+		}
 		t->tm_year -= 1900;
 	}
-	else if (*c == 'T' || *c == '.') { /* we have more than a date, keep going */
-		c++; /* skip the "T" */
+	else if (*c == 'T' || *c == '.')
+	{
+		c++;
+		/* we have more than a date, keep going */
 
 		/* 2 digit hour */
-		if (sscanf(c, "%02d:%02d:%02d", &t->tm_hour, &t->tm_min, &t->tm_sec) == 3 ||
-			sscanf(c, "%02d%02d%02d", &t->tm_hour, &t->tm_min, &t->tm_sec) == 3) {
-			int tzhrs, tzmins;
-			c += 8;
-			if (*c == '.') /* dealing with precision we don't care about */
-				c += 4;
-			if ((*c == '+' || *c == '-') &&
-				sscanf(c+1, "%02d:%02d", &tzhrs, &tzmins)) {
-				tzoff = tzhrs*60*60 + tzmins*60;
-				if (*c == '+')
-					tzoff *= -1;
-			}
+		if ((sscanf(c, "%02d:%02d:%02d", &t->tm_hour, &t->tm_min, &t->tm_sec) == 3 && (c = c + 8)) ||
+		    (sscanf(c, "%02d%02d%02d", &t->tm_hour, &t->tm_min, &t->tm_sec) == 3 && (c = c + 6)))
+		{
+			gboolean offset_positive = FALSE;
+			int tzhrs;
+			int tzmins;
 
 			t->tm_isdst = -1;
 
-			if (tzoff || utc) {
+			if (*c == '.' && *(c+1) >= '0' && *(c+1) <= '9') /* dealing with precision we don't care about */
+				c += 4;
+			if (*c == '+')
+				offset_positive = TRUE;
+			if (((*c == '+' || *c == '-') && (c = c + 1)) &&
+			    ((sscanf(c, "%02d:%02d", &tzhrs, &tzmins) == 2 && (c = c + 5)) ||
+			     (sscanf(c, "%02d%02d", &tzhrs, &tzmins) == 2 && (c = c + 4))))
+			{
+				tzoff = tzhrs*60*60 + tzmins*60;
+				if (offset_positive)
+					tzoff *= -1;
+				/* We don't want the C library doing DST calculations
+				 * if we know the UTC offset already. */
+				t->tm_isdst = 0;
+			}
+
+			if (rest != NULL && *c != '\0')
+			{
+				if (*c == ' ')
+					c++;
+				if (*c != '\0')
+					*rest = c;
+			}
+
+			if (tzoff != GAIM_NO_TZ_OFF || utc)
+			{
+#if defined(_WIN32) || defined(HAVE_TM_GMTOFF) || defined (HAVE_TIMEZONE)
+				if (tzoff == GAIM_NO_TZ_OFF)
+					tzoff = 0;
+#endif
+
 #ifdef _WIN32
 				TIME_ZONE_INFORMATION tzi;
 				DWORD ret;
-				if ((ret = GetTimeZoneInformation(&tzi))
-						!= TIME_ZONE_ID_INVALID) {
+				if ((ret = GetTimeZoneInformation(&tzi)) != TIME_ZONE_ID_INVALID)
+				{
 					tzoff -= tzi.Bias * 60;
-					if (ret == TIME_ZONE_ID_DAYLIGHT) {
+					if (ret == TIME_ZONE_ID_DAYLIGHT)
+					{
 						tzoff -= tzi.DaylightBias * 60;
 					}
 				}
+				else
+					tzoff = GAIM_NO_TZ_OFF;
 #else
 #ifdef HAVE_TM_GMTOFF
 				tzoff += t->tm_gmtoff;
@@ -630,21 +676,35 @@
 #	ifdef HAVE_TIMEZONE
 				tzset();    /* making sure */
 				tzoff -= timezone;
-				t->tm_isdst = 0; /* I think this might fix it */
 #	endif
 #endif
 #endif /* _WIN32 */
 			}
 		}
+		else
+		{
+			if (*rest != NULL && *c != '\0')
+				*rest = c;
+		}
+	}
+
+	if (tm != NULL)
+	{
+		*tm = *t;
+		tm->tm_isdst = -1;
+		mktime(tm);
 	}
 
 	retval = mktime(t);
-	retval += tzoff;
+	if (tzoff != GAIM_NO_TZ_OFF)
+		retval += tzoff;
+
+	if (tz_off != NULL)
+		*tz_off = tzoff;
 
 	return retval;
 }
 
-
 /**************************************************************************
  * Markup Functions
  **************************************************************************/
--- a/src/util.h	Sat Feb 04 05:17:46 2006 +0000
+++ b/src/util.h	Sat Feb 04 20:55:52 2006 +0000
@@ -215,6 +215,11 @@
  *
  * This is essentially strftime(), but it has a static buffer
  * and handles the UTF-8 conversion for the caller.
+ *
+ * @param format The format string
+ * @param tm     The time to format, or @c NULL to use the current local time
+ *
+ * @return The formatted time, in UTF-8.
  */
 const char *gaim_utf8_strftime(const char *format, const struct tm *tm);
 
@@ -224,7 +229,7 @@
  * 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).
+ * @param time The time to format, or @c NULL to use the current local time
  *
  * @return The date, formatted as per the user's settings.
  */
@@ -236,7 +241,7 @@
  * 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).
+ * @param time The time to format, or @c NULL to use the current local time
  *
  * @return The timestamp, formatted as per the user's settings.
  */
@@ -248,11 +253,11 @@
  * 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).
+ * @param time The time to format, or @c NULL to use the current local time
  *
  * @return The date and time, formatted as per the user's settings.
  */
-const char *gaim_date_format_full(time_t time);
+const char *gaim_date_format_full(const struct tm *tm);
 
 /**
  * Formats a time into the user's preferred time format.
@@ -260,7 +265,8 @@
  * 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).
+ * @param time The time value to format.
+ * @param time The time to format, or @c NULL to use the current local time
  *
  * @return The time, formatted as per the user's settings.
  */
@@ -281,16 +287,32 @@
 time_t gaim_time_build(int year, int month, int day, int hour,
 					   int min, int sec);
 
+/** Used by gaim_str_to_time to indicate no timezone offset was
+  * specified in the timestamp string. */
+#define GAIM_NO_TZ_OFF -500000
+
 /**
  * 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
+ * @param utc       Assume UTC if no timezone specified
+ * @param tm        If not @c NULL, the caller can get a copy of the
+ *                  struct tm used to calculate the time_t return value.
+ * @param tz_off    If not @c NULL, the caller can get a copy of the
+ *                  timezone offset (from UTC) used to calculate the time_t
+ *                  return value. Note: Zero is a valid offset. As such,
+ *                  the value of the macro @c GAIM_NO_TZ_OFF indicates no
+ *                  offset was specified (which means that the local
+ *                  timezone was used in the calculation).
+ * @param rest      If not @c NULL, the caller can get a pointer to the
+ *                  part of @a timestamp left over after parsing is
+ *                  completed, if it's not the end of @a timestamp.
  *
  * @return A time_t.
  */
-time_t gaim_str_to_time(const char *timestamp, gboolean utc);
+time_t gaim_str_to_time(const char *timestamp, gboolean utc,
+                        struct tm *tm, long *tz_off, const char **rest);
 
 /*@}*/