changeset 15000:82d1a2fc6d25

[gaim-migrate @ 17778] SF Patch #1598994 from lexande Fixes SF Bug #1581999 Original Description: "The gaim 2.0.0beta5 log reader plugin uses the "Date" XML field in MSN logs, the format of which varies between locales, leading to logs being displayed on incorrect/future dates in locales that do not use the MM/DD/YYYY format. This patch fixes the plugin to use the "DateTime" field which has YYYY-MM-DD date in all locales." I totally Warmenhovened this patch. My approach: "I'm using the Date, Time, and DateTime fields. If I can determine that the log matches the current timezone, it'll display 100% properly in the log viewer. If it has a reasonable offset, it'll display as the original time, sort in the right place, but not have a timezone abbreviation. If the offset is bogus or I can't parse the Date or Time field, it'll display in UTC, but still sort properly. I think this is as ideal as is possible, given the data we have to work with." You don't have to understand this. ;) committer: Tailor Script <tailor@pidgin.im>
author Richard Laager <rlaager@wiktel.com>
date Sun, 19 Nov 2006 06:54:38 +0000
parents db590283788d
children 89ae31668a9c
files libgaim/plugins/log_reader.c
diffstat 1 files changed, 160 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/libgaim/plugins/log_reader.c	Sun Nov 19 05:56:50 2006 +0000
+++ b/libgaim/plugins/log_reader.c	Sun Nov 19 06:54:38 2006 +0000
@@ -445,52 +445,174 @@
 	GString *text;
 };
 
-static time_t msn_logger_parse_timestamp(xmlnode *message)
+/* This function is really confusing.  It makes baby rlaager cry...
+   In other news: "You lost a lot of blood but we found most of it."
+ */
+static time_t msn_logger_parse_timestamp(xmlnode *message, struct tm **tm_out)
 {
+	const char *datetime;
+	static struct tm tm2;
+	time_t stamp;
 	const char *date;
 	const char *time;
-	struct tm tm;
+	int month;
+	int day;
+	int year;
+	int hour;
+	int min;
+	int sec;
 	char am_pm;
-
-	g_return_val_if_fail(message != NULL, (time_t)0);
+	char *str;
+	static struct tm tm;
+	time_t t;
+	time_t diff;
 
-	date = xmlnode_get_attrib(message, "Date");
-	if (!(date && *date)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "MSN log timestamp parse",
-			"Attribute missing: %s\n", "Date");
-		return (time_t)0;
+#ifndef G_DISABLE_CHECKS
+	if (message != NULL)
+	{
+		*tm_out = NULL;
+
+		/* Trigger the usual warning. */
+		g_return_val_if_fail(message != NULL, (time_t)0);
 	}
+#endif
 
-	time = xmlnode_get_attrib(message, "Time");
-	if (!(time && *time)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "MSN log timestamp parse",
-			"Attribute missing: %s\n", "Time");
+	datetime = xmlnode_get_attrib(message, "DateTime");
+	if (!(datetime && *datetime))
+	{
+		gaim_debug_error("MSN log timestamp parse",
+		                 "Attribute missing: %s\n", "DateTime");
 		return (time_t)0;
 	}
 
-	if (sscanf(date, "%u/%u/%u", &tm.tm_mon, &tm.tm_mday, &tm.tm_year) != 3)
-		gaim_debug(GAIM_DEBUG_ERROR, "MSN log timestamp parse",
-			"%s parsing error\n", "Date");
+	stamp = gaim_str_to_time(datetime, TRUE, &tm2, NULL, NULL);
+#ifdef HAVE_TM_GMTOFF
+	tm2.tm_gmtoff = 0;
+#endif
+#ifdef HAVE_STRUCT_TM_TM_ZONE
+	/* This is used in the place of a timezone abbreviation if the
+	 * offset is way off.  The user should never really see it, but
+	 * it's here just in case.  The parens are to make it clear it's
+	 * not a real timezone. */
+	tm2.tm_zone = _("(UTC)");
+#endif
+
+
+	date = xmlnode_get_attrib(message, "Date");
+	if (!(date && *date))
+	{
+		gaim_debug_error("MSN log timestamp parse",
+		                 "Attribute missing: %s\n", "Date");
+		*tm_out = &tm2;
+		return stamp;
+	}
+
+	time = xmlnode_get_attrib(message, "Time");
+	if (!(time && *time))
+	{
+		gaim_debug_error("MSN log timestamp parse",
+		                 "Attribute missing: %s\n", "Time");
+		*tm_out = &tm2;
+		return stamp;
+	}
 
-	if (sscanf(time, "%u:%u:%u %c", &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &am_pm) != 4)
-		gaim_debug(GAIM_DEBUG_ERROR, "MSN log timestamp parse",
-			"%s parsing error\n", "Time");
+	if (sscanf(date, "%u/%u/%u", &month, &day, &year) != 3)
+	{
+		gaim_debug_error("MSN log timestamp parse",
+		                 "%s parsing error\n", "Date");
+		*tm_out = &tm2;
+		return stamp;
+	}
+	else
+	{
+		if (month > 12)
+		{
+			int tmp = day;
+			day = month;
+			month = tmp;
+		}
+	}
+
+	if (sscanf(time, "%u:%u:%u %c", &hour, &min, &sec, &am_pm) != 4)
+	{
+		gaim_debug_error("MSN log timestamp parse",
+		                 "%s parsing error\n", "Time");
+		*tm_out = &tm2;
+		return stamp;
+	}
+
+        if (am_pm == 'P') {
+                hour += 12;
+        } else if (hour == 12) {
+                /* 12 AM = 00 hr */
+                hour = 0;
+        }
 
-	tm.tm_year -= 1900;
-	tm.tm_mon  -= 1;
-	if (am_pm == 'P') {
-		tm.tm_hour += 12;
-	} else if (tm.tm_hour == 12) {
-		/* 12 AM = 00 hr */
-		tm.tm_hour = 0;
+	str = g_strdup_printf("%04i-%02i-%02iT%02i:%02i:%02i", year, month, day, hour, min, sec);
+	t = gaim_str_to_time(str, TRUE, &tm, NULL, NULL);
+
+
+	if (stamp > t)
+		diff = stamp - t;
+	else
+		diff = t - stamp;
+
+	if (diff > (14 * 60 * 60))
+	{
+		if (day <= 12)
+		{
+			/* Swap day & month variables, to see if it's a non-US date. */
+			g_free(str);
+			str = g_strdup_printf("%04i-%02i-%02iT%02i:%02i:%02i", year, month, day, hour, min, sec);
+			t = gaim_str_to_time(str, TRUE, &tm, NULL, NULL);
+
+			if (stamp > t)
+				diff = stamp - t;
+			else
+				diff = t - stamp;
+
+			if (diff > (14 * 60 * 60))
+			{
+				/* We got a time, it's not impossible, but
+				 * the diff is too large.  Display the UTC time. */
+				g_free(str);
+				*tm_out = &tm2;
+				return stamp;
+			}
+			else
+			{
+				/* Legal time */
+				/* Fall out */
+			}
+		}
+		else
+		{
+			/* We got a time, it's not impossible, but
+			 * the diff is too large.  Display the UTC time. */
+			g_free(str);
+			*tm_out = &tm2;
+			return stamp;
+		}
 	}
-	/* Let the C library deal with daylight savings time. */
-	tm.tm_isdst = -1;
 
-	return mktime(&tm);
+	/* If we got here, the time is legal with a reasonable offset.
+	 * Let's find out if it's in our TZ. */
+	if (gaim_str_to_time(str, FALSE, &tm, NULL, NULL) == stamp)
+	{
+		g_free(str);
+		*tm_out = &tm;
+		return stamp;
+	}
+	g_free(str);
+
+	/* The time isn't in our TZ, but it's reasonable. */
+#ifdef HAVE_STRUCT_TM_TM_ZONE
+	tm.tm_zone = "   ";
+#endif
+	*tm_out = &tm;
+	return stamp;
 }
 
-
 static GList *msn_logger_list(GaimLogType type, const char *sn, GaimAccount *account)
 {
 	GList *list = NULL;
@@ -725,6 +847,8 @@
 			 * The session ID differs from the last message.
 			 * Thus, this is the start of a new conversation.
 			 */
+			struct tm *tm;
+			time_t stamp;
 			GaimLog *log;
 
 			data = g_new0(struct msn_logger_data, 1);
@@ -734,8 +858,9 @@
 			data->text = NULL;
 			data->last_log = FALSE;
 
-			/* 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);
+			stamp = msn_logger_parse_timestamp(message, &tm);
+
+			log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, stamp, tm);
 			log->logger = msn_logger;
 			log->logger_data = data;
 
@@ -792,7 +917,7 @@
 		enum name_guesses name_guessed = NAME_GUESS_UNKNOWN;
 		const char *their_name;
 		time_t time_unix;
-		struct tm *tm_new;
+		struct tm *tm;
 		char *timestamp;
 		char *tmp;
 		const char *style;
@@ -961,11 +1086,10 @@
 			text = g_string_append(text, ";\">");
 		}
 
-		time_unix = msn_logger_parse_timestamp(message);
-		tm_new = localtime(&time_unix);
+		time_unix = msn_logger_parse_timestamp(message, &tm);
 
 		timestamp = g_strdup_printf("<font size=\"2\">(%02u:%02u:%02u)</font> ",
-				tm_new->tm_hour, tm_new->tm_min, tm_new->tm_sec);
+				tm->tm_hour, tm->tm_min, tm->tm_sec);
 		text = g_string_append(text, timestamp);
 		g_free(timestamp);