]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Accept leap second in utc_mktime
authorMartti Rannanjärvi <martti.rannanjarvi@dovecot.fi>
Tue, 21 Feb 2017 14:25:25 +0000 (16:25 +0200)
committerGitLab <gitlab@git.dovecot.net>
Tue, 21 Feb 2017 17:44:39 +0000 (19:44 +0200)
Accept leap second at any time in utc_mktime since utc_mktime is being
used before applying the timezone offset everywhere.

src/lib/test-utc-mktime.c
src/lib/utc-mktime.c
src/lib/utc-mktime.h

index 33dce404d48ec5ae5a2de3e294091dba329d4ebb..c5fadb663078bff62b7249bfd2cd1b8e8d99ba96 100644 (file)
@@ -19,7 +19,21 @@ void test_utc_mktime(void)
 #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 @@ void test_utc_mktime(void)
 #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;
index 34ef0049a6af0c14b9150772ce09b02e9d5ffe85..eca4a66c31196206a857514ad38a45b80ec31287 100644 (file)
@@ -20,20 +20,33 @@ static int tm_cmp(const struct tm *tm1, const struct tm *tm2)
        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 @@ time_t utc_mktime(const struct tm *tm)
 #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)
index e1af2fafda851a4ca403c804e0b5eef09a88021c..9f32f6eb360b4a3ba287a4b418dfd40a990ba9c8 100644 (file)
@@ -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