summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/datetime.rst17
-rw-r--r--Include/pytime.h17
-rw-r--r--Include/timefuncs.h25
-rw-r--r--Lib/datetime.py4
-rw-r--r--Lib/test/datetimetester.py34
-rw-r--r--Lib/test/test_time.py57
-rw-r--r--Misc/NEWS9
-rw-r--r--Modules/_datetimemodule.c45
-rw-r--r--Modules/_testcapimodule.c45
-rw-r--r--Modules/_time.c28
-rw-r--r--Modules/_time.h3
-rw-r--r--Modules/posixmodule.c65
-rw-r--r--Modules/selectmodule.c39
-rw-r--r--Modules/timemodule.c7
-rw-r--r--Python/pytime.c99
-rw-r--r--setup.py4
16 files changed, 277 insertions, 221 deletions
diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
index a6c1997..d16551e 100644
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -395,12 +395,17 @@ Other constructors, all class methods:
.. classmethod:: date.fromtimestamp(timestamp)
Return the local date corresponding to the POSIX timestamp, such as is returned
- by :func:`time.time`. This may raise :exc:`ValueError`, if the timestamp is out
+ by :func:`time.time`. This may raise :exc:`OverflowError`, if the timestamp is out
of the range of values supported by the platform C :c:func:`localtime` function.
It's common for this to be restricted to years from 1970 through 2038. Note
that on non-POSIX systems that include leap seconds in their notion of a
timestamp, leap seconds are ignored by :meth:`fromtimestamp`.
+ .. versionchanged:: 3.3
+ Raise :exc:`OverflowError` instead of :exc:`ValueError` if the timestamp
+ is out of the range of values supported by the platform C
+ :c:func:`localtime` function.
+
.. classmethod:: date.fromordinal(ordinal)
@@ -712,6 +717,11 @@ Other constructors, all class methods:
and then it's possible to have two timestamps differing by a second that yield
identical :class:`.datetime` objects. See also :meth:`utcfromtimestamp`.
+ .. versionchanged:: 3.3
+ Raise :exc:`OverflowError` instead of :exc:`ValueError` if the timestamp
+ is out of the range of values supported by the platform C
+ :c:func:`localtime` or :c:func:`gmtime` functions
+
.. classmethod:: datetime.utcfromtimestamp(timestamp)
@@ -737,6 +747,11 @@ Other constructors, all class methods:
timestamp = (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)) / timedelta(seconds=1)
+ .. versionchanged:: 3.3
+ Raise :exc:`OverflowError` instead of :exc:`ValueError` if the timestamp
+ is out of the range of values supported by the platform C
+ :c:func:`gmtime` function.
+
.. classmethod:: datetime.fromordinal(ordinal)
diff --git a/Include/pytime.h b/Include/pytime.h
index 2ea64c9..0473dc7 100644
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -39,9 +39,22 @@ do { \
(tv_end.tv_usec - tv_start.tv_usec) * 0.000001)
#ifndef Py_LIMITED_API
+/* Convert a number of seconds, int or float, to time_t. */
+PyAPI_FUNC(int) _PyTime_ObjectToTime_t(
+ PyObject *obj,
+ time_t *sec);
+
+/* Convert a number of seconds, int or float, to a timeval structure.
+ usec is in the range [0; 999999] and rounded towards zero.
+ For example, -1.2 is converted to (-2, 800000). */
+PyAPI_FUNC(int) _PyTime_ObjectToTimeval(
+ PyObject *obj,
+ time_t *sec,
+ long *usec);
+
/* Convert a number of seconds, int or float, to a timespec structure.
- nsec is always in the range [0; 999999999]. For example, -1.2 is converted
- to (-2, 800000000). */
+ nsec is in the range [0; 999999999] and rounded towards zero.
+ For example, -1.2 is converted to (-2, 800000000). */
PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
PyObject *obj,
time_t *sec,
diff --git a/Include/timefuncs.h b/Include/timefuncs.h
deleted file mode 100644
index 3c43575..0000000
--- a/Include/timefuncs.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* 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().
- */
-#ifndef Py_LIMITED_API
-PyAPI_FUNC(time_t) _PyTime_DoubleToTimet(double x);
-#endif
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* TIMEFUNCS_H */
diff --git a/Lib/datetime.py b/Lib/datetime.py
index c5eeca4..59f3c68 100644
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -1360,7 +1360,7 @@ class datetime(date):
converter = _time.localtime if tz is None else _time.gmtime
t, frac = divmod(t, 1.0)
- us = round(frac * 1e6)
+ us = int(frac * 1e6)
# If timestamp is less than one microsecond smaller than a
# full second, us can be rounded up to 1000000. In this case,
@@ -1380,7 +1380,7 @@ class datetime(date):
def utcfromtimestamp(cls, t):
"Construct a UTC datetime from a POSIX timestamp (like time.time())."
t, frac = divmod(t, 1.0)
- us = round(frac * 1e6)
+ us = int(frac * 1e6)
# If timestamp is less than one microsecond smaller than a
# full second, us can be rounded up to 1000000. In this case,
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 5c02b3a..6de29c5 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -977,7 +977,7 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
# exempt such platforms (provided they return reasonable
# results!).
for insane in -1e200, 1e200:
- self.assertRaises(ValueError, self.theclass.fromtimestamp,
+ self.assertRaises(OverflowError, self.theclass.fromtimestamp,
insane)
def test_today(self):
@@ -1736,12 +1736,32 @@ class TestDateTime(TestDate):
self.verify_field_equality(expected, got)
def test_microsecond_rounding(self):
- # Test whether fromtimestamp "rounds up" floats that are less
- # than 1/2 microsecond smaller than an integer.
for fts in [self.theclass.fromtimestamp,
self.theclass.utcfromtimestamp]:
- self.assertEqual(fts(0.9999999), fts(1))
- self.assertEqual(fts(0.99999949).microsecond, 999999)
+ zero = fts(0)
+ self.assertEqual(zero.second, 0)
+ self.assertEqual(zero.microsecond, 0)
+ minus_one = fts(-1e-6)
+ self.assertEqual(minus_one.second, 59)
+ self.assertEqual(minus_one.microsecond, 999999)
+
+ t = fts(-1e-8)
+ self.assertEqual(t, minus_one)
+ t = fts(-9e-7)
+ self.assertEqual(t, minus_one)
+ t = fts(-1e-7)
+ self.assertEqual(t, minus_one)
+
+ t = fts(1e-7)
+ self.assertEqual(t, zero)
+ t = fts(9e-7)
+ self.assertEqual(t, zero)
+ t = fts(0.99999949)
+ self.assertEqual(t.second, 0)
+ self.assertEqual(t.microsecond, 999999)
+ t = fts(0.9999999)
+ self.assertEqual(t.second, 0)
+ self.assertEqual(t.microsecond, 999999)
def test_insane_fromtimestamp(self):
# It's possible that some platform maps time_t to double,
@@ -1749,7 +1769,7 @@ class TestDateTime(TestDate):
# exempt such platforms (provided they return reasonable
# results!).
for insane in -1e200, 1e200:
- self.assertRaises(ValueError, self.theclass.fromtimestamp,
+ self.assertRaises(OverflowError, self.theclass.fromtimestamp,
insane)
def test_insane_utcfromtimestamp(self):
@@ -1758,7 +1778,7 @@ class TestDateTime(TestDate):
# exempt such platforms (provided they return reasonable
# results!).
for insane in -1e200, 1e200:
- self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
+ self.assertRaises(OverflowError, self.theclass.utcfromtimestamp,
insane)
@unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
def test_negative_float_fromtimestamp(self):
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
index 26492c1..1fc46aa 100644
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -281,7 +281,7 @@ class TimeTestCase(unittest.TestCase):
# results!).
for func in time.ctime, time.gmtime, time.localtime:
for unreasonable in -1e200, 1e200:
- self.assertRaises(ValueError, func, unreasonable)
+ self.assertRaises(OverflowError, func, unreasonable)
def test_ctime_without_arg(self):
# Not sure how to check the values, since the clock could tick
@@ -365,11 +365,8 @@ class TimeTestCase(unittest.TestCase):
for time_t in (-1, 2**30, 2**33, 2**60):
try:
time.localtime(time_t)
- except ValueError as err:
- if str(err) == "timestamp out of range for platform time_t":
- self.skipTest("need 64-bit time_t")
- else:
- raise
+ except OverflowError:
+ self.skipTest("need 64-bit time_t")
except OSError:
invalid_time_t = time_t
break
@@ -498,19 +495,63 @@ class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear):
class TestPytime(unittest.TestCase):
+ def setUp(self):
+ self.invalid_values = (
+ -(2 ** 100), 2 ** 100,
+ -(2.0 ** 100.0), 2.0 ** 100.0,
+ )
+
+ def test_time_t(self):
+ from _testcapi import pytime_object_to_time_t
+ for obj, time_t in (
+ (0, 0),
+ (-1, -1),
+ (-1.0, -1),
+ (-1.9, -1),
+ (1.0, 1),
+ (1.9, 1),
+ ):
+ self.assertEqual(pytime_object_to_time_t(obj), time_t)
+
+ for invalid in self.invalid_values:
+ self.assertRaises(OverflowError, pytime_object_to_time_t, invalid)
+
+ def test_timeval(self):
+ from _testcapi import pytime_object_to_timeval
+ for obj, timeval in (
+ (0, (0, 0)),
+ (-1, (-1, 0)),
+ (-1.0, (-1, 0)),
+ (1e-6, (0, 1)),
+ (-1e-6, (-1, 999999)),
+ (-1.2, (-2, 800000)),
+ (1.1234560, (1, 123456)),
+ (1.1234569, (1, 123456)),
+ (-1.1234560, (-2, 876544)),
+ (-1.1234561, (-2, 876543)),
+ ):
+ self.assertEqual(pytime_object_to_timeval(obj), timeval)
+
+ for invalid in self.invalid_values:
+ self.assertRaises(OverflowError, pytime_object_to_timeval, invalid)
+
def test_timespec(self):
from _testcapi import pytime_object_to_timespec
for obj, timespec in (
(0, (0, 0)),
(-1, (-1, 0)),
(-1.0, (-1, 0)),
+ (1e-9, (0, 1)),
(-1e-9, (-1, 999999999)),
(-1.2, (-2, 800000000)),
- (1.123456789, (1, 123456789)),
+ (1.1234567890, (1, 123456789)),
+ (1.1234567899, (1, 123456789)),
+ (-1.1234567890, (-2, 876543211)),
+ (-1.1234567891, (-2, 876543210)),
):
self.assertEqual(pytime_object_to_timespec(obj), timespec)
- for invalid in (-(2 ** 100), -(2.0 ** 100.0), 2 ** 100, 2.0 ** 100.0):
+ for invalid in self.invalid_values:
self.assertRaises(OverflowError, pytime_object_to_timespec, invalid)
diff --git a/Misc/NEWS b/Misc/NEWS
index c07e555..85b7c9f 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -24,6 +24,15 @@ Core and Builtins
Library
-------
+- Issue #14180: time.ctime(), gmtime(), time.localtime(),
+ datetime.date.fromtimestamp(), datetime.datetime.fromtimestamp() and
+ datetime.datetime.utcfromtimestamp() now raises an OverflowError, instead of
+ a ValueError, if the timestamp does not fit in time_t.
+
+- Issue #14180: datetime.datetime.fromtimestamp() and
+ datetime.datetime.utcfromtimestamp() now round microseconds towards zero
+ instead of rounding to nearest with ties going away from zero.
+
- Issue #10543: Fix unittest test discovery with Jython bytecode files.
- Issue #1178863: Separate initialisation from setting when initializing
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 3b0b362..669170a 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -7,8 +7,6 @@
#include <time.h>
-#include "_time.h"
-
/* Differentiate between building the core module and building extension
* modules.
*/
@@ -2441,15 +2439,15 @@ date_new(PyTypeObject *type, PyObject *args, PyObject *kw)
/* Return new date from localtime(t). */
static PyObject *
-date_local_from_time_t(PyObject *cls, double ts)
+date_local_from_object(PyObject *cls, PyObject *obj)
{
struct tm *tm;
time_t t;
PyObject *result = NULL;
- t = _PyTime_DoubleToTimet(ts);
- if (t == (time_t)-1 && PyErr_Occurred())
+ if (_PyTime_ObjectToTime_t(obj, &t) == -1)
return NULL;
+
tm = localtime(&t);
if (tm)
result = PyObject_CallFunction(cls, "iii",
@@ -2494,11 +2492,11 @@ date_today(PyObject *cls, PyObject *dummy)
static PyObject *
date_fromtimestamp(PyObject *cls, PyObject *args)
{
- double timestamp;
+ PyObject *timestamp;
PyObject *result = NULL;
- if (PyArg_ParseTuple(args, "d:fromtimestamp", &timestamp))
- result = date_local_from_time_t(cls, timestamp);
+ if (PyArg_ParseTuple(args, "O:fromtimestamp", &timestamp))
+ result = date_local_from_object(cls, timestamp);
return result;
}
@@ -4096,31 +4094,14 @@ datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us,
* to get that much precision (e.g., C time() isn't good enough).
*/
static PyObject *
-datetime_from_timestamp(PyObject *cls, TM_FUNC f, double timestamp,
+datetime_from_timestamp(PyObject *cls, TM_FUNC f, PyObject *timestamp,
PyObject *tzinfo)
{
time_t timet;
- double fraction;
- int us;
+ long us;
- timet = _PyTime_DoubleToTimet(timestamp);
- if (timet == (time_t)-1 && PyErr_Occurred())
+ if (_PyTime_ObjectToTimeval(timestamp, &timet, &us) == -1)
return NULL;
- fraction = timestamp - (double)timet;
- us = (int)round_to_long(fraction * 1e6);
- if (us < 0) {
- /* Truncation towards zero is not what we wanted
- for negative numbers (Python's mod semantics) */
- timet -= 1;
- us += 1000000;
- }
- /* If timestamp is less than one microsecond smaller than a
- * full second, round up. Otherwise, ValueErrors are raised
- * for some floats. */
- if (us == 1000000) {
- timet += 1;
- us = 0;
- }
return datetime_from_timet_and_us(cls, f, timet, us, tzinfo);
}
@@ -4181,11 +4162,11 @@ static PyObject *
datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw)
{
PyObject *self;
- double timestamp;
+ PyObject *timestamp;
PyObject *tzinfo = Py_None;
static char *keywords[] = {"timestamp", "tz", NULL};
- if (! PyArg_ParseTupleAndKeywords(args, kw, "d|O:fromtimestamp",
+ if (! PyArg_ParseTupleAndKeywords(args, kw, "O|O:fromtimestamp",
keywords, &timestamp, &tzinfo))
return NULL;
if (check_tzinfo_subclass(tzinfo) < 0)
@@ -4210,10 +4191,10 @@ datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw)
static PyObject *
datetime_utcfromtimestamp(PyObject *cls, PyObject *args)
{
- double timestamp;
+ PyObject *timestamp;
PyObject *result = NULL;
- if (PyArg_ParseTuple(args, "d:utcfromtimestamp", &timestamp))
+ if (PyArg_ParseTuple(args, "O:utcfromtimestamp", &timestamp))
result = datetime_from_timestamp(cls, gmtime, timestamp,
Py_None);
return result;
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 9294df3..9cafa73 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2323,6 +2323,42 @@ run_in_subinterp(PyObject *self, PyObject *args)
return PyLong_FromLong(r);
}
+static PyObject*
+_PyLong_FromTime_t(time_t value)
+{
+#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
+ return PyLong_FromLongLong(value);
+#else
+ assert(sizeof(time_t) <= sizeof(long));
+ return PyLong_FromLong(value);
+#endif
+}
+
+static PyObject *
+test_pytime_object_to_time_t(PyObject *self, PyObject *args)
+{
+ PyObject *obj;
+ time_t sec;
+ if (!PyArg_ParseTuple(args, "O:pytime_object_to_time_t", &obj))
+ return NULL;
+ if (_PyTime_ObjectToTime_t(obj, &sec) == -1)
+ return NULL;
+ return _PyLong_FromTime_t(sec);
+}
+
+static PyObject *
+test_pytime_object_to_timeval(PyObject *self, PyObject *args)
+{
+ PyObject *obj;
+ time_t sec;
+ long usec;
+ if (!PyArg_ParseTuple(args, "O:pytime_object_to_timeval", &obj))
+ return NULL;
+ if (_PyTime_ObjectToTimeval(obj, &sec, &usec) == -1)
+ return NULL;
+ return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), usec);
+}
+
static PyObject *
test_pytime_object_to_timespec(PyObject *self, PyObject *args)
{
@@ -2333,12 +2369,7 @@ test_pytime_object_to_timespec(PyObject *self, PyObject *args)
return NULL;
if (_PyTime_ObjectToTimespec(obj, &sec, &nsec) == -1)
return NULL;
-#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
- return Py_BuildValue("Ll", (PY_LONG_LONG)sec, nsec);
-#else
- assert(sizeof(time_t) <= sizeof(long));
- return Py_BuildValue("ll", (long)sec, nsec);
-#endif
+ return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), nsec);
}
@@ -2430,6 +2461,8 @@ static PyMethodDef TestMethods[] = {
METH_NOARGS},
{"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS},
{"run_in_subinterp", run_in_subinterp, METH_VARARGS},
+ {"pytime_object_to_time_t", test_pytime_object_to_time_t, METH_VARARGS},
+ {"pytime_object_to_timeval", test_pytime_object_to_timeval, METH_VARARGS},
{"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS},
{NULL, NULL} /* sentinel */
};
diff --git a/Modules/_time.c b/Modules/_time.c
deleted file mode 100644
index 10cc8e1..0000000
--- a/Modules/_time.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "Python.h"
-#include "_time.h"
-
-/* Exposed in timefuncs.h. */
-time_t
-_PyTime_DoubleToTimet(double x)
-{
- time_t result;
- double diff;
-
- result = (time_t)x;
- /* How much info did we lose? time_t may be an integral or
- * floating type, and we don't know which. If it's integral,
- * we don't know whether C truncates, rounds, returns the floor,
- * etc. If we lost a second or more, the C rounding is
- * unreasonable, or the input just doesn't fit in a time_t;
- * call it an error regardless. Note that the original cast to
- * time_t can cause a C error too, but nothing we can do to
- * work around that.
- */
- diff = x - (double)result;
- if (diff <= -1.0 || diff >= 1.0) {
- PyErr_SetString(PyExc_ValueError,
- "timestamp out of range for platform time_t");
- result = (time_t)-1;
- }
- return result;
-}
diff --git a/Modules/_time.h b/Modules/_time.h
deleted file mode 100644
index 816593b..0000000
--- a/Modules/_time.h
+++ /dev/null
@@ -1,3 +0,0 @@
-/* XXX: It is probably best to move timefuncs.h content in here, and
- remove it but user code may rely on it. */
-#include "timefuncs.h"
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 628b0b9..c7d48b0 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -3540,31 +3540,6 @@ posix_uname(PyObject *self, PyObject *noargs)
#endif /* HAVE_UNAME */
-static int
-extract_time(PyObject *t, time_t* sec, long* nsec)
-{
- time_t intval;
- if (PyFloat_Check(t)) {
- double d = PyFloat_AsDouble(t);
- double mod;
- *sec = (time_t)d;
- mod = fmod(d, 1.0);
- mod *= 1e9;
- *nsec = (long)mod;
- return 0;
- }
-#if SIZEOF_TIME_T > SIZEOF_LONG
- intval = PyLong_AsUnsignedLongLongMask(t);
-#else
- intval = PyLong_AsLong(t);
-#endif
- if (intval == -1 && PyErr_Occurred())
- return -1;
- *sec = intval;
- *nsec = 0;
- return 0;
-}
-
PyDoc_STRVAR(posix_utime__doc__,
"utime(path[, (atime, mtime)])\n\
Set the access and modified time of the file to the given values.\n\
@@ -3633,12 +3608,12 @@ posix_utime(PyObject *self, PyObject *args)
goto done;
}
else {
- if (extract_time(PyTuple_GET_ITEM(arg, 0),
- &atimesec, &ansec) == -1)
+ if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 0),
+ &atimesec, &ansec) == -1)
goto done;
time_t_to_FILE_TIME(atimesec, ansec, &atime);
- if (extract_time(PyTuple_GET_ITEM(arg, 1),
- &mtimesec, &mnsec) == -1)
+ if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 1),
+ &mtimesec, &mnsec) == -1)
goto done;
time_t_to_FILE_TIME(mtimesec, mnsec, &mtime);
}
@@ -3681,13 +3656,13 @@ done:
return NULL;
}
else {
- if (extract_time(PyTuple_GET_ITEM(arg, 0),
- &atime, &ansec) == -1) {
+ if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 0),
+ &atime, &ansec) == -1) {
Py_DECREF(opath);
return NULL;
}
- if (extract_time(PyTuple_GET_ITEM(arg, 1),
- &mtime, &mnsec) == -1) {
+ if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 1),
+ &mtime, &mnsec) == -1) {
Py_DECREF(opath);
return NULL;
}
@@ -3763,12 +3738,12 @@ posix_futimes(PyObject *self, PyObject *args)
return NULL;
}
else {
- if (extract_time(PyTuple_GET_ITEM(arg, 0),
- &atime, &ansec) == -1) {
+ if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 0),
+ &atime, &ansec) == -1) {
return NULL;
}
- if (extract_time(PyTuple_GET_ITEM(arg, 1),
- &mtime, &mnsec) == -1) {
+ if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 1),
+ &mtime, &mnsec) == -1) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
@@ -3829,13 +3804,13 @@ posix_lutimes(PyObject *self, PyObject *args)
return NULL;
}
else {
- if (extract_time(PyTuple_GET_ITEM(arg, 0),
- &atime, &ansec) == -1) {
+ if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 0),
+ &atime, &ansec) == -1) {
Py_DECREF(opath);
return NULL;
}
- if (extract_time(PyTuple_GET_ITEM(arg, 1),
- &mtime, &mnsec) == -1) {
+ if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 1),
+ &mtime, &mnsec) == -1) {
Py_DECREF(opath);
return NULL;
}
@@ -9610,13 +9585,13 @@ posix_futimesat(PyObject *self, PyObject *args)
return NULL;
}
else {
- if (extract_time(PyTuple_GET_ITEM(arg, 0),
- &atime, &ansec) == -1) {
+ if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 0),
+ &atime, &ansec) == -1) {
Py_DECREF(opath);
return NULL;
}
- if (extract_time(PyTuple_GET_ITEM(arg, 1),
- &mtime, &mnsec) == -1) {
+ if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(arg, 1),
+ &mtime, &mnsec) == -1) {
Py_DECREF(opath);
return NULL;
}
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
index 945055f..4d99250 100644
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -206,9 +206,7 @@ select_select(PyObject *self, PyObject *args)
PyObject *ret = NULL;
PyObject *tout = Py_None;
fd_set ifdset, ofdset, efdset;
- double timeout;
struct timeval tv, *tvp;
- long seconds;
int imax, omax, emax, max;
int n;
@@ -225,23 +223,12 @@ select_select(PyObject *self, PyObject *args)
return NULL;
}
else {
- timeout = PyFloat_AsDouble(tout);
- if (timeout == -1 && PyErr_Occurred())
+ if (_PyTime_ObjectToTimeval(tout, &tv.tv_sec, &tv.tv_usec) == -1)
return NULL;
- if (timeout > (double)LONG_MAX) {
- PyErr_SetString(PyExc_OverflowError,
- "timeout period too long");
+ if (tv.tv_sec < 0) {
+ PyErr_SetString(PyExc_ValueError, "timeout must be non-negative");
return NULL;
}
- if (timeout < 0) {
- PyErr_SetString(PyExc_ValueError,
- "timeout must be non-negative");
- return NULL;
- }
- seconds = (long)timeout;
- timeout = timeout - (double)seconds;
- tv.tv_sec = seconds;
- tv.tv_usec = (long)(timeout * 1E6);
tvp = &tv;
}
@@ -1870,27 +1857,15 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
ptimeoutspec = NULL;
}
else if (PyNumber_Check(otimeout)) {
- double timeout;
- long seconds;
-
- timeout = PyFloat_AsDouble(otimeout);
- if (timeout == -1 && PyErr_Occurred())
- return NULL;
- if (timeout > (double)LONG_MAX) {
- PyErr_SetString(PyExc_OverflowError,
- "timeout period too long");
+ if (_PyTime_ObjectToTimespec(otimeout,
+ &timeout.tv_sec, &timeout.tv_nsec) == -1)
return NULL;
- }
- if (timeout < 0) {
+
+ if (timeout.tv_sec < 0) {
PyErr_SetString(PyExc_ValueError,
"timeout must be positive or None");
return NULL;
}
-
- seconds = (long)timeout;
- timeout = timeout - (double)seconds;
- timeoutspec.tv_sec = seconds;
- timeoutspec.tv_nsec = (long)(timeout * 1E9);
ptimeoutspec = &timeoutspec;
}
else {
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
index 6ebd3ef..efebd49 100644
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -1,7 +1,6 @@
/* Time module */
#include "Python.h"
-#include "_time.h"
#include <ctype.h>
@@ -288,11 +287,7 @@ parse_time_t_args(PyObject *args, char *format, time_t *pwhen)
whent = time(NULL);
}
else {
- double d = PyFloat_AsDouble(ot);
- if (PyErr_Occurred())
- return 0;
- whent = _PyTime_DoubleToTimet(d);
- if (whent == (time_t)-1 && PyErr_Occurred())
+ if (_PyTime_ObjectToTime_t(ot, &whent) == -1)
return 0;
}
*pwhen = whent;
diff --git a/Python/pytime.c b/Python/pytime.c
index d23ce75..79a1a33 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -70,9 +70,37 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
#endif /* MS_WINDOWS */
}
-int
-_PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec)
+static void
+error_time_t_overflow(void)
+{
+ PyErr_SetString(PyExc_OverflowError,
+ "timestamp out of range for platform time_t");
+}
+
+static time_t
+_PyLong_AsTime_t(PyObject *obj)
+{
+#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
+ PY_LONG_LONG val;
+ val = PyLong_AsLongLong(obj);
+#else
+ long val;
+ assert(sizeof(time_t) <= sizeof(long));
+ val = PyLong_AsLong(obj);
+#endif
+ if (val == -1 && PyErr_Occurred()) {
+ if (PyErr_ExceptionMatches(PyExc_OverflowError))
+ error_time_t_overflow();
+ return -1;
+ }
+ return (time_t)val;
+}
+
+static int
+_PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator,
+ double denominator)
{
+ assert(denominator <= LONG_MAX);
if (PyFloat_Check(obj)) {
double d, intpart, floatpart, err;
@@ -85,34 +113,61 @@ _PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec)
*sec = (time_t)intpart;
err = intpart - (double)*sec;
- if (err <= -1.0 || err >= 1.0)
- goto overflow;
+ if (err <= -1.0 || err >= 1.0) {
+ error_time_t_overflow();
+ return -1;
+ }
- floatpart *= 1e9;
- *nsec = (long)floatpart;
+ floatpart *= denominator;
+ *numerator = (long)floatpart;
return 0;
}
else {
-#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
- *sec = PyLong_AsLongLong(obj);
-#else
- assert(sizeof(time_t) <= sizeof(long));
- *sec = PyLong_AsLong(obj);
-#endif
- if (*sec == -1 && PyErr_Occurred()) {
- if (PyErr_ExceptionMatches(PyExc_OverflowError))
- goto overflow;
- else
- return -1;
+ *sec = _PyLong_AsTime_t(obj);
+ if (*sec == (time_t)-1 && PyErr_Occurred())
+ return -1;
+ *numerator = 0;
+ return 0;
+ }
+}
+
+int
+_PyTime_ObjectToTime_t(PyObject *obj, time_t *sec)
+{
+ if (PyFloat_Check(obj)) {
+ double d, intpart, err;
+
+ /*whent = _PyTime_DoubleToTimet(d);*/
+
+ d = PyFloat_AsDouble(obj);
+ (void)modf(d, &intpart);
+
+ *sec = (time_t)intpart;
+ err = intpart - (double)*sec;
+ if (err <= -1.0 || err >= 1.0) {
+ error_time_t_overflow();
+ return -1;
}
- *nsec = 0;
return 0;
}
+ else {
+ *sec = _PyLong_AsTime_t(obj);
+ if (*sec == (time_t)-1 && PyErr_Occurred())
+ return -1;
+ return 0;
+ }
+}
-overflow:
- PyErr_SetString(PyExc_OverflowError,
- "timestamp out of range for platform time_t");
- return -1;
+int
+_PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec)
+{
+ return _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9);
+}
+
+int
+_PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec)
+{
+ return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6);
}
void
diff --git a/setup.py b/setup.py
index c636f08..7650996 100644
--- a/setup.py
+++ b/setup.py
@@ -512,9 +512,9 @@ class PyBuildExt(build_ext):
time_libs.append(lib)
# time operations and variables
- exts.append( Extension('time', ['timemodule.c', '_time.c'],
+ exts.append( Extension('time', ['timemodule.c'],
libraries=time_libs) )
- exts.append( Extension('_datetime', ['_datetimemodule.c', '_time.c']) )
+ exts.append( Extension('_datetime', ['_datetimemodule.c']) )
# random number generator implemented in C
exts.append( Extension("_random", ["_randommodule.c"]) )
# bisect