]> granicus.if.org Git - python/commitdiff
Bug 975996: Add _PyTime_DoubleToTimet to C API
authorTim Peters <tim.peters@gmail.com>
Sun, 20 Jun 2004 02:50:16 +0000 (02:50 +0000)
committerTim Peters <tim.peters@gmail.com>
Sun, 20 Jun 2004 02:50:16 +0000 (02:50 +0000)
New include file timefuncs.h exports private API function
_PyTime_DoubleToTimet() from timemodule.c.  timemodule should export
some other functions too (look for painful bits in datetimemodule.c).

Added insane-argument checking to datetime's assorted fromtimestamp()
and utcfromtimestamp() methods.  Added insane-argument tests of these
to test_datetime, and insane-argument tests for ctime(), localtime()
and gmtime() to test_time.

Include/timefuncs.h [new file with mode: 0644]
Lib/test/test_datetime.py
Lib/test/test_time.py
Misc/NEWS
Modules/datetimemodule.c
Modules/timemodule.c

diff --git a/Include/timefuncs.h b/Include/timefuncs.h
new file mode 100644 (file)
index 0000000..553142d
--- /dev/null
@@ -0,0 +1,23 @@
+/*  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 */
index f7fec572cf834db7b638d68aadcce662fff78107..2a6aca23016e8c25ee17c56ef0e803666a5c0b8a 100644 (file)
@@ -730,6 +730,15 @@ class TestDate(HarmlessMixedComparison):
         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
 
@@ -1380,6 +1389,24 @@ class TestDateTime(TestDate):
         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
 
index 9e16d0b12222c37fe13c6af13129acc59a76eec9..64f1f01d6e79dd3925885fd860aa4fdc88d4373b 100644 (file)
@@ -176,6 +176,14 @@ class TimeTestCase(unittest.TestCase):
                 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)
index b3096ca1ebe600662c1bbc62d8bc2948bf6f7bc8..f2b65e60da831ff1f48bc6f36be870da07d459ab 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -228,9 +228,13 @@ Core and builtins
 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.
 
@@ -555,6 +559,11 @@ Build
 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.
 
index 7f38d1a645fde738897a09bed629a727efd6ece5..d9cf6041eaa9dac63305b81b5d5824b67e60eb62 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <time.h>
 
+#include "timefuncs.h"
 #include "datetime.h"
 
 /* We require that C int be at least 32 bits, and use int virtually
@@ -2226,11 +2227,15 @@ date_new(PyTypeObject *type, PyObject *args, PyObject *kw)
 
 /* 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",
@@ -2278,7 +2283,7 @@ date_fromtimestamp(PyObject *cls, PyObject *args)
        PyObject *result = NULL;
 
        if (PyArg_ParseTuple(args, "d:fromtimestamp", &timestamp))
-               result = date_local_from_time_t(cls, (time_t)timestamp);
+               result = date_local_from_time_t(cls, timestamp);
        return result;
 }
 
@@ -3654,10 +3659,15 @@ static PyObject *
 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);
 }
 
index 0783f7f783e0b62b3c30ddf45d99383d699af8c4..f8557968f55feb6e4f361cb3671af8a90729aa92 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "Python.h"
 #include "structseq.h"
+#include "timefuncs.h"
 
 #include <ctype.h>
 
@@ -84,11 +85,8 @@ static double floattime(void);
 /* 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;
@@ -382,7 +380,7 @@ time_strftime(PyObject *self, PyObject *args)
         /* 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) {
@@ -583,7 +581,7 @@ time_tzset(PyObject *self, PyObject *args)
        /* Reset timezone, altzone, daylight and tzname */
        inittimezone(m);
        Py_DECREF(m);
-       
+
        Py_INCREF(Py_None);
        return Py_None;
 }