diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 4e31abf4ec8eec7..1008380a287ebfe 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -120,7 +120,13 @@ def test_clock_monotonic(self): def test_pthread_getcpuclockid(self): clk_id = time.pthread_getcpuclockid(threading.get_ident()) self.assertTrue(type(clk_id) is int) - self.assertNotEqual(clk_id, time.CLOCK_THREAD_CPUTIME_ID) + # when in 32-bit mode AIX only returns the predefined constant + if not platform.system() == "AIX": + self.assertNotEqual(clk_id, time.CLOCK_THREAD_CPUTIME_ID) + elif (sys.maxsize.bit_length() > 32): + self.assertNotEqual(clk_id, time.CLOCK_THREAD_CPUTIME_ID) + else: + self.assertEqual(clk_id, time.CLOCK_THREAD_CPUTIME_ID) t1 = time.clock_gettime(clk_id) t2 = time.clock_gettime(clk_id) self.assertLessEqual(t1, t2) 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 000000000000000..af593d4925b7298 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-04-08-14-41-22.bpo-34373.lEAl_-.rst @@ -0,0 +1,2 @@ +Fix :func:`time.mktime` error handling on AIX for year before 1970. +Fix `test_time.test_pthread_getcpuclockid` for AIX in 32-bit mode. diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 4c8e2cb2344bcd2..3cc40767ab440a4 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -960,44 +960,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; - if (!gettmarg(tup, &buf, + + if (!gettmarg(tm_tuple, &tm, "iiiiiiiii;mktime(): illegal time tuple argument")) { return NULL; } + #ifdef _AIX - /* year < 1902 or year > 2037 */ - if (buf.tm_year < 2 || buf.tm_year > 137) { + /* 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) { /* Issue #19748: On AIX, mktime() doesn't report overflow error for * timestamp < -2^31 or timestamp > 2**31-1. */ PyErr_SetString(PyExc_OverflowError, "mktime argument out of range"); return NULL; } -#else - buf.tm_wday = -1; /* sentinel; original value ignored */ + + /* 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 - tt = mktime(&buf); + + tm.tm_wday = -1; /* sentinel; original value ignored */ + tt = mktime(&tm); + /* 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) -#ifndef _AIX /* 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 -#else - /* on AIX, tm_wday is always sets, even on error */ -#endif - ) + && 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 0e9413174195d9b..9ff300699f04af0 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -1062,12 +1062,23 @@ _PyTime_localtime(time_t t, struct tm *tm) } return 0; #else /* !MS_WINDOWS */ + +#ifdef _AIX + /* 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; + PyErr_SetString(PyExc_OverflowError, + "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; }