summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@redhat.com>2019-04-09 17:12:26 (GMT)
committerGitHub <noreply@github.com>2019-04-09 17:12:26 (GMT)
commit8709490f48fc27b3dd1a16acb33bea2299c6a575 (patch)
tree93776ed6abaae382eb6c3ea2cd369fde769e9841
parent8abc3f4f91e6b523c761c7a6fa2e3405019803a1 (diff)
downloadcpython-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_-.rst1
-rw-r--r--Modules/timemodule.c66
-rw-r--r--Python/pytime.c17
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;
}