Closes #25283: Make tm_gmtoff and tm_zone available on all platforms.

This commit is contained in:
Alexander Belopolsky 2016-09-11 22:55:16 -04:00
parent a3222b8424
commit 18f3a9b93c
3 changed files with 89 additions and 37 deletions

View File

@ -83,6 +83,10 @@ An explanation of some terminology and conventions is in order.
and :attr:`tm_zone` attributes when platform supports corresponding
``struct tm`` members.
.. versionchanged:: 3.6
The :class:`struct_time` attributes :attr:`tm_gmtoff` and :attr:`tm_zone`
are now available on all platforms.
* Use the following functions to convert between time representations:
+-------------------------+-------------------------+-------------------------+
@ -566,10 +570,6 @@ The module defines the following functions and data items:
:class:`struct_time`, or having elements of the wrong type, a
:exc:`TypeError` is raised.
.. versionchanged:: 3.3
:attr:`tm_gmtoff` and :attr:`tm_zone` attributes are available on platforms
with C library supporting the corresponding fields in ``struct tm``.
.. function:: time()
Return the time in seconds since the epoch as a floating point number.

View File

@ -143,6 +143,10 @@ Core and Builtins
Library
-------
- Issue #25283: Attributes tm_gmtoff and tm_zone are now available on
all platforms in the return values of time.localtime() and
time.gmtime().
- Issue #24454: Regular expression match object groups are now
accessible using __getitem__. "mo[x]" is equivalent to
"mo.group(x)".

View File

@ -250,10 +250,8 @@ static PyStructSequence_Field struct_time_type_fields[] = {
{"tm_wday", "day of week, range [0, 6], Monday is 0"},
{"tm_yday", "day of year, range [1, 366]"},
{"tm_isdst", "1 if summer time is in effect, 0 if not, and -1 if unknown"},
#ifdef HAVE_STRUCT_TM_TM_ZONE
{"tm_zone", "abbreviation of timezone name"},
{"tm_gmtoff", "offset from UTC in seconds"},
#endif /* HAVE_STRUCT_TM_TM_ZONE */
{0}
};
@ -275,7 +273,11 @@ static PyTypeObject StructTimeType;
static PyObject *
tmtotuple(struct tm *p)
tmtotuple(struct tm *p
#ifndef HAVE_STRUCT_TM_TM_ZONE
, const char *zone, int gmtoff
#endif
)
{
PyObject *v = PyStructSequence_New(&StructTimeType);
if (v == NULL)
@ -296,6 +298,10 @@ tmtotuple(struct tm *p)
PyStructSequence_SET_ITEM(v, 9,
PyUnicode_DecodeLocale(p->tm_zone, "surrogateescape"));
SET(10, p->tm_gmtoff);
#else
PyStructSequence_SET_ITEM(v, 9,
PyUnicode_DecodeLocale(zone, "surrogateescape"));
SET(10, gmtoff);
#endif /* HAVE_STRUCT_TM_TM_ZONE */
#undef SET
if (PyErr_Occurred()) {
@ -348,9 +354,26 @@ time_gmtime(PyObject *self, PyObject *args)
return PyErr_SetFromErrno(PyExc_OSError);
}
buf = *local;
#ifdef HAVE_STRUCT_TM_TM_ZONE
return tmtotuple(&buf);
#else
return tmtotuple(&buf, "UTC", 0);
#endif
}
#ifndef HAVE_TIMEGM
static time_t
timegm(struct tm *p)
{
/* XXX: the following implementation will not work for tm_year < 1970.
but it is likely that platforms that don't have timegm do not support
negative timestamps anyways. */
return p->tm_sec + p->tm_min*60 + p->tm_hour*3600 + p->tm_yday*86400 +
(p->tm_year-70)*31536000 + ((p->tm_year-69)/4)*86400 -
((p->tm_year-1)/100)*86400 + ((p->tm_year+299)/400)*86400;
}
#endif
PyDoc_STRVAR(gmtime_doc,
"gmtime([seconds]) -> (tm_year, tm_mon, tm_mday, tm_hour, tm_min,\n\
tm_sec, tm_wday, tm_yday, tm_isdst)\n\
@ -391,7 +414,18 @@ time_localtime(PyObject *self, PyObject *args)
return NULL;
if (pylocaltime(&when, &buf) == -1)
return NULL;
#ifdef HAVE_STRUCT_TM_TM_ZONE
return tmtotuple(&buf);
#else
{
struct tm local = buf;
char zone[100];
int gmtoff;
strftime(zone, sizeof(buf), "%Z", &buf);
gmtoff = timegm(&buf) - when;
return tmtotuple(&local, zone, gmtoff);
}
#endif
}
PyDoc_STRVAR(localtime_doc,
@ -1145,6 +1179,27 @@ PyDoc_STRVAR(get_clock_info_doc,
\n\
Get information of the specified clock.");
static void
get_zone(char *zone, int n, struct tm *p)
{
#ifdef HAVE_STRUCT_TM_TM_ZONE
strncpy(zone, p->tm_zone ? p->tm_zone : " ", n);
#else
tzset();
strftime(zone, n, "%Z", p);
#endif
}
static int
get_gmtoff(time_t t, struct tm *p)
{
#ifdef HAVE_STRUCT_TM_TM_ZONE
return p->tm_gmtoff;
#else
return timegm(p) - t;
#endif
}
static void
PyInit_timezone(PyObject *m) {
/* This code moved from PyInit_time wholesale to allow calling it from
@ -1177,7 +1232,6 @@ PyInit_timezone(PyObject *m) {
otz1 = PyUnicode_DecodeLocale(tzname[1], "surrogateescape");
PyModule_AddObject(m, "tzname", Py_BuildValue("(NN)", otz0, otz1));
#else /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/
#ifdef HAVE_STRUCT_TM_TM_ZONE
{
#define YEAR ((time_t)((365 * 24 + 6) * 3600))
time_t t;
@ -1186,13 +1240,13 @@ PyInit_timezone(PyObject *m) {
char janname[10], julyname[10];
t = (time((time_t *)0) / YEAR) * YEAR;
p = localtime(&t);
janzone = -p->tm_gmtoff;
strncpy(janname, p->tm_zone ? p->tm_zone : " ", 9);
get_zone(janname, 9, p);
janzone = -get_gmtoff(t, p);
janname[9] = '\0';
t += YEAR/2;
p = localtime(&t);
julyzone = -p->tm_gmtoff;
strncpy(julyname, p->tm_zone ? p->tm_zone : " ", 9);
get_zone(julyname, 9, p);
julyzone = -get_gmtoff(t, p);
julyname[9] = '\0';
if( janzone < julyzone ) {
@ -1214,8 +1268,6 @@ PyInit_timezone(PyObject *m) {
janname, julyname));
}
}
#else
#endif /* HAVE_STRUCT_TM_TM_ZONE */
#ifdef __CYGWIN__
tzset();
PyModule_AddIntConstant(m, "timezone", _timezone);
@ -1225,25 +1277,6 @@ PyInit_timezone(PyObject *m) {
Py_BuildValue("(zz)", _tzname[0], _tzname[1]));
#endif /* __CYGWIN__ */
#endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/
#if defined(HAVE_CLOCK_GETTIME)
PyModule_AddIntMacro(m, CLOCK_REALTIME);
#ifdef CLOCK_MONOTONIC
PyModule_AddIntMacro(m, CLOCK_MONOTONIC);
#endif
#ifdef CLOCK_MONOTONIC_RAW
PyModule_AddIntMacro(m, CLOCK_MONOTONIC_RAW);
#endif
#ifdef CLOCK_HIGHRES
PyModule_AddIntMacro(m, CLOCK_HIGHRES);
#endif
#ifdef CLOCK_PROCESS_CPUTIME_ID
PyModule_AddIntMacro(m, CLOCK_PROCESS_CPUTIME_ID);
#endif
#ifdef CLOCK_THREAD_CPUTIME_ID
PyModule_AddIntMacro(m, CLOCK_THREAD_CPUTIME_ID);
#endif
#endif /* HAVE_CLOCK_GETTIME */
}
@ -1350,17 +1383,32 @@ PyInit_time(void)
/* Set, or reset, module variables like time.timezone */
PyInit_timezone(m);
#if defined(HAVE_CLOCK_GETTIME)
PyModule_AddIntMacro(m, CLOCK_REALTIME);
#ifdef CLOCK_MONOTONIC
PyModule_AddIntMacro(m, CLOCK_MONOTONIC);
#endif
#ifdef CLOCK_MONOTONIC_RAW
PyModule_AddIntMacro(m, CLOCK_MONOTONIC_RAW);
#endif
#ifdef CLOCK_HIGHRES
PyModule_AddIntMacro(m, CLOCK_HIGHRES);
#endif
#ifdef CLOCK_PROCESS_CPUTIME_ID
PyModule_AddIntMacro(m, CLOCK_PROCESS_CPUTIME_ID);
#endif
#ifdef CLOCK_THREAD_CPUTIME_ID
PyModule_AddIntMacro(m, CLOCK_THREAD_CPUTIME_ID);
#endif
#endif /* HAVE_CLOCK_GETTIME */
if (!initialized) {
if (PyStructSequence_InitType2(&StructTimeType,
&struct_time_type_desc) < 0)
return NULL;
}
Py_INCREF(&StructTimeType);
#ifdef HAVE_STRUCT_TM_TM_ZONE
PyModule_AddIntConstant(m, "_STRUCT_TM_ITEMS", 11);
#else
PyModule_AddIntConstant(m, "_STRUCT_TM_ITEMS", 9);
#endif
PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType);
initialized = 1;
return m;