diff options
author | Victor Stinner <vstinner@redhat.com> | 2019-04-09 17:12:26 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-09 17:12:26 (GMT) |
commit | 8709490f48fc27b3dd1a16acb33bea2299c6a575 (patch) | |
tree | 93776ed6abaae382eb6c3ea2cd369fde769e9841 | |
parent | 8abc3f4f91e6b523c761c7a6fa2e3405019803a1 (diff) | |
download | cpython-8709490f48fc27b3dd1a16acb33bea2299c6a575.zip cpython-8709490f48fc27b3dd1a16acb33bea2299c6a575.tar.gz cpython-8709490f48fc27b3dd1a16acb33bea2299c6a575.tar.bz2 |
bpo-34373: Fix time.mktime() on AIX (GH-12726)
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.
-rw-r--r-- | Misc/NEWS.d/next/Library/2019-04-08-14-41-22.bpo-34373.lEAl_-.rst | 1 | ||||
-rw-r--r-- | Modules/timemodule.c | 66 | ||||
-rw-r--r-- | Python/pytime.c | 17 |
3 files changed, 45 insertions, 39 deletions
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 index 0000000..19b38fe --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-04-08-14-41-22.bpo-34373.lEAl_-.rst @@ -0,0 +1 @@ +Fix :func:`time.mktime` error handling on AIX for year before 1970. diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 7c01cef..724a064 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -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); } diff --git a/Python/pytime.c b/Python/pytime.c index 68c49a8..9ff3006 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -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; } |