Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Lib/test/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
52 changes: 38 additions & 14 deletions Modules/timemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
15 changes: 13 additions & 2 deletions Python/pytime.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down