mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
8abc3f4f91
commit
8709490f48
|
@ -0,0 +1 @@
|
|||
Fix :func:`time.mktime` error handling on AIX for year before 1970.
|
|
@ -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));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
tt = clk + (delta_days * (24 * 3600));
|
||||
#endif
|
||||
|
||||
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)
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue