summaryrefslogtreecommitdiffstats
path: root/Modules/timemodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/timemodule.c')
-rw-r--r--Modules/timemodule.c486
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