changeset 21661:c4584d7dc07e

lib: Accept leap second in utc_mktime Accept leap second at any time in utc_mktime since utc_mktime is being used before applying the timezone offset everywhere.
author Martti Rannanj?rvi <martti.rannanjarvi@dovecot.fi>
date Tue, 21 Feb 2017 16:25:25 +0200
parents ed928407244b
children 4f63ef17d9dc
files src/lib/test-utc-mktime.c src/lib/utc-mktime.c src/lib/utc-mktime.h
diffstat 3 files changed, 50 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib/test-utc-mktime.c	Tue Feb 21 14:42:01 2017 +0200
+++ b/src/lib/test-utc-mktime.c	Tue Feb 21 16:25:25 2017 +0200
@@ -19,7 +19,21 @@
 #endif
 		{ 2007, 11, 7, 1, 7, 20 },
 		{ 1970, 1, 1, 0, 0, 0 },
-		{ 2038, 1, 19, 3, 14, 7 }
+		{ 2038, 1, 19, 3, 14, 7 },
+		{ INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX },
+		{ 2038, 1, 19, 3, 14, 8 },
+		{ 2106, 2, 7, 6, 28, 15 },
+		{ 2106, 2, 7, 6, 28, 16 },
+		/* June leap second */
+		{ 2015, 6, 30, 23, 59, 59 },
+		{ 2015, 6, 30, 23, 59, 60 },
+		{ 2015, 7, 1, 0, 0, 0 },
+		/* Invalid leap second */
+		{ 2017, 1, 24, 16, 40, 60 },
+		/* Dec leap second */
+		{ 2016, 12, 31, 23, 59, 59 },
+		{ 2016, 12, 31, 23, 59, 60 },
+		{ 2017, 1, 1, 0, 0, 0 },
 	};
 	static time_t output[] = {
 #ifdef TIME_T_SIGNED
@@ -31,8 +45,23 @@
 #endif
 		1194397640,
 		0,
-		2147483647
+		2147483647,
+		-1,
+		2147483648,
+		4294967295,
+		4294967296,
+		/* June leap second */
+		1435708799,
+		1435708799,
+		1435708800,
+		/* Invalid leap second - utc_mktime() doesn't mind */
+		1485276059,
+		/* Dec leap second */
+		1483228799,
+		1483228799,
+		1483228800,
 	};
+	i_assert(N_ELEMENTS(input) == N_ELEMENTS(output));
 	struct tm tm;
 	unsigned int i;
 	time_t t;
--- a/src/lib/utc-mktime.c	Tue Feb 21 14:42:01 2017 +0200
+++ b/src/lib/utc-mktime.c	Tue Feb 21 16:25:25 2017 +0200
@@ -20,20 +20,33 @@
 	return tm1->tm_sec - tm2->tm_sec;
 }
 
+static inline void adjust_leap_second(struct tm *tm)
+{
+	if (tm->tm_sec == 60)
+		tm->tm_sec = 59;
+}
+
 #ifdef HAVE_TIMEGM
+/* Normalization done by timegm is considered a failure here, since it means
+ * the timestamp is not valid as-is. Leap second 60 is adjusted to 59 before
+ * this though. */
 time_t utc_mktime(const struct tm *tm)
 {
-	struct tm mod_tm = *tm;
+	struct tm leap_adj_tm = *tm;
+	adjust_leap_second(&leap_adj_tm);
+	struct tm tmp = leap_adj_tm;
 	time_t t;
 
-	t = timegm(&mod_tm);
-	if (tm_cmp(tm, &mod_tm) != 0)
+	t = timegm(&tmp);
+	if (tm_cmp(&leap_adj_tm, &tmp) != 0)
 		return (time_t)-1;
 	return t;
 }
 #else
 time_t utc_mktime(const struct tm *tm)
 {
+	struct tm leap_adj_tm = *tm;
+	adjust_leap_second(&leap_adj_tm);
 	const struct tm *try_tm;
 	time_t t;
 	int bits, dir;
@@ -49,7 +62,7 @@
 #endif
 	for (bits = TIME_T_MAX_BITS - 2;; bits--) {
 		try_tm = gmtime(&t);
-		dir = tm_cmp(tm, try_tm);
+		dir = tm_cmp(&leap_adj_tm, try_tm);
 		if (dir == 0)
 			return t;
 		if (bits < 0)
--- a/src/lib/utc-mktime.h	Tue Feb 21 14:42:01 2017 +0200
+++ b/src/lib/utc-mktime.h	Tue Feb 21 16:25:25 2017 +0200
@@ -4,7 +4,8 @@
 #include <time.h>
 
 /* Like mktime(), but assume that tm is in UTC. Unlike mktime(), values in
-   tm fields must be in valid range. */
+   tm fields must be in valid range. Leap second is accepted any time though
+   since utc_mktime is often used before applying the time zone offset. */
 time_t utc_mktime(const struct tm *tm);
 
 #endif