diff options
Diffstat (limited to 'Modules/timemodule.c')
| -rw-r--r-- | Modules/timemodule.c | 486 |
1 files changed, 214 insertions, 272 deletions
diff --git a/Modules/timemodule.c b/Modules/timemodule.c index faed0e1..3af041a 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -1,24 +1,10 @@ - /* Time module */ #include "Python.h" -#include "structseq.h" -#include "timefuncs.h" +#include "_time.h" #define TZNAME_ENCODING "utf-8" -#ifdef __APPLE__ -#if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_FTIME) - /* - * floattime falls back to ftime when getttimeofday fails because the latter - * might fail on some platforms. This fallback is unwanted on MacOSX because - * that makes it impossible to use a binary build on OSX 10.4 on earlier - * releases of the OS. Therefore claim we don't support ftime. - */ -# undef HAVE_FTIME -#endif -#endif - #include <ctype.h> #ifdef HAVE_SYS_TYPES_H @@ -29,13 +15,6 @@ #include <io.h> #endif -#ifdef HAVE_FTIME -#include <sys/timeb.h> -#if !defined(MS_WINDOWS) && !defined(PYOS_OS2) -extern int ftime(struct timeb *); -#endif /* MS_WINDOWS */ -#endif /* HAVE_FTIME */ - #if defined(__WATCOMC__) && !defined(__QNX__) #include <i86.h> #else @@ -90,32 +69,6 @@ static double floattime(void); /* For Y2K check */ static PyObject *moddict; -/* Exposed in timefuncs.h. */ -time_t -_PyTime_DoubleToTimet(double x) -{ - time_t result; - double diff; - - result = (time_t)x; - /* How much info did we lose? time_t may be an integral or - * floating type, and we don't know which. If it's integral, - * we don't know whether C truncates, rounds, returns the floor, - * etc. If we lost a second or more, the C rounding is - * unreasonable, or the input just doesn't fit in a time_t; - * call it an error regardless. Note that the original cast to - * time_t can cause a C error too, but nothing we can do to - * worm around that. - */ - diff = x - (double)result; - if (diff <= -1.0 || diff >= 1.0) { - PyErr_SetString(PyExc_ValueError, - "timestamp out of range for platform time_t"); - result = (time_t)-1; - } - return result; -} - static PyObject * time_time(PyObject *self, PyObject *unused) { @@ -208,21 +161,27 @@ Delay execution for a given number of seconds. The argument may be\n\ a floating point number for subsecond precision."); static PyStructSequence_Field struct_time_type_fields[] = { - {"tm_year", NULL}, - {"tm_mon", NULL}, - {"tm_mday", NULL}, - {"tm_hour", NULL}, - {"tm_min", NULL}, - {"tm_sec", NULL}, - {"tm_wday", NULL}, - {"tm_yday", NULL}, - {"tm_isdst", NULL}, + {"tm_year", "year, for example, 1993"}, + {"tm_mon", "month of year, range [1, 12]"}, + {"tm_mday", "day of month, range [1, 31]"}, + {"tm_hour", "hours, range [0, 23]"}, + {"tm_min", "minutes, range [0, 59]"}, + {"tm_sec", "seconds, range [0, 61])"}, + {"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"}, {0} }; static PyStructSequence_Desc struct_time_type_desc = { "time.struct_time", - NULL, + "The time value as returned by gmtime(), localtime(), and strptime(), and\n" + " accepted by asctime(), mktime() and strftime(). May be considered as a\n" + " sequence of 9 integers.\n\n" + " Note that several fields' values are not the same as those defined by\n" + " the C language standard for struct tm. For example, the value of the\n" + " field tm_year is the actual year, not year - 1900. See individual\n" + " fields' descriptions for details.", struct_time_type_fields, 9, }; @@ -258,29 +217,6 @@ tmtotuple(struct tm *p) } static PyObject * -structtime_totuple(PyObject *t) -{ - PyObject *x = NULL; - unsigned int i; - PyObject *v = PyTuple_New(9); - if (v == NULL) - return NULL; - - for (i=0; i<9; i++) { - x = PyStructSequence_GET_ITEM(t, i); - Py_INCREF(x); - PyTuple_SET_ITEM(v, i, x); - } - - if (PyErr_Occurred()) { - Py_XDECREF(v); - return NULL; - } - - return v; -} - -static PyObject * time_convert(double when, struct tm * (*function)(const time_t *)) { struct tm *p; @@ -354,60 +290,69 @@ PyDoc_STRVAR(localtime_doc, Convert seconds since the Epoch to a time tuple expressing local time.\n\ When 'seconds' is not passed in, convert the current time instead."); +/* Convert 9-item tuple to tm structure. Return 1 on success, set + * an exception and return 0 on error. + */ static int gettmarg(PyObject *args, struct tm *p) { int y; - PyObject *t = NULL; memset((void *) p, '\0', sizeof(struct tm)); - if (PyTuple_Check(args)) { - t = args; - Py_INCREF(t); - } - else if (Py_TYPE(args) == &StructTimeType) { - t = structtime_totuple(args); - } - else { + if (!PyTuple_Check(args)) { PyErr_SetString(PyExc_TypeError, "Tuple or struct_time argument required"); return 0; } - if (t == NULL || !PyArg_ParseTuple(t, "iiiiiiiii", - &y, - &p->tm_mon, - &p->tm_mday, - &p->tm_hour, - &p->tm_min, - &p->tm_sec, - &p->tm_wday, - &p->tm_yday, - &p->tm_isdst)) { - Py_XDECREF(t); + if (!PyArg_ParseTuple(args, "iiiiiiiii", + &y, &p->tm_mon, &p->tm_mday, + &p->tm_hour, &p->tm_min, &p->tm_sec, + &p->tm_wday, &p->tm_yday, &p->tm_isdst)) return 0; - } - Py_DECREF(t); - if (y < 1900) { + /* If year is specified with less than 4 digits, its interpretation + * depends on the accept2dyear value. + * + * If accept2dyear is true (default), a backward compatibility behavior is + * invoked as follows: + * + * - for 2-digit year, century is guessed according to POSIX rules for + * %y strptime format: 21st century for y < 69, 20th century + * otherwise. A deprecation warning is issued when century + * information is guessed in this way. + * + * - for 3-digit or negative year, a ValueError exception is raised. + * + * If accept2dyear is false (set by the program or as a result of a + * non-empty value assigned to PYTHONY2K environment variable) all year + * values are interpreted as given. + */ + if (y < 1000) { PyObject *accept = PyDict_GetItemString(moddict, "accept2dyear"); - if (accept == NULL || !PyLong_CheckExact(accept) || - !PyObject_IsTrue(accept)) { - PyErr_SetString(PyExc_ValueError, - "year >= 1900 required"); - return 0; + if (accept != NULL) { + int acceptval = PyObject_IsTrue(accept); + if (acceptval == -1) + return 0; + if (acceptval) { + if (0 <= y && y < 69) + y += 2000; + else if (69 <= y && y < 100) + y += 1900; + else { + PyErr_SetString(PyExc_ValueError, + "year out of range"); + return 0; + } + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Century info guessed for a 2-digit year.", 1) != 0) + return 0; + } } - if (69 <= y && y <= 99) - y += 1900; - else if (0 <= y && y <= 68) - y += 2000; - else { - PyErr_SetString(PyExc_ValueError, - "year out of range"); + else return 0; - } } p->tm_year = y - 1900; p->tm_mon--; @@ -416,6 +361,76 @@ gettmarg(PyObject *args, struct tm *p) return 1; } +/* Check values of the struct tm fields before it is passed to strftime() and + * asctime(). Return 1 if all values are valid, otherwise set an exception + * and returns 0. + */ +static int +checktm(struct tm* buf) +{ + /* Checks added to make sure strftime() and asctime() does not crash Python by + indexing blindly into some array for a textual representation + by some bad index (fixes bug #897625 and #6608). + + Also support values of zero from Python code for arguments in which + that is out of range by forcing that value to the lowest value that + is valid (fixed bug #1520914). + + Valid ranges based on what is allowed in struct tm: + + - tm_year: [0, max(int)] (1) + - tm_mon: [0, 11] (2) + - tm_mday: [1, 31] + - tm_hour: [0, 23] + - tm_min: [0, 59] + - tm_sec: [0, 60] + - tm_wday: [0, 6] (1) + - tm_yday: [0, 365] (2) + - tm_isdst: [-max(int), max(int)] + + (1) gettmarg() handles bounds-checking. + (2) Python's acceptable range is one greater than the range in C, + thus need to check against automatic decrement by gettmarg(). + */ + if (buf->tm_mon == -1) + buf->tm_mon = 0; + else if (buf->tm_mon < 0 || buf->tm_mon > 11) { + PyErr_SetString(PyExc_ValueError, "month out of range"); + return 0; + } + if (buf->tm_mday == 0) + buf->tm_mday = 1; + else if (buf->tm_mday < 0 || buf->tm_mday > 31) { + PyErr_SetString(PyExc_ValueError, "day of month out of range"); + return 0; + } + if (buf->tm_hour < 0 || buf->tm_hour > 23) { + PyErr_SetString(PyExc_ValueError, "hour out of range"); + return 0; + } + if (buf->tm_min < 0 || buf->tm_min > 59) { + PyErr_SetString(PyExc_ValueError, "minute out of range"); + return 0; + } + if (buf->tm_sec < 0 || buf->tm_sec > 61) { + PyErr_SetString(PyExc_ValueError, "seconds out of range"); + return 0; + } + /* tm_wday does not need checking of its upper-bound since taking + ``% 7`` in gettmarg() automatically restricts the range. */ + if (buf->tm_wday < 0) { + PyErr_SetString(PyExc_ValueError, "day of week out of range"); + return 0; + } + if (buf->tm_yday == -1) + buf->tm_yday = 0; + else if (buf->tm_yday < 0 || buf->tm_yday > 365) { + PyErr_SetString(PyExc_ValueError, "day of year out of range"); + return 0; + } + return 1; +} + #ifdef HAVE_STRFTIME #ifdef HAVE_WCSFTIME #define time_char wchar_t @@ -433,108 +448,56 @@ time_strftime(PyObject *self, PyObject *args) PyObject *tup = NULL; struct tm buf; const time_char *fmt; - PyObject *format, *tmpfmt; +#ifdef HAVE_WCSFTIME + wchar_t *format; +#else + PyObject *format; +#endif + PyObject *format_arg; size_t fmtlen, buflen; - time_char *outbuf = 0; + time_char *outbuf = NULL; size_t i; + PyObject *ret = NULL; memset((void *) &buf, '\0', sizeof(buf)); /* Will always expect a unicode string to be passed as format. Given that there's no str type anymore in py3k this seems safe. */ - if (!PyArg_ParseTuple(args, "U|O:strftime", &format, &tup)) + if (!PyArg_ParseTuple(args, "U|O:strftime", &format_arg, &tup)) return NULL; if (tup == NULL) { time_t tt = time(NULL); buf = *localtime(&tt); - } else if (!gettmarg(tup, &buf)) - return NULL; - - /* Checks added to make sure strftime() does not crash Python by - indexing blindly into some array for a textual representation - by some bad index (fixes bug #897625). - - Also support values of zero from Python code for arguments in which - that is out of range by forcing that value to the lowest value that - is valid (fixed bug #1520914). - - Valid ranges based on what is allowed in struct tm: - - - tm_year: [0, max(int)] (1) - - tm_mon: [0, 11] (2) - - tm_mday: [1, 31] - - tm_hour: [0, 23] - - tm_min: [0, 59] - - tm_sec: [0, 60] - - tm_wday: [0, 6] (1) - - tm_yday: [0, 365] (2) - - tm_isdst: [-max(int), max(int)] - - (1) gettmarg() handles bounds-checking. - (2) Python's acceptable range is one greater than the range in C, - thus need to check against automatic decrement by gettmarg(). - */ - if (buf.tm_mon == -1) - buf.tm_mon = 0; - else if (buf.tm_mon < 0 || buf.tm_mon > 11) { - PyErr_SetString(PyExc_ValueError, "month out of range"); - return NULL; - } - if (buf.tm_mday == 0) - buf.tm_mday = 1; - else if (buf.tm_mday < 0 || buf.tm_mday > 31) { - PyErr_SetString(PyExc_ValueError, "day of month out of range"); - return NULL; - } - if (buf.tm_hour < 0 || buf.tm_hour > 23) { - PyErr_SetString(PyExc_ValueError, "hour out of range"); - return NULL; - } - if (buf.tm_min < 0 || buf.tm_min > 59) { - PyErr_SetString(PyExc_ValueError, "minute out of range"); - return NULL; - } - if (buf.tm_sec < 0 || buf.tm_sec > 61) { - PyErr_SetString(PyExc_ValueError, "seconds out of range"); - return NULL; } - /* tm_wday does not need checking of its upper-bound since taking - ``% 7`` in gettmarg() automatically restricts the range. */ - if (buf.tm_wday < 0) { - PyErr_SetString(PyExc_ValueError, "day of week out of range"); + else if (!gettmarg(tup, &buf) || !checktm(&buf)) return NULL; - } - if (buf.tm_yday == -1) - buf.tm_yday = 0; - else if (buf.tm_yday < 0 || buf.tm_yday > 365) { - PyErr_SetString(PyExc_ValueError, "day of year out of range"); - return NULL; - } - if (buf.tm_isdst < -1 || buf.tm_isdst > 1) { + +#if defined(_MSC_VER) || defined(sun) + if (buf.tm_year + 1900 < 1 || 9999 < buf.tm_year + 1900) { PyErr_SetString(PyExc_ValueError, - "daylight savings flag out of range"); + "strftime() requires year in [1; 9999]"); return NULL; } +#endif + + /* Normalize tm_isdst just in case someone foolishly implements %Z + based on the assumption that tm_isdst falls within the range of + [-1, 1] */ + if (buf.tm_isdst < -1) + buf.tm_isdst = -1; + else if (buf.tm_isdst > 1) + buf.tm_isdst = 1; #ifdef HAVE_WCSFTIME - tmpfmt = PyBytes_FromStringAndSize(NULL, - sizeof(wchar_t) * (PyUnicode_GetSize(format)+1)); - if (!tmpfmt) + format = PyUnicode_AsWideCharString(format_arg, NULL); + if (format == NULL) return NULL; - /* This assumes that PyUnicode_AsWideChar doesn't do any UTF-16 - expansion. */ - if (PyUnicode_AsWideChar((PyUnicodeObject*)format, - (wchar_t*)PyBytes_AS_STRING(tmpfmt), - PyUnicode_GetSize(format)+1) == (size_t)-1) - /* This shouldn't fail. */ - Py_FatalError("PyUnicode_AsWideChar failed"); - format = tmpfmt; - fmt = (wchar_t*)PyBytes_AS_STRING(format); + fmt = format; #else /* Convert the unicode string to an ascii one */ - format = PyUnicode_AsEncodedString(format, TZNAME_ENCODING, NULL); + format = PyUnicode_AsEncodedString(format_arg, TZNAME_ENCODING, NULL); if (format == NULL) return NULL; fmt = PyBytes_AS_STRING(format); @@ -565,8 +528,8 @@ time_strftime(PyObject *self, PyObject *args) for (i = 1024; ; i += i) { outbuf = (time_char *)PyMem_Malloc(i*sizeof(time_char)); if (outbuf == NULL) { - Py_DECREF(format); - return PyErr_NoMemory(); + PyErr_NoMemory(); + break; } buflen = format_time(outbuf, i, fmt, &buf); if (buflen > 0 || i >= 256 * fmtlen) { @@ -575,7 +538,6 @@ time_strftime(PyObject *self, PyObject *args) More likely, the format yields an empty result, e.g. an empty format, or %Z when the timezone is unknown. */ - PyObject *ret; #ifdef HAVE_WCSFTIME ret = PyUnicode_FromWideChar(outbuf, buflen); #else @@ -583,19 +545,23 @@ time_strftime(PyObject *self, PyObject *args) TZNAME_ENCODING, NULL); #endif PyMem_Free(outbuf); - Py_DECREF(format); - return ret; + break; } PyMem_Free(outbuf); #if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__) /* VisualStudio .NET 2005 does this properly */ if (buflen == 0 && errno == EINVAL) { PyErr_SetString(PyExc_ValueError, "Invalid format string"); - Py_DECREF(format); - return 0; + break; } #endif } +#ifdef HAVE_WCSFTIME + PyMem_Free(format); +#else + Py_DECREF(format); +#endif + return ret; } #undef time_char @@ -616,40 +582,66 @@ time_strptime(PyObject *self, PyObject *args) PyObject *strptime_result; if (!strptime_module) - return NULL; - strptime_result = PyObject_CallMethod(strptime_module, "_strptime_time", "O", args); + return NULL; + strptime_result = PyObject_CallMethod(strptime_module, + "_strptime_time", "O", args); Py_DECREF(strptime_module); return strptime_result; } + PyDoc_STRVAR(strptime_doc, "strptime(string, format) -> struct_time\n\ \n\ Parse a string to a time tuple according to a format specification.\n\ See the library reference manual for formatting codes (same as strftime())."); +static PyObject * +_asctime(struct tm *timeptr) +{ + /* Inspired by Open Group reference implementation available at + * http://pubs.opengroup.org/onlinepubs/009695399/functions/asctime.html */ + static char wday_name[7][3] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static char mon_name[12][3] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + char buf[20]; /* 'Sun Sep 16 01:03:52\0' */ + int n; + + n = PyOS_snprintf(buf, sizeof(buf), "%.3s %.3s%3d %.2d:%.2d:%.2d", + wday_name[timeptr->tm_wday], + mon_name[timeptr->tm_mon], + timeptr->tm_mday, timeptr->tm_hour, + timeptr->tm_min, timeptr->tm_sec); + /* XXX: since the fields used by snprintf above are validated in checktm, + * the following condition should never trigger. We keep the check because + * historically fixed size buffer used in asctime was the source of + * crashes. */ + if (n + 1 != sizeof(buf)) { + PyErr_SetString(PyExc_ValueError, "unconvertible time"); + return NULL; + } + + return PyUnicode_FromFormat("%s %d", buf, 1900 + timeptr->tm_year); +} static PyObject * time_asctime(PyObject *self, PyObject *args) { PyObject *tup = NULL; struct tm buf; - char *p; + if (!PyArg_UnpackTuple(args, "asctime", 0, 1, &tup)) return NULL; if (tup == NULL) { time_t tt = time(NULL); buf = *localtime(&tt); - } else if (!gettmarg(tup, &buf)) - return NULL; - p = asctime(&buf); - if (p == NULL) { - PyErr_SetString(PyExc_ValueError, "invalid time"); + } else if (!gettmarg(tup, &buf) || !checktm(&buf)) return NULL; - } - if (p[24] == '\n') - p[24] = '\0'; - return PyUnicode_FromString(p); + return _asctime(&buf); } PyDoc_STRVAR(asctime_doc, @@ -664,7 +656,7 @@ time_ctime(PyObject *self, PyObject *args) { PyObject *ot = NULL; time_t tt; - char *p; + struct tm *timeptr; if (!PyArg_UnpackTuple(args, "ctime", 0, 1, &ot)) return NULL; @@ -678,14 +670,12 @@ time_ctime(PyObject *self, PyObject *args) if (tt == (time_t)-1 && PyErr_Occurred()) return NULL; } - p = ctime(&tt); - if (p == NULL) { + timeptr = localtime(&tt); + if (timeptr == NULL) { PyErr_SetString(PyExc_ValueError, "unconvertible time"); return NULL; } - if (p[24] == '\n') - p[24] = '\0'; - return PyUnicode_FromString(p); + return _asctime(timeptr); } PyDoc_STRVAR(ctime_doc, @@ -970,44 +960,12 @@ PyInit_time(void) return m; } - -/* Implement floattime() for various platforms */ - static double floattime(void) { - /* There are three ways to get the time: - (1) gettimeofday() -- resolution in microseconds - (2) ftime() -- resolution in milliseconds - (3) time() -- resolution in seconds - In all cases the return value is a float in seconds. - Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may - fail, so we fall back on ftime() or time(). - Note: clock resolution does not imply clock accuracy! */ -#ifdef HAVE_GETTIMEOFDAY - { - struct timeval t; -#ifdef GETTIMEOFDAY_NO_TZ - if (gettimeofday(&t) == 0) - return (double)t.tv_sec + t.tv_usec*0.000001; -#else /* !GETTIMEOFDAY_NO_TZ */ - if (gettimeofday(&t, (struct timezone *)NULL) == 0) - return (double)t.tv_sec + t.tv_usec*0.000001; -#endif /* !GETTIMEOFDAY_NO_TZ */ - } - -#endif /* !HAVE_GETTIMEOFDAY */ - { -#if defined(HAVE_FTIME) - struct timeb t; - ftime(&t); - return (double)t.time + (double)t.millitm * (double)0.001; -#else /* !HAVE_FTIME */ - time_t secs; - time(&secs); - return (double)secs; -#endif /* !HAVE_FTIME */ - } + _PyTime_timeval t; + _PyTime_gettimeofday(&t); + return (double)t.tv_sec + t.tv_usec*0.000001; } @@ -1088,22 +1046,6 @@ floatsleep(double secs) return -1; } Py_END_ALLOW_THREADS -#elif defined(PLAN9) - { - double millisecs = secs * 1000.0; - if (millisecs > (double)LONG_MAX) { - PyErr_SetString(PyExc_OverflowError, "sleep length is too large"); - return -1; - } - /* This sleep *CAN BE* interrupted. */ - Py_BEGIN_ALLOW_THREADS - if(sleep((long)millisecs) < 0){ - Py_BLOCK_THREADS - PyErr_SetFromErrno(PyExc_IOError); - return -1; - } - Py_END_ALLOW_THREADS - } #else /* XXX Can't interrupt this sleep */ Py_BEGIN_ALLOW_THREADS |
