# HG changeset patch
# Node ID 4b58e52ca86c
# Parent  03ad128b5361
diff --git a/libpurple/tests/test_util.c b/libpurple/tests/test_util.c
--- a/libpurple/tests/test_util.c
+++ b/libpurple/tests/test_util.c
@@ -160,23 +160,51 @@
 	long tz_off;
 	const char *rest;
 	time_t timestamp;
+	gchar *oldtz = g_strdup(g_getenv("TZ"));
+
+	timestamp = purple_str_to_time("2010-08-27.134202-0700PDT", FALSE, &tm, &tz_off, &rest);
+	fail_unless(1282941722 == timestamp);
+	fail_unless((-7 * 60 * 60) == tz_off);
+	assert_string_equal("PDT", rest);
 
 	fail_unless(377182200 == purple_str_to_time("19811214T12:50:00", TRUE, NULL, NULL, NULL));
-	fail_unless(377203800 == purple_str_to_time("19811214T12:50:00-06", FALSE, NULL, NULL, NULL));
 	fail_unless(1175919261 == purple_str_to_time("20070407T04:14:21", TRUE, NULL, NULL, NULL));
 	fail_unless(1282941722 == purple_str_to_time("2010-08-27.204202", TRUE, NULL, NULL, NULL));
 	fail_unless(1175919261 == purple_str_to_time("20070407T04:14:21.3234", TRUE, NULL, NULL, NULL));
 	fail_unless(1175919261 == purple_str_to_time("20070407T04:14:21Z", TRUE, NULL, NULL, NULL));
 	fail_unless(1631512800 == purple_str_to_time("09-13-2021", TRUE, NULL, NULL, NULL));
 
-	timestamp = purple_str_to_time("2010-08-27.134202-0700PDT", FALSE, &tm, &tz_off, &rest);
-	fail_unless(1282941722 == timestamp);
-	fail_unless((-7 * 60 * 60) == tz_off);
-	assert_string_equal("PDT", rest);
+	/* For testing local time We use Asia/Kathmandu because it's +05:45 and
+	 * doesn't have DST which means the test should always pass regardles of
+	 * the time of year, at least until Kathmandu changes something.
+	 */
+	g_setenv("TZ", "Asia/Kathmandu", TRUE);
+
+	/* There's a timezone in this timestamp, so the returned value with be in
+	 * UTC with the offset added/subtracted.
+	 */
+	fail_unless(377203800 == purple_str_to_time("19811214T12:50:00-06", FALSE, NULL, NULL, NULL));
+
+	/* This also has a tz, so the returned value with be utc with the given
+	 * offset.
+	 */
+	fail_unless(1569746481 == purple_str_to_time("2019-09-29T08:41:21.401000+00:00", FALSE, NULL, NULL, NULL));
 
+	/* while it looks like the time is specified, this time is not valid
+	 * according to our parser, so it's just the date which will be handled by
+	 * localtime.
+	 */
 	timestamp = purple_str_to_time("09/13/202115:34:34", TRUE, NULL, NULL, &rest);
-	fail_unless(1631512800 == timestamp);
+	fail_unless(1631470500 == timestamp);
 	assert_string_equal("15:34:34", rest);
+
+	/* finally revert the TZ environment variable */
+	if(oldtz != NULL) {
+		g_setenv("TZ", oldtz, TRUE);
+		g_free(oldtz);
+	} else {
+		g_unsetenv("TZ");
+	}
 }
 END_TEST
 
diff --git a/libpurple/util.c b/libpurple/util.c
--- a/libpurple/util.c
+++ b/libpurple/util.c
@@ -756,6 +756,7 @@
 	GMatchInfo *info = NULL;
 	gboolean mktime_with_utc = FALSE, matched = FALSE;
 	gchar *match = NULL;
+	gchar *hours = NULL, *minutes = NULL, *seconds = NULL;
 	struct tm t;
 	long tzoff = PURPLE_NO_TZ_OFF;
 	time_t retval;
@@ -818,83 +819,80 @@
 	g_free(match);
 
 	/* get the hour */
-	match = g_match_info_fetch_named(info, "hours");
-	if(match != NULL && *match != '\0') {
-		t.tm_hour = atoi(match);
-	}
-	g_free(match);
-
-	/* get the minutes */
-	match = g_match_info_fetch_named(info, "minutes");
-	if(match != NULL && *match != '\0') {
-		t.tm_min = atoi(match);
-	}
-	g_free(match);
-
-	/* get the seconds */
-	match = g_match_info_fetch_named(info, "seconds");
-	if(match != NULL && *match != '\0') {
-		t.tm_sec = atoi(match);
-	}
-	g_free(match);
-
-	/* check if this is utc time */
-	match = g_match_info_fetch_named(info, "utc");
-	if(match != NULL && *match != '\0') {
-		tzoff = 0;
-		g_free(match);
-	} else {
-		/* free match if it was just an empty string */
-		g_free(match);
-
-		/* if match is null, check if we have tzsign which is required */
-		match = g_match_info_fetch_named(info, "tzsign");
-		if(match != NULL) {
-			gint tzsign = -1, tzhour = 0, tzminute = 0;
-
-			/* Figure out if we're ahead or behind of utc, assuming ahead by
-			 * default.
-			 */
-			if(*match == '+') {
-				tzsign = -1;
-			}
+	hours = g_match_info_fetch_named(info, "hours");
+	minutes = g_match_info_fetch_named(info, "minutes");
+	seconds = g_match_info_fetch_named(info, "seconds");
+
+	if(hours != NULL && *hours != '\0' &&
+		minutes != NULL && *minutes != '\0' &&
+		seconds != NULL && *seconds != '\0')
+	{
+		t.tm_hour = atoi(hours);
+		t.tm_min = atoi(minutes);
+		t.tm_sec = atoi(seconds);
+
+		/* check if this is utc time */
+		match = g_match_info_fetch_named(info, "utc");
+		if(match != NULL && *match != '\0') {
+			g_free(match);
+
+			tzoff = 0;
+			mktime_with_utc = TRUE;
+		} else {
+			/* free match if it was just an empty string */
 			g_free(match);
 
-			/* get the tz hour */
-			match = g_match_info_fetch_named(info, "tzhour");
-			if(match != NULL && *match != '\0') {
-				tzhour = atoi(match);
-			}
-			g_free(match);
-
-			/* get the tz minute */
-			match = g_match_info_fetch_named(info, "tzminute");
-			if(match != NULL && *match != '\0') {
-				tzminute = atoi(match);
-			}
-			g_free(match);
-
-			/* we need at least either an hour or minute to offset the timezone */
-			if(tzhour > 0 || tzminute > 0) {
+			/* if match is null, check if we have tzsign which is required */
+			match = g_match_info_fetch_named(info, "tzsign");
+			if(match != NULL) {
+				gint tzsign = -1, tzhour = 0, tzminute = 0;
+
+				/* Figure out if we're ahead or behind of utc, assuming ahead by
+				 * default.
+				 */
+				if(*match == '+') {
+					tzsign = 1;
+				}
+				g_free(match);
+
+				/* get the tz hour */
+				match = g_match_info_fetch_named(info, "tzhour");
+				if(match != NULL && *match != '\0') {
+					tzhour = atoi(match);
+				}
+				g_free(match);
+
+				/* get the tz minute */
+				match = g_match_info_fetch_named(info, "tzminute");
+				if(match != NULL && *match != '\0') {
+					tzminute = atoi(match);
+				}
+				g_free(match);
+
+				/* if the timezone is utc +00:00 or -00:00 tzoff will multiple out to
+				 * be zero like it should be.
+				 */
 				mktime_with_utc = TRUE;
 				tzoff = tzsign * ((tzhour * 3600) + (tzminute * 60));
 			}
 		}
-	}
-
-	/* If we have a time, figure out if we need to adjust our tz offset. */
-	if(t.tm_hour > 0 || t.tm_min > 0 || t.tm_sec > 0) {
+
+		/* If we have a time, figure out if we need to adjust our tz offset. */
 		if(!mktime_with_utc) {
 			if(utc) {
 				mktime_with_utc = TRUE;
 				tzoff = 0;
 			} else {
 				/* Local Time */
-				t.tm_isdst = -1;
+				t.tm_isdst = -1; /* -1 means dst info is not available */
 			}
 		}
 	}
 
+	g_free(hours);
+	g_free(minutes);
+	g_free(seconds);
+
 	if(mktime_with_utc) {
 		retval = mktime_utc(&t);
 	} else {
