Bug 975996: Add _PyTime_DoubleToTimet to C API
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.
This commit is contained in:
parent
1c3fa18be7
commit
1b6f7a9057
|
@ -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 */
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
15
Misc/NEWS
15
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.
|
||||
|
||||
|
|
|
@ -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", ×tamp))
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue