From 3e7a3cb903258321a17265a1c01976f88e7b72fc Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Wed, 28 Sep 2016 17:31:35 -0400 Subject: [PATCH] Issue #28148: Stop using localtime() and gmtime() in the time module. Introduced platform independent _PyTime_localtime API that is similar to POSIX localtime_r, but available on all platforms. Patch by Ed Schouten. --- Include/pytime.h | 8 +++++ Misc/ACKS | 1 + Modules/_datetimemodule.c | 67 ++++++++++----------------------------- Modules/timemodule.c | 55 ++++++++------------------------ Python/pytime.c | 52 ++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 92 deletions(-) diff --git a/Include/pytime.h b/Include/pytime.h index 595fafc5c0e..87ac7fcbbad 100644 --- a/Include/pytime.h +++ b/Include/pytime.h @@ -184,6 +184,14 @@ PyAPI_FUNC(int) _PyTime_GetMonotonicClockWithInfo( Return 0 on success, raise an exception and return -1 on error. */ PyAPI_FUNC(int) _PyTime_Init(void); +/* Converts a timestamp to the Gregorian time, using the local time zone. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm); + +/* Converts a timestamp to the Gregorian time, assuming UTC. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm); + #ifdef __cplusplus } #endif diff --git a/Misc/ACKS b/Misc/ACKS index 34d2fd344ae..f55583b4677 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1340,6 +1340,7 @@ Michael Schneider Peter Schneider-Kamp Arvin Schnell Nofar Schnider +Ed Schouten Scott Schram Robin Schreiber Chad J. Schroeder diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 1a63a019915..5ddf6504c57 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -9,18 +9,6 @@ #ifdef MS_WINDOWS # include /* struct timeval */ -static struct tm *localtime_r(const time_t *timep, struct tm *result) -{ - if (localtime_s(result, timep) == 0) - return result; - return NULL; -} -static struct tm *gmtime_r(const time_t *timep, struct tm *result) -{ - if (gmtime_s(result, timep) == 0) - return result; - return NULL; -} #endif /* Differentiate between building the core module and building extension @@ -2529,15 +2517,8 @@ date_local_from_object(PyObject *cls, PyObject *obj) if (_PyTime_ObjectToTime_t(obj, &t, _PyTime_ROUND_FLOOR) == -1) return NULL; - if (localtime_r(&t, &tm) == NULL) { - /* unconvertible time */ -#ifdef EINVAL - if (errno == 0) - errno = EINVAL; -#endif - PyErr_SetFromErrno(PyExc_OSError); + if (_PyTime_localtime(t, &tm) != 0) return NULL; - } return PyObject_CallFunction(cls, "iii", tm.tm_year + 1900, @@ -4199,8 +4180,9 @@ datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) return self; } -/* TM_FUNC is the shared type of localtime_r() and gmtime_r(). */ -typedef struct tm *(*TM_FUNC)(const time_t *timer, struct tm*); +/* TM_FUNC is the shared type of _PyTime_localtime() and + * _PyTime_gmtime(). */ +typedef int (*TM_FUNC)(time_t timer, struct tm*); /* As of version 2015f max fold in IANA database is * 23 hours at 1969-09-30 13:00:00 in Kwajalein. */ @@ -4229,10 +4211,8 @@ local(long long u) return -1; } /* XXX: add bounds checking */ - if (localtime_r(&t, &local_time) == NULL) { - PyErr_SetFromErrno(PyExc_OSError); + if (_PyTime_localtime(t, &local_time) != 0) return -1; - } return utc_to_seconds(local_time.tm_year + 1900, local_time.tm_mon + 1, local_time.tm_mday, @@ -4252,13 +4232,8 @@ datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us, struct tm tm; int year, month, day, hour, minute, second, fold = 0; - if (f(&timet, &tm) == NULL) { -#ifdef EINVAL - if (errno == 0) - errno = EINVAL; -#endif - return PyErr_SetFromErrno(PyExc_OSError); - } + if (f(timet, &tm) != 0) + return NULL; year = tm.tm_year + 1900; month = tm.tm_mon + 1; @@ -4273,7 +4248,7 @@ datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us, */ second = Py_MIN(59, tm.tm_sec); - if (tzinfo == Py_None && f == localtime_r) { + if (tzinfo == Py_None && f == _PyTime_localtime) { long long probe_seconds, result_seconds, transition; result_seconds = utc_to_seconds(year, month, day, @@ -4361,7 +4336,8 @@ datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz) return NULL; self = datetime_best_possible((PyObject *)type, - tz == Py_None ? localtime_r : gmtime_r, + tz == Py_None ? _PyTime_localtime : + _PyTime_gmtime, tz); if (self != NULL && tz != Py_None) { /* Convert UTC to tzinfo's zone. */ @@ -4376,7 +4352,7 @@ datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz) static PyObject * datetime_utcnow(PyObject *cls, PyObject *dummy) { - return datetime_best_possible(cls, gmtime_r, Py_None); + return datetime_best_possible(cls, _PyTime_gmtime, Py_None); } /* Return new local datetime from timestamp (Python timestamp -- a double). */ @@ -4395,7 +4371,8 @@ datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw) return NULL; self = datetime_from_timestamp(cls, - tzinfo == Py_None ? localtime_r : gmtime_r, + tzinfo == Py_None ? _PyTime_localtime : + _PyTime_gmtime, timestamp, tzinfo); if (self != NULL && tzinfo != Py_None) { @@ -4413,7 +4390,7 @@ datetime_utcfromtimestamp(PyObject *cls, PyObject *args) PyObject *result = NULL; if (PyArg_ParseTuple(args, "O:utcfromtimestamp", ×tamp)) - result = datetime_from_timestamp(cls, gmtime_r, timestamp, + result = datetime_from_timestamp(cls, _PyTime_gmtime, timestamp, Py_None); return result; } @@ -5040,14 +5017,8 @@ local_timezone_from_timestamp(time_t timestamp) PyObject *nameo = NULL; const char *zone = NULL; - if (localtime_r(×tamp, &local_time_tm) == NULL) { -#ifdef EINVAL - if (errno == 0) - errno = EINVAL; -#endif - PyErr_SetFromErrno(PyExc_OSError); + if (_PyTime_localtime(timestamp, &local_time_tm) != 0) return NULL; - } #ifdef HAVE_STRUCT_TM_TM_ZONE zone = local_time_tm.tm_zone; delta = new_delta(0, local_time_tm.tm_gmtoff, 0, 1); @@ -5067,14 +5038,8 @@ local_timezone_from_timestamp(time_t timestamp) if (local_time == NULL) { return NULL; } - if (gmtime_r(×tamp, &utc_time_tm) == NULL) { -#ifdef EINVAL - if (errno == 0) - errno = EINVAL; -#endif - PyErr_SetFromErrno(PyExc_OSError); + if (_PyTime_gmtime(timestamp, &utc_time_tm) != 0) return NULL; - } utc_time = new_datetime(utc_time_tm.tm_year + 1900, utc_time_tm.tm_mon + 1, utc_time_tm.tm_mday, diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 8194fd9a35b..f0708338e84 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -343,21 +343,14 @@ static PyObject * time_gmtime(PyObject *self, PyObject *args) { time_t when; - struct tm buf, *local; + struct tm buf; if (!parse_time_t_args(args, "|O:gmtime", &when)) return NULL; errno = 0; - local = gmtime(&when); - if (local == NULL) { -#ifdef EINVAL - if (errno == 0) - errno = EINVAL; -#endif - return PyErr_SetFromErrno(PyExc_OSError); - } - buf = *local; + if (_PyTime_gmtime(when, &buf) != 0) + return NULL; #ifdef HAVE_STRUCT_TM_TM_ZONE return tmtotuple(&buf); #else @@ -388,26 +381,6 @@ GMT). When 'seconds' is not passed in, convert the current time instead.\n\ If the platform supports the tm_gmtoff and tm_zone, they are available as\n\ attributes only."); -static int -pylocaltime(time_t *timep, struct tm *result) -{ - struct tm *local; - - assert (timep != NULL); - local = localtime(timep); - if (local == NULL) { - /* unconvertible time */ -#ifdef EINVAL - if (errno == 0) - errno = EINVAL; -#endif - PyErr_SetFromErrno(PyExc_OSError); - return -1; - } - *result = *local; - return 0; -} - static PyObject * time_localtime(PyObject *self, PyObject *args) { @@ -416,7 +389,7 @@ time_localtime(PyObject *self, PyObject *args) if (!parse_time_t_args(args, "|O:localtime", &when)) return NULL; - if (pylocaltime(&when, &buf) == -1) + if (_PyTime_localtime(when, &buf) != 0) return NULL; #ifdef HAVE_STRUCT_TM_TM_ZONE return tmtotuple(&buf); @@ -611,7 +584,7 @@ time_strftime(PyObject *self, PyObject *args) if (tup == NULL) { time_t tt = time(NULL); - if (pylocaltime(&tt, &buf) == -1) + if (_PyTime_localtime(tt, &buf) != 0) return NULL; } else if (!gettmarg(tup, &buf) || !checktm(&buf)) @@ -796,7 +769,7 @@ time_asctime(PyObject *self, PyObject *args) return NULL; if (tup == NULL) { time_t tt = time(NULL); - if (pylocaltime(&tt, &buf) == -1) + if (_PyTime_localtime(tt, &buf) != 0) return NULL; } else if (!gettmarg(tup, &buf) || !checktm(&buf)) @@ -818,7 +791,7 @@ time_ctime(PyObject *self, PyObject *args) struct tm buf; if (!parse_time_t_args(args, "|O:ctime", &tt)) return NULL; - if (pylocaltime(&tt, &buf) == -1) + if (_PyTime_localtime(tt, &buf) != 0) return NULL; return _asctime(&buf); } @@ -1239,18 +1212,18 @@ PyInit_timezone(PyObject *m) { { #define YEAR ((time_t)((365 * 24 + 6) * 3600)) time_t t; - struct tm *p; + struct tm p; long janzone, julyzone; char janname[10], julyname[10]; t = (time((time_t *)0) / YEAR) * YEAR; - p = localtime(&t); - get_zone(janname, 9, p); - janzone = -get_gmtoff(t, p); + _PyTime_localtime(t, &p); + get_zone(janname, 9, &p); + janzone = -get_gmtoff(t, &p); janname[9] = '\0'; t += YEAR/2; - p = localtime(&t); - get_zone(julyname, 9, p); - julyzone = -get_gmtoff(t, p); + _PyTime_localtime(t, &p); + get_zone(julyname, 9, &p); + julyzone = -get_gmtoff(t, &p); julyname[9] = '\0'; if( janzone < julyzone ) { diff --git a/Python/pytime.c b/Python/pytime.c index 02ef8ee7d63..3015a6be0b8 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -766,3 +766,55 @@ _PyTime_Init(void) return 0; } + +int +_PyTime_localtime(time_t t, struct tm *tm) +{ +#ifdef MS_WINDOWS + int error; + + error = localtime_s(tm, &t); + if (error != 0) { + errno = error; + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return 0; +#else /* !MS_WINDOWS */ + if (localtime_r(&t, tm) == NULL) { +#ifdef EINVAL + if (errno == 0) + errno = EINVAL; +#endif + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return 0; +#endif /* MS_WINDOWS */ +} + +int +_PyTime_gmtime(time_t t, struct tm *tm) +{ +#ifdef MS_WINDOWS + int error; + + error = gmtime_s(tm, &t); + if (error != 0) { + errno = error; + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return 0; +#else /* !MS_WINDOWS */ + if (gmtime_r(&t, tm) == NULL) { +#ifdef EINVAL + if (errno == 0) + errno = EINVAL; +#endif + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return 0; +#endif /* MS_WINDOWS */ +}