Skip to content

Commit 3e7a3cb

Browse files
committedSep 28, 2016
Issue python#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.
1 parent e5ccf3d commit 3e7a3cb

File tree

5 files changed

+91
-92
lines changed

5 files changed

+91
-92
lines changed
 

‎Include/pytime.h

+8
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,14 @@ PyAPI_FUNC(int) _PyTime_GetMonotonicClockWithInfo(
184184
Return 0 on success, raise an exception and return -1 on error. */
185185
PyAPI_FUNC(int) _PyTime_Init(void);
186186

187+
/* Converts a timestamp to the Gregorian time, using the local time zone.
188+
Return 0 on success, raise an exception and return -1 on error. */
189+
PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm);
190+
191+
/* Converts a timestamp to the Gregorian time, assuming UTC.
192+
Return 0 on success, raise an exception and return -1 on error. */
193+
PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
194+
187195
#ifdef __cplusplus
188196
}
189197
#endif

‎Misc/ACKS

+1
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,7 @@ Michael Schneider
13401340
Peter Schneider-Kamp
13411341
Arvin Schnell
13421342
Nofar Schnider
1343+
Ed Schouten
13431344
Scott Schram
13441345
Robin Schreiber
13451346
Chad J. Schroeder

‎Modules/_datetimemodule.c

+16-51
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,6 @@
99

1010
#ifdef MS_WINDOWS
1111
# include <winsock2.h> /* struct timeval */
12-
static struct tm *localtime_r(const time_t *timep, struct tm *result)
13-
{
14-
if (localtime_s(result, timep) == 0)
15-
return result;
16-
return NULL;
17-
}
18-
static struct tm *gmtime_r(const time_t *timep, struct tm *result)
19-
{
20-
if (gmtime_s(result, timep) == 0)
21-
return result;
22-
return NULL;
23-
}
2412
#endif
2513

2614
/* Differentiate between building the core module and building extension
@@ -2529,15 +2517,8 @@ date_local_from_object(PyObject *cls, PyObject *obj)
25292517
if (_PyTime_ObjectToTime_t(obj, &t, _PyTime_ROUND_FLOOR) == -1)
25302518
return NULL;
25312519

2532-
if (localtime_r(&t, &tm) == NULL) {
2533-
/* unconvertible time */
2534-
#ifdef EINVAL
2535-
if (errno == 0)
2536-
errno = EINVAL;
2537-
#endif
2538-
PyErr_SetFromErrno(PyExc_OSError);
2520+
if (_PyTime_localtime(t, &tm) != 0)
25392521
return NULL;
2540-
}
25412522

25422523
return PyObject_CallFunction(cls, "iii",
25432524
tm.tm_year + 1900,
@@ -4199,8 +4180,9 @@ datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw)
41994180
return self;
42004181
}
42014182

4202-
/* TM_FUNC is the shared type of localtime_r() and gmtime_r(). */
4203-
typedef struct tm *(*TM_FUNC)(const time_t *timer, struct tm*);
4183+
/* TM_FUNC is the shared type of _PyTime_localtime() and
4184+
* _PyTime_gmtime(). */
4185+
typedef int (*TM_FUNC)(time_t timer, struct tm*);
42044186

42054187
/* As of version 2015f max fold in IANA database is
42064188
* 23 hours at 1969-09-30 13:00:00 in Kwajalein. */
@@ -4229,10 +4211,8 @@ local(long long u)
42294211
return -1;
42304212
}
42314213
/* XXX: add bounds checking */
4232-
if (localtime_r(&t, &local_time) == NULL) {
4233-
PyErr_SetFromErrno(PyExc_OSError);
4214+
if (_PyTime_localtime(t, &local_time) != 0)
42344215
return -1;
4235-
}
42364216
return utc_to_seconds(local_time.tm_year + 1900,
42374217
local_time.tm_mon + 1,
42384218
local_time.tm_mday,
@@ -4252,13 +4232,8 @@ datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us,
42524232
struct tm tm;
42534233
int year, month, day, hour, minute, second, fold = 0;
42544234

4255-
if (f(&timet, &tm) == NULL) {
4256-
#ifdef EINVAL
4257-
if (errno == 0)
4258-
errno = EINVAL;
4259-
#endif
4260-
return PyErr_SetFromErrno(PyExc_OSError);
4261-
}
4235+
if (f(timet, &tm) != 0)
4236+
return NULL;
42624237

42634238
year = tm.tm_year + 1900;
42644239
month = tm.tm_mon + 1;
@@ -4273,7 +4248,7 @@ datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us,
42734248
*/
42744249
second = Py_MIN(59, tm.tm_sec);
42754250

4276-
if (tzinfo == Py_None && f == localtime_r) {
4251+
if (tzinfo == Py_None && f == _PyTime_localtime) {
42774252
long long probe_seconds, result_seconds, transition;
42784253

42794254
result_seconds = utc_to_seconds(year, month, day,
@@ -4361,7 +4336,8 @@ datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz)
43614336
return NULL;
43624337

43634338
self = datetime_best_possible((PyObject *)type,
4364-
tz == Py_None ? localtime_r : gmtime_r,
4339+
tz == Py_None ? _PyTime_localtime :
4340+
_PyTime_gmtime,
43654341
tz);
43664342
if (self != NULL && tz != Py_None) {
43674343
/* Convert UTC to tzinfo's zone. */
@@ -4376,7 +4352,7 @@ datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz)
43764352
static PyObject *
43774353
datetime_utcnow(PyObject *cls, PyObject *dummy)
43784354
{
4379-
return datetime_best_possible(cls, gmtime_r, Py_None);
4355+
return datetime_best_possible(cls, _PyTime_gmtime, Py_None);
43804356
}
43814357

43824358
/* Return new local datetime from timestamp (Python timestamp -- a double). */
@@ -4395,7 +4371,8 @@ datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw)
43954371
return NULL;
43964372

43974373
self = datetime_from_timestamp(cls,
4398-
tzinfo == Py_None ? localtime_r : gmtime_r,
4374+
tzinfo == Py_None ? _PyTime_localtime :
4375+
_PyTime_gmtime,
43994376
timestamp,
44004377
tzinfo);
44014378
if (self != NULL && tzinfo != Py_None) {
@@ -4413,7 +4390,7 @@ datetime_utcfromtimestamp(PyObject *cls, PyObject *args)
44134390
PyObject *result = NULL;
44144391

44154392
if (PyArg_ParseTuple(args, "O:utcfromtimestamp", &timestamp))
4416-
result = datetime_from_timestamp(cls, gmtime_r, timestamp,
4393+
result = datetime_from_timestamp(cls, _PyTime_gmtime, timestamp,
44174394
Py_None);
44184395
return result;
44194396
}
@@ -5040,14 +5017,8 @@ local_timezone_from_timestamp(time_t timestamp)
50405017
PyObject *nameo = NULL;
50415018
const char *zone = NULL;
50425019

5043-
if (localtime_r(&timestamp, &local_time_tm) == NULL) {
5044-
#ifdef EINVAL
5045-
if (errno == 0)
5046-
errno = EINVAL;
5047-
#endif
5048-
PyErr_SetFromErrno(PyExc_OSError);
5020+
if (_PyTime_localtime(timestamp, &local_time_tm) != 0)
50495021
return NULL;
5050-
}
50515022
#ifdef HAVE_STRUCT_TM_TM_ZONE
50525023
zone = local_time_tm.tm_zone;
50535024
delta = new_delta(0, local_time_tm.tm_gmtoff, 0, 1);
@@ -5067,14 +5038,8 @@ local_timezone_from_timestamp(time_t timestamp)
50675038
if (local_time == NULL) {
50685039
return NULL;
50695040
}
5070-
if (gmtime_r(&timestamp, &utc_time_tm) == NULL) {
5071-
#ifdef EINVAL
5072-
if (errno == 0)
5073-
errno = EINVAL;
5074-
#endif
5075-
PyErr_SetFromErrno(PyExc_OSError);
5041+
if (_PyTime_gmtime(timestamp, &utc_time_tm) != 0)
50765042
return NULL;
5077-
}
50785043
utc_time = new_datetime(utc_time_tm.tm_year + 1900,
50795044
utc_time_tm.tm_mon + 1,
50805045
utc_time_tm.tm_mday,

‎Modules/timemodule.c

+14-41
Original file line numberDiff line numberDiff line change
@@ -343,21 +343,14 @@ static PyObject *
343343
time_gmtime(PyObject *self, PyObject *args)
344344
{
345345
time_t when;
346-
struct tm buf, *local;
346+
struct tm buf;
347347

348348
if (!parse_time_t_args(args, "|O:gmtime", &when))
349349
return NULL;
350350

351351
errno = 0;
352-
local = gmtime(&when);
353-
if (local == NULL) {
354-
#ifdef EINVAL
355-
if (errno == 0)
356-
errno = EINVAL;
357-
#endif
358-
return PyErr_SetFromErrno(PyExc_OSError);
359-
}
360-
buf = *local;
352+
if (_PyTime_gmtime(when, &buf) != 0)
353+
return NULL;
361354
#ifdef HAVE_STRUCT_TM_TM_ZONE
362355
return tmtotuple(&buf);
363356
#else
@@ -388,26 +381,6 @@ GMT). When 'seconds' is not passed in, convert the current time instead.\n\
388381
If the platform supports the tm_gmtoff and tm_zone, they are available as\n\
389382
attributes only.");
390383

391-
static int
392-
pylocaltime(time_t *timep, struct tm *result)
393-
{
394-
struct tm *local;
395-
396-
assert (timep != NULL);
397-
local = localtime(timep);
398-
if (local == NULL) {
399-
/* unconvertible time */
400-
#ifdef EINVAL
401-
if (errno == 0)
402-
errno = EINVAL;
403-
#endif
404-
PyErr_SetFromErrno(PyExc_OSError);
405-
return -1;
406-
}
407-
*result = *local;
408-
return 0;
409-
}
410-
411384
static PyObject *
412385
time_localtime(PyObject *self, PyObject *args)
413386
{
@@ -416,7 +389,7 @@ time_localtime(PyObject *self, PyObject *args)
416389

417390
if (!parse_time_t_args(args, "|O:localtime", &when))
418391
return NULL;
419-
if (pylocaltime(&when, &buf) == -1)
392+
if (_PyTime_localtime(when, &buf) != 0)
420393
return NULL;
421394
#ifdef HAVE_STRUCT_TM_TM_ZONE
422395
return tmtotuple(&buf);
@@ -611,7 +584,7 @@ time_strftime(PyObject *self, PyObject *args)
611584

612585
if (tup == NULL) {
613586
time_t tt = time(NULL);
614-
if (pylocaltime(&tt, &buf) == -1)
587+
if (_PyTime_localtime(tt, &buf) != 0)
615588
return NULL;
616589
}
617590
else if (!gettmarg(tup, &buf) || !checktm(&buf))
@@ -796,7 +769,7 @@ time_asctime(PyObject *self, PyObject *args)
796769
return NULL;
797770
if (tup == NULL) {
798771
time_t tt = time(NULL);
799-
if (pylocaltime(&tt, &buf) == -1)
772+
if (_PyTime_localtime(tt, &buf) != 0)
800773
return NULL;
801774

802775
} else if (!gettmarg(tup, &buf) || !checktm(&buf))
@@ -818,7 +791,7 @@ time_ctime(PyObject *self, PyObject *args)
818791
struct tm buf;
819792
if (!parse_time_t_args(args, "|O:ctime", &tt))
820793
return NULL;
821-
if (pylocaltime(&tt, &buf) == -1)
794+
if (_PyTime_localtime(tt, &buf) != 0)
822795
return NULL;
823796
return _asctime(&buf);
824797
}
@@ -1239,18 +1212,18 @@ PyInit_timezone(PyObject *m) {
12391212
{
12401213
#define YEAR ((time_t)((365 * 24 + 6) * 3600))
12411214
time_t t;
1242-
struct tm *p;
1215+
struct tm p;
12431216
long janzone, julyzone;
12441217
char janname[10], julyname[10];
12451218
t = (time((time_t *)0) / YEAR) * YEAR;
1246-
p = localtime(&t);
1247-
get_zone(janname, 9, p);
1248-
janzone = -get_gmtoff(t, p);
1219+
_PyTime_localtime(t, &p);
1220+
get_zone(janname, 9, &p);
1221+
janzone = -get_gmtoff(t, &p);
12491222
janname[9] = '\0';
12501223
t += YEAR/2;
1251-
p = localtime(&t);
1252-
get_zone(julyname, 9, p);
1253-
julyzone = -get_gmtoff(t, p);
1224+
_PyTime_localtime(t, &p);
1225+
get_zone(julyname, 9, &p);
1226+
julyzone = -get_gmtoff(t, &p);
12541227
julyname[9] = '\0';
12551228

12561229
if( janzone < julyzone ) {

‎Python/pytime.c

+52
Original file line numberDiff line numberDiff line change
@@ -766,3 +766,55 @@ _PyTime_Init(void)
766766

767767
return 0;
768768
}
769+
770+
int
771+
_PyTime_localtime(time_t t, struct tm *tm)
772+
{
773+
#ifdef MS_WINDOWS
774+
int error;
775+
776+
error = localtime_s(tm, &t);
777+
if (error != 0) {
778+
errno = error;
779+
PyErr_SetFromErrno(PyExc_OSError);
780+
return -1;
781+
}
782+
return 0;
783+
#else /* !MS_WINDOWS */
784+
if (localtime_r(&t, tm) == NULL) {
785+
#ifdef EINVAL
786+
if (errno == 0)
787+
errno = EINVAL;
788+
#endif
789+
PyErr_SetFromErrno(PyExc_OSError);
790+
return -1;
791+
}
792+
return 0;
793+
#endif /* MS_WINDOWS */
794+
}
795+
796+
int
797+
_PyTime_gmtime(time_t t, struct tm *tm)
798+
{
799+
#ifdef MS_WINDOWS
800+
int error;
801+
802+
error = gmtime_s(tm, &t);
803+
if (error != 0) {
804+
errno = error;
805+
PyErr_SetFromErrno(PyExc_OSError);
806+
return -1;
807+
}
808+
return 0;
809+
#else /* !MS_WINDOWS */
810+
if (gmtime_r(&t, tm) == NULL) {
811+
#ifdef EINVAL
812+
if (errno == 0)
813+
errno = EINVAL;
814+
#endif
815+
PyErr_SetFromErrno(PyExc_OSError);
816+
return -1;
817+
}
818+
return 0;
819+
#endif /* MS_WINDOWS */
820+
}

0 commit comments

Comments
 (0)
Please sign in to comment.