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:
Tim Peters 2004-06-20 02:50:16 +00:00
parent 1c3fa18be7
commit 1b6f7a9057
6 changed files with 90 additions and 15 deletions

23
Include/timefuncs.h Normal file
View File

@ -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 */

View 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

View 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)

View File

@ -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.

View 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);
}

View 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;
}