diff libpurple/util.c @ 31372:17a4b32f4d46

propagate from branch 'im.pidgin.pidgin' (head 8480ab9498b0e22b164f3a40c100f59e2d6dd916) to branch 'im.pidgin.pidgin.next.minor' (head 490036a7b4ce807cb851ded91af7d959fe1c029e)
author Paul Aurich <paul@darkrain42.org>
date Sun, 30 Jan 2011 17:52:06 +0000
parents c61d3e5ec1da
children 1e1b598c725e 73b005a20d06
line wrap: on
line diff
--- a/libpurple/util.c	Sun Jan 09 04:40:07 2011 +0000
+++ b/libpurple/util.c	Sun Jan 30 17:52:06 2011 +0000
@@ -664,159 +664,192 @@
 	return mktime(&tm);
 }
 
+/* originally taken from GLib trunk 1-6-11 */
+/* originally licensed as LGPL 2+ */
+static time_t
+mktime_utc(struct tm *tm)
+{
+	time_t retval;
+
+#ifndef HAVE_TIMEGM
+	static const gint days_before[] =
+	{
+		0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+	};
+#endif
+
+#ifndef HAVE_TIMEGM
+	if (tm->tm_mon < 0 || tm->tm_mon > 11)
+		return (time_t) -1;
+
+	retval = (tm->tm_year - 70) * 365;
+	retval += (tm->tm_year - 68) / 4;
+	retval += days_before[tm->tm_mon] + tm->tm_mday - 1;
+
+	if (tm->tm_year % 4 == 0 && tm->tm_mon < 2)
+		retval -= 1;
+
+	retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec;
+#else
+	retval = timegm (tm);
+#endif /* !HAVE_TIMEGM */
+
+	return retval;
+}
+
 time_t
 purple_str_to_time(const char *timestamp, gboolean utc,
-                 struct tm *tm, long *tz_off, const char **rest)
+	struct tm *tm, long *tz_off, const char **rest)
 {
-	time_t retval = 0;
-	static struct tm t;
-	const char *c = timestamp;
-	int year = 0;
+	struct tm t;
+	const gchar *str;
+	gint year = 0;
 	long tzoff = PURPLE_NO_TZ_OFF;
-
-	time(&retval);
-	localtime_r(&retval, &t);
+	time_t retval;
+	gboolean mktime_with_utc = TRUE;
 
 	if (rest != NULL)
 		*rest = NULL;
 
+	g_return_val_if_fail(timestamp != NULL, 0);
+
+	str = timestamp;
+
+	/* Strip leading whitespace */
+	while (g_ascii_isspace(*str))
+		str++;
+
+	if (*str == '\0') {
+		if (rest != NULL && *str != '\0')
+			*rest = str;
+
+		return 0;
+	}
+
+	if (!g_ascii_isdigit(*str) && *str != '-' && *str != '+') {
+		if (rest != NULL && *str != '\0')
+			*rest = str;
+
+		return 0;
+	}
+
 	/* 4 digit year */
-	if (sscanf(c, "%04d", &year) && year > 1900)
-	{
-		c += 4;
-		if (*c == '-')
-			c++;
+	if (sscanf(str, "%04d", &year) && year >= 1900) {
+		str += 4;
+
+		if (*str == '-' || *str == '/')
+			str++;
+
 		t.tm_year = year - 1900;
 	}
 
 	/* 2 digit month */
-	if (!sscanf(c, "%02d", &t.tm_mon))
-	{
-		if (rest != NULL && *c != '\0')
-			*rest = c;
+	if (!sscanf(str, "%02d", &t.tm_mon)) {
+		if (rest != NULL && *str != '\0')
+			*rest = str;
+
 		return 0;
 	}
-	c += 2;
-	if (*c == '-' || *c == '/')
-		c++;
+
+	str += 2;
 	t.tm_mon -= 1;
 
+	if (*str == '-' || *str == '/')
+		str++;
+
 	/* 2 digit day */
-	if (!sscanf(c, "%02d", &t.tm_mday))
-	{
-		if (rest != NULL && *c != '\0')
-			*rest = c;
+	if (!sscanf(str, "%02d", &t.tm_mday)) {
+		if (rest != NULL && *str != '\0')
+			*rest = str;
+
 		return 0;
 	}
-	c += 2;
-	if (*c == '/')
-	{
-		c++;
-
-		if (!sscanf(c, "%04d", &t.tm_year))
-		{
-			if (rest != NULL && *c != '\0')
-				*rest = c;
+
+	str += 2;
+
+	/* Grab the year off the end if there's still stuff */
+	if (*str == '/' || *str == '-') {
+		/* But make sure we don't read the year twice */
+		if (year >= 1900) {
+			if (rest != NULL && *str != '\0')
+				*rest = str;
+
+			return 0;
+		}
+
+		str++;
+
+		if (!sscanf(str, "%04d", &t.tm_year)) {
+			if (rest != NULL && *str != '\0')
+				*rest = str;
+
 			return 0;
 		}
+
 		t.tm_year -= 1900;
-	}
-	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 && (c = c + 8)) ||
-		    (sscanf(c, "%02d%02d%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 && (c = c + 6)))
+	} else if (*str == 'T' || *str == '.') {
+		str++;
+
+		/* Continue grabbing the hours/minutes/seconds */
+		if ((sscanf(str, "%02d:%02d:%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 &&
+				(str += 8)) ||
+		    (sscanf(str, "%02d%02d%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 &&
+				(str += 6)))
 		{
-			gboolean offset_positive = FALSE;
-			int tzhrs;
-			int tzmins;
-
-			t.tm_isdst = -1;
-
-			if (*c == '.') {
+			gint sign, tzhrs, tzmins;
+
+			if (*str == '.') {
+				/* Cut off those pesky micro-seconds */
 				do {
-					c++;
-				} while (*c >= '0' && *c <= '9'); /* dealing with precision we don't care about */
-			}
-			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;
-			}
-			else if ((*c == 'Z') && (c = c + 1))
-			{
-				/* 'Z' = Zulu = UTC */
-				tzoff = 0;
+					str++;
+				} while (*str >= '0' && *str <= '9');
 			}
-			else if (utc)
-			{
-				static struct tm tmptm;
-				time_t tmp;
-				tmp = mktime(&t);
-				/* we care about whether it *was* dst, and the offset, here on this
-				 * date, not whether we are currently observing dst locally *now*.
-				 * This isn't perfect, because we would need to know in advance the
-				 * offset we are trying to work out in advance to be sure this
-				 * works for times around dst transitions but it'll have to do. */
-				localtime_r(&tmp, &tmptm);
-				t.tm_isdst = tmptm.tm_isdst;
-#ifdef HAVE_TM_GMTOFF
-				t.tm_gmtoff = tmptm.tm_gmtoff;
-#endif
-			}
-
-			if (rest != NULL && *c != '\0')
-			{
-				if (*c == ' ')
-					c++;
-				if (*c != '\0')
-					*rest = c;
+
+			sign = (*str == '+') ? -1 : 1;
+
+			/* Process the timezone */
+			if (*str == '+' || *str == '-') {
+				str++;
+
+				if (((sscanf(str, "%02d:%02d", &tzhrs, &tzmins) == 2 && (str += 5)) ||
+					(sscanf(str, "%02d%02d", &tzhrs, &tzmins) == 2 && (str += 4))))
+				{
+					tzoff = tzhrs * 60 * 60 + tzmins * 60;
+					tzoff *= sign;
+				} else {
+					if (rest != NULL && *str != '\0')
+						*rest = str;
+
+					return 0;
+				}
+			} else if (*str == 'Z') {
+				/* 'Z' = Zulu = UTC */
+				str++;
+				utc = TRUE;
+			} else if (!utc) {
+				/* Local Time */
+				t.tm_isdst = -1;
+				mktime_with_utc = FALSE;
 			}
 
-			if (tzoff != PURPLE_NO_TZ_OFF || utc)
-			{
-#if defined(_WIN32)
-				long sys_tzoff;
-#endif
-
-#if defined(_WIN32) || defined(HAVE_TM_GMTOFF) || defined (HAVE_TIMEZONE)
-				if (tzoff == PURPLE_NO_TZ_OFF)
-					tzoff = 0;
-#endif
-
-#ifdef _WIN32
-				if ((sys_tzoff = wpurple_get_tz_offset()) == -1)
-					tzoff = PURPLE_NO_TZ_OFF;
-				else
-					tzoff += sys_tzoff;
-#else
-#ifdef HAVE_TM_GMTOFF
-				tzoff += t.tm_gmtoff;
-#else
-#	ifdef HAVE_TIMEZONE
-				tzset();    /* making sure */
-				tzoff -= timezone;
-#	endif
-#endif
-#endif /* _WIN32 */
-			}
-		}
-		else
-		{
-			if (rest != NULL && *c != '\0')
-				*rest = c;
+			if (utc)
+				tzoff = 0;
 		}
 	}
 
-	retval = mktime(&t);
+	if (rest != NULL && *str != '\0') {
+		/* Strip trailing whitespace */
+		while (g_ascii_isspace(*str))
+			str++;
+
+		if (*str != '\0')
+			*rest = str;
+	}
+
+	if (mktime_with_utc)
+		retval = mktime_utc(&t);
+	else
+		retval = mktime(&t);
 
 	if (tm != NULL)
 		*tm = t;