]> granicus.if.org Git - python/commitdiff
bpo-34373: Fix time.mktime() on AIX (GH-12726)
authorVictor Stinner <vstinner@redhat.com>
Tue, 9 Apr 2019 17:12:26 +0000 (19:12 +0200)
committerGitHub <noreply@github.com>
Tue, 9 Apr 2019 17:12:26 +0000 (19:12 +0200)
Fix time.mktime() error handling on AIX for year before 1970.

Other changes:

* mktime(): rename variable 'buf' to 'tm'.
* _PyTime_localtime():

  * Use "localtime" rather than "ctime" in the error message
    (specific to AIX).
  * Always initialize errno to 0 just in case if localtime_r()
    doesn't set errno on error.
  * On AIX, avoid abs() which is limited to int type.
  * EINVAL constant is now always available.

Misc/NEWS.d/next/Library/2019-04-08-14-41-22.bpo-34373.lEAl_-.rst [new file with mode: 0644]
Modules/timemodule.c
Python/pytime.c

diff --git a/Misc/NEWS.d/next/Library/2019-04-08-14-41-22.bpo-34373.lEAl_-.rst b/Misc/NEWS.d/next/Library/2019-04-08-14-41-22.bpo-34373.lEAl_-.rst
new file mode 100644 (file)
index 0000000..19b38fe
--- /dev/null
@@ -0,0 +1 @@
+Fix :func:`time.mktime` error handling on AIX for year before 1970.
index 7c01cefa4d3cd0f446da05be5d73849c4542881f..724a064f5cebb0d0054011617819aeea43d1f8ca 100644 (file)
@@ -990,60 +990,68 @@ not present, current time as returned by localtime() is used.");
 
 #ifdef HAVE_MKTIME
 static PyObject *
-time_mktime(PyObject *self, PyObject *tup)
+time_mktime(PyObject *self, PyObject *tm_tuple)
 {
-    struct tm buf;
+    struct tm tm;
     time_t tt;
-#ifdef _AIX
-    time_t clk;
-    int     year = buf.tm_year;
-    int     delta_days = 0;
-#endif
 
-    if (!gettmarg(tup, &buf,
+    if (!gettmarg(tm_tuple, &tm,
                   "iiiiiiiii;mktime(): illegal time tuple argument"))
     {
         return NULL;
     }
-#ifndef _AIX
-    buf.tm_wday = -1;  /* sentinel; original value ignored */
-    tt = mktime(&buf);
-#else
-    /* year < 1902 or year > 2037 */
-    if ((buf.tm_year < 2) || (buf.tm_year > 137))  {
-        /* Issue #19748: On AIX, mktime() doesn't report overflow error for
-         * timestamp < -2^31 or timestamp > 2**31-1. */
+
+#ifdef _AIX
+    /* bpo-19748: AIX mktime() valid range is 00:00:00 UTC, January 1, 1970
+       to 03:14:07 UTC, January 19, 2038. Thanks to the workaround below,
+       it is possible to support years in range [1902; 2037] */
+    if (tm.tm_year < 2 || tm.tm_year > 137) {
+        /* bpo-19748: On AIX, mktime() does not report overflow error
+           for timestamp < -2^31 or timestamp > 2**31-1. */
         PyErr_SetString(PyExc_OverflowError,
                         "mktime argument out of range");
         return NULL;
     }
-    year = buf.tm_year;
-    /* year < 1970 - adjust buf.tm_year into legal range */
-    while (buf.tm_year < 70) {
-        buf.tm_year += 4;
+
+    /* bpo-34373: AIX mktime() has an integer overflow for years in range
+       [1902; 1969]. Workaround the issue by using a year greater or equal than
+       1970 (tm_year >= 70): mktime() behaves correctly in that case
+       (ex: properly report errors). tm_year and tm_wday are adjusted after
+       mktime() call. */
+    int orig_tm_year = tm.tm_year;
+    int delta_days = 0;
+    while (tm.tm_year < 70) {
+        /* Use 4 years to account properly leap years */
+        tm.tm_year += 4;
         delta_days -= (366 + (365 * 3));
     }
+#endif
 
-    buf.tm_wday = -1;
-    clk = mktime(&buf);
-    buf.tm_year = year;
-
-    if ((buf.tm_wday != -1) && delta_days)
-        buf.tm_wday = (buf.tm_wday + delta_days) % 7;
+    tm.tm_wday = -1;  /* sentinel; original value ignored */
+    tt = mktime(&tm);
 
-    tt = clk + (delta_days * (24 * 3600));
-#endif
     /* Return value of -1 does not necessarily mean an error, but tm_wday
      * cannot remain set to -1 if mktime succeeded. */
     if (tt == (time_t)(-1)
         /* Return value of -1 does not necessarily mean an error, but
          * tm_wday cannot remain set to -1 if mktime succeeded. */
-        && buf.tm_wday == -1)
+        && tm.tm_wday == -1)
     {
         PyErr_SetString(PyExc_OverflowError,
                         "mktime argument out of range");
         return NULL;
     }
+
+#ifdef _AIX
+    if (delta_days != 0) {
+        tm.tm_year = orig_tm_year;
+        if (tm.tm_wday != -1) {
+            tm.tm_wday = (tm.tm_wday + delta_days) % 7;
+        }
+        tt += delta_days * (24 * 3600);
+    }
+#endif
+
     return PyFloat_FromDouble((double)tt);
 }
 
index 68c49a86da25b689c66d325a3b88089a54b67d4a..9ff300699f04af06bb3c522cdad2c24680374e9b 100644 (file)
@@ -1062,26 +1062,23 @@ _PyTime_localtime(time_t t, struct tm *tm)
     }
     return 0;
 #else /* !MS_WINDOWS */
+
 #ifdef _AIX
-    /* AIX does not return NULL on an error
-       so test ranges - asif!
-       (1902-01-01, -2145916800.0)
-       (2038-01-01,  2145916800.0) */
-    if (abs(t) > (time_t) 2145916800) {
-#ifdef EINVAL
+    /* bpo-34373: AIX does not return NULL if t is too small or too large */
+    if (t < -2145916800 /* 1902-01-01 */
+       || t > 2145916800 /* 2038-01-01 */) {
         errno = EINVAL;
-#endif
         PyErr_SetString(PyExc_OverflowError,
-                        "ctime argument out of range");
+                        "localtime argument out of range");
         return -1;
     }
 #endif
+
+    errno = 0;
     if (localtime_r(&t, tm) == NULL) {
-#ifdef EINVAL
         if (errno == 0) {
             errno = EINVAL;
         }
-#endif
         PyErr_SetFromErrno(PyExc_OSError);
         return -1;
     }