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
|
#ifdef HAVE_MKTIME
|
||||||
static PyObject *
|
static PyObject *
|
||||||
time_mktime(PyObject *self, PyObject *tup)
|
time_mktime(PyObject *self, PyObject *tm_tuple)
|
||||||
{
|
{
|
||||||
struct tm buf;
|
struct tm tm;
|
||||||
time_t tt;
|
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"))
|
"iiiiiiiii;mktime(): illegal time tuple argument"))
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#ifndef _AIX
|
|
||||||
buf.tm_wday = -1; /* sentinel; original value ignored */
|
#ifdef _AIX
|
||||||
tt = mktime(&buf);
|
/* bpo-19748: AIX mktime() valid range is 00:00:00 UTC, January 1, 1970
|
||||||
#else
|
to 03:14:07 UTC, January 19, 2038. Thanks to the workaround below,
|
||||||
/* year < 1902 or year > 2037 */
|
it is possible to support years in range [1902; 2037] */
|
||||||
if ((buf.tm_year < 2) || (buf.tm_year > 137)) {
|
if (tm.tm_year < 2 || tm.tm_year > 137) {
|
||||||
/* Issue #19748: On AIX, mktime() doesn't report overflow error for
|
/* bpo-19748: On AIX, mktime() does not report overflow error
|
||||||
* timestamp < -2^31 or timestamp > 2**31-1. */
|
for timestamp < -2^31 or timestamp > 2**31-1. */
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
"mktime argument out of range");
|
"mktime argument out of range");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
year = buf.tm_year;
|
|
||||||
/* year < 1970 - adjust buf.tm_year into legal range */
|
/* bpo-34373: AIX mktime() has an integer overflow for years in range
|
||||||
while (buf.tm_year < 70) {
|
[1902; 1969]. Workaround the issue by using a year greater or equal than
|
||||||
buf.tm_year += 4;
|
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));
|
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
|
#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
|
/* Return value of -1 does not necessarily mean an error, but tm_wday
|
||||||
* cannot remain set to -1 if mktime succeeded. */
|
* cannot remain set to -1 if mktime succeeded. */
|
||||||
if (tt == (time_t)(-1)
|
if (tt == (time_t)(-1)
|
||||||
/* Return value of -1 does not necessarily mean an error, but
|
/* Return value of -1 does not necessarily mean an error, but
|
||||||
* tm_wday cannot remain set to -1 if mktime succeeded. */
|
* tm_wday cannot remain set to -1 if mktime succeeded. */
|
||||||
&& buf.tm_wday == -1)
|
&& tm.tm_wday == -1)
|
||||||
{
|
{
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
"mktime argument out of range");
|
"mktime argument out of range");
|
||||||
return NULL;
|
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);
|
return PyFloat_FromDouble((double)tt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1062,26 +1062,23 @@ _PyTime_localtime(time_t t, struct tm *tm)
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
#else /* !MS_WINDOWS */
|
#else /* !MS_WINDOWS */
|
||||||
|
|
||||||
#ifdef _AIX
|
#ifdef _AIX
|
||||||
/* AIX does not return NULL on an error
|
/* bpo-34373: AIX does not return NULL if t is too small or too large */
|
||||||
so test ranges - asif!
|
if (t < -2145916800 /* 1902-01-01 */
|
||||||
(1902-01-01, -2145916800.0)
|
|| t > 2145916800 /* 2038-01-01 */) {
|
||||||
(2038-01-01, 2145916800.0) */
|
|
||||||
if (abs(t) > (time_t) 2145916800) {
|
|
||||||
#ifdef EINVAL
|
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
#endif
|
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
"ctime argument out of range");
|
"localtime argument out of range");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
if (localtime_r(&t, tm) == NULL) {
|
if (localtime_r(&t, tm) == NULL) {
|
||||||
#ifdef EINVAL
|
|
||||||
if (errno == 0) {
|
if (errno == 0) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue