Commit 1b6f7a90 authored by Tim Peters's avatar Tim Peters

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.
parent 1c3fa18b
/* 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)
......
......@@ -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", &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);
}
......
......@@ -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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment