261102.1 · 5c001b18d7237cce1fdba63608732163f978dd23
Fix mktime/0 to allow the last two seconds of 1969 (#3671)
The mktime/0 function failed for 1969-12-31T23:59:59Z with "invalid gmtime representation", because its result -1 is also the failure value of timegm(), and for 1969-12-31T23:59:58Z with "mktime not supported on this platform", because of a leftover check for -2.
Tell a failure apart from the valid result -1 by tm_wday, which is set to -1 before the conversion and overwritten only on success, and remove the check for -2. Now `-2, -1 | gmtime | mktime` round-trip to -2 and -1, as do normalized inputs such as [1970,0,0,23,59,59,0,0], which gives -1. Inputs that the platform rejects, such as years before 1900 on macOS, still fail with "invalid gmtime representation".
reading declarations
Changes to src/. All files
@@ -1572,20 +1572,23 @@ static int setenv(const char *var, const char *val, int ovr) * Our wrapper tries to use timegm() if available, or mktime() and * correct for its side-effects if possible. *- * Returns (time_t)-2 if mktime()'s side-effects cannot be corrected.+ * Returns (time_t)-1 on failure, which is also the valid result for+ * 1969-12-31T23:59:59Z. Tell them apart by tm_wday, which is set to -1 here+ * and overwritten by the conversion only on success. */ static time_t my_mktime(struct tm *tm) {+ tm->tm_wday = -1; #ifdef HAVE_TIMEGM return timegm(tm); #elif HAVE_TM_TM_GMT_OFF time_t t = mktime(tm);- if (t == (time_t)-1)+ if (t == (time_t)-1 && tm->tm_wday == -1) return t; return t + tm->tm_gmtoff; #elif HAVE_TM___TM_GMT_OFF time_t t = mktime(tm);- if (t == (time_t)-1)+ if (t == (time_t)-1 && tm->tm_wday == -1) return t; return t + tm->__tm_gmtoff; #elif WIN32@@ -1793,10 +1796,8 @@ static jv f_mktime(jq_state *jq, jv a) { if (!jv2tm(a, &tm, 0)) return jv_invalid_with_msg(jv_string("mktime requires parsed datetime inputs")); time_t t = my_mktime(&tm);- if (t == (time_t)-1)+ if (t == (time_t)-1 && tm.tm_wday == -1) return jv_invalid_with_msg(jv_string("invalid gmtime representation"));- if (t == (time_t)-2)- return jv_invalid_with_msg(jv_string("mktime not supported on this platform")); return jv_number(t); }