--- /dev/null
+/* timefuncs.h
+ */
+
+/* Utility function related to timemodule.c. */
+
+#ifndef TIMEFUNCS_H
+#define TIMEFUNCS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Cast double x to time_t, but raise ValueError if x is too large
+ * to fit in a time_t. ValueError is set on return iff the return
+ * value is (time_t)-1 and PyErr_Occurred().
+ */
+PyAPI_FUNC(time_t) _PyTime_DoubleToTimet(double x);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* TIMEFUNCS_H */
self.assertEqual(d.month, month)
self.assertEqual(d.day, day)
+ def test_insane_fromtimestamp(self):
+ # It's possible that some platform maps time_t to double,
+ # and that this test will fail there. This test should
+ # exempt such platforms (provided they return reasonable
+ # results!).
+ for insane in -1e200, 1e200:
+ self.assertRaises(ValueError, self.theclass.fromtimestamp,
+ insane)
+
def test_today(self):
import time
got = self.theclass.utcfromtimestamp(ts)
self.verify_field_equality(expected, got)
+ def test_insane_fromtimestamp(self):
+ # It's possible that some platform maps time_t to double,
+ # and that this test will fail there. This test should
+ # exempt such platforms (provided they return reasonable
+ # results!).
+ for insane in -1e200, 1e200:
+ self.assertRaises(ValueError, self.theclass.fromtimestamp,
+ insane)
+
+ def test_insane_utcfromtimestamp(self):
+ # It's possible that some platform maps time_t to double,
+ # and that this test will fail there. This test should
+ # exempt such platforms (provided they return reasonable
+ # results!).
+ for insane in -1e200, 1e200:
+ self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
+ insane)
+
def test_utcnow(self):
import time
del environ['TZ']
time.tzset()
+ def test_insane_timestamps(self):
+ # It's possible that some platform maps time_t to double,
+ # and that this test will fail there. This test should
+ # exempt such platforms (provided they return reasonable
+ # results!).
+ for func in time.ctime, time.gmtime, time.localtime:
+ for unreasonable in -1e200, 1e200:
+ self.assertRaises(ValueError, func, unreasonable)
def test_main():
test_support.run_unittest(TimeTestCase)
Extension modules
-----------------
-- time module code that deals with time_t timestamps will now raise a
- ValueError if more than a second is lost in precision from time_t being less
- precise than a double. Closes bug #919012.
+- time module code that deals with input POSIX timestamps will now raise
+ ValueError if more than a second is lost in precision when the
+ timestamp is cast to the platform C time_t type. There's no chance
+ that the platform will do anything sensible with the result in such
+ cases. This includes ctime(), localtime() and gmtime(). Assorted
+ fromtimestamp() and utcfromtimestamp() methods in the datetime module
+ were also protected. Closes bugs #919012 and 975996.
- fcntl.ioctl now warns if the mutate flag is not specified.
C API
-----
+- Private function _PyTime_DoubleToTimet added, to convert a Python
+ timestamp (C double) to platform time_t with some out-of-bounds
+ checking. Declared in new header file timefuncs.h. It would be
+ good to expose some other internal timemodule.c functions there.
+
- New public functions PyEval_EvaluateFrame and PyGen_New to expose
generator objects.
#include <time.h>
+#include "timefuncs.h"
#include "datetime.h"
/* We require that C int be at least 32 bits, and use int virtually
/* Return new date from localtime(t). */
static PyObject *
-date_local_from_time_t(PyObject *cls, time_t t)
+date_local_from_time_t(PyObject *cls, double ts)
{
struct tm *tm;
+ time_t t;
PyObject *result = NULL;
+ t = _PyTime_DoubleToTimet(ts);
+ if (t == (time_t)-1 && PyErr_Occurred())
+ return NULL;
tm = localtime(&t);
if (tm)
result = PyObject_CallFunction(cls, "iii",
PyObject *result = NULL;
if (PyArg_ParseTuple(args, "d:fromtimestamp", ×tamp))
- result = date_local_from_time_t(cls, (time_t)timestamp);
+ result = date_local_from_time_t(cls, timestamp);
return result;
}
datetime_from_timestamp(PyObject *cls, TM_FUNC f, double timestamp,
PyObject *tzinfo)
{
- time_t timet = (time_t)timestamp;
- double fraction = timestamp - (double)timet;
- int us = (int)round_to_long(fraction * 1e6);
+ time_t timet;
+ double fraction;
+ int us;
+ timet = _PyTime_DoubleToTimet(timestamp);
+ if (timet == (time_t)-1 && PyErr_Occurred())
+ return NULL;
+ fraction = timestamp - (double)timet;
+ us = (int)round_to_long(fraction * 1e6);
return datetime_from_timet_and_us(cls, f, timet, us, tzinfo);
}
#include "Python.h"
#include "structseq.h"
+#include "timefuncs.h"
#include <ctype.h>
/* For Y2K check */
static PyObject *moddict;
-/* Cast double x to time_t, but raise ValueError if x is too large
- * to fit in a time_t. ValueError is set on return iff the return
- * value is (time_t)-1 and PyErr_Occurred().
- */
-static time_t
+/* Exposed in timefuncs.h. */
+time_t
_PyTime_DoubleToTimet(double x)
{
time_t result;
/* 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).
-
+
No check for year since handled in gettmarg().
*/
if (buf.tm_mon < 0 || buf.tm_mon > 11) {
/* Reset timezone, altzone, daylight and tzname */
inittimezone(m);
Py_DECREF(m);
-
+
Py_INCREF(Py_None);
return Py_None;
}