diff -r e49efa892efb Include/pytime.h --- a/Include/pytime.h Thu Jul 31 13:07:17 2014 +0200 +++ b/Include/pytime.h Fri Aug 01 01:08:00 2014 +0200 @@ -13,14 +13,20 @@ functions and constants extern "C" { #endif -#ifdef HAVE_GETTIMEOFDAY -typedef struct timeval _PyTime_timeval; -#else +#ifndef Py_LIMITED_API + +typedef enum { + /* Round towards zero. */ + _PyTime_ROUND_DOWN=0, + /* Round away from zero. */ + _PyTime_ROUND_UP +} _PyTime_round_t; + +/* Timestamp with nanosecond resolution */ typedef struct { - time_t tv_sec; /* seconds since Jan. 1, 1970 */ - long tv_usec; /* and microseconds */ -} _PyTime_timeval; -#endif + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds, must be in the range [0; 999999999] */ +} _PyTimeSpec; /* Structure used by time.get_clock_info() */ typedef struct { @@ -30,43 +36,6 @@ typedef struct { double resolution; } _Py_clock_info_t; -/* Similar to POSIX gettimeofday but cannot fail. If system gettimeofday - * fails or is not available, fall back to lower resolution clocks. - */ -PyAPI_FUNC(void) _PyTime_gettimeofday(_PyTime_timeval *tp); - -/* Similar to _PyTime_gettimeofday() but retrieve also information on the - * clock used to get the current time. */ -PyAPI_FUNC(void) _PyTime_gettimeofday_info( - _PyTime_timeval *tp, - _Py_clock_info_t *info); - -#define _PyTime_ADD_SECONDS(tv, interval) \ -do { \ - tv.tv_usec += (long) (((long) interval - interval) * 1000000); \ - tv.tv_sec += (time_t) interval + (time_t) (tv.tv_usec / 1000000); \ - tv.tv_usec %= 1000000; \ -} while (0) - -#define _PyTime_INTERVAL(tv_start, tv_end) \ - ((tv_end.tv_sec - tv_start.tv_sec) + \ - (tv_end.tv_usec - tv_start.tv_usec) * 0.000001) - -#ifndef Py_LIMITED_API - -typedef enum { - /* Round towards zero. */ - _PyTime_ROUND_DOWN=0, - /* Round away from zero. */ - _PyTime_ROUND_UP -} _PyTime_round_t; - -/* Convert a number of seconds, int or float, to time_t. */ -PyAPI_FUNC(int) _PyTime_ObjectToTime_t( - PyObject *obj, - time_t *sec, - _PyTime_round_t); - /* Convert a time_t to a PyLong. */ PyAPI_FUNC(PyObject *) _PyLong_FromTime_t( time_t sec); @@ -75,27 +44,66 @@ PyAPI_FUNC(PyObject *) _PyLong_FromTime_ PyAPI_FUNC(time_t) _PyLong_AsTime_t( PyObject *obj); -/* 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( + +/* Get the system clock. The function never fails. + _PyTimeSpec_Init() ensures that the system clock works. */ +PyAPI_FUNC(void) _PyTimeSpec_get_time(_PyTimeSpec *tp); + +/* Get the system clock: fill into (if set) with information of the + function used to get the system block. */ +PyAPI_FUNC(void) _PyTimeSpec_get_time_info( + _PyTimeSpec *tp, + _Py_clock_info_t *info); + + +/* Convert a number of seconds, int or float, to a _PyTimeSpec structure. + Return 0 on success, -1 on overflow. */ +PyAPI_FUNC(int) _PyTimeSpec_from_double(_PyTimeSpec *ts, + double seconds, + _PyTime_round_t round); + +/* Convert a number of seconds, int or float, to a timestamp. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) _PyTimeSpec_from_object(_PyTimeSpec *ts, PyObject *obj, - time_t *sec, + _PyTime_round_t round); + +/* Add two timestamps: res = ts + ts2. + Return 0 on success, -1 on underflow or overflow. */ +PyAPI_FUNC(int) _PyTimeSpec_add( + _PyTimeSpec *res, + const _PyTimeSpec *ts, + const _PyTimeSpec *ts2); + +/* Substract ts2 timestamp to ts: res = end - start. + Return 0 on success, -1 on underflow or overflow. */ +PyAPI_FUNC(int) _PyTimeSpec_sub( + _PyTimeSpec *res, + const _PyTimeSpec *end, + const _PyTimeSpec *start); + +/* Convert a timestamp to a number of seconds. + Return (time_t)-1 and raise an exception on error, return the number of + seconds on success. */ +PyAPI_FUNC(time_t)_PyTimeSpec_to_time_t(const _PyTimeSpec *ts, + _PyTime_round_t round); + +/* Convert a timestamp to a number of milliseconds. + Return LONG_MIN on underflow, LONG_MAX on overflow. */ +PyAPI_FUNC(long) _PyTimeSpec_ms(const _PyTimeSpec *ts, + _PyTime_round_t round); + +/* Convert a timestamp to a (seconds, microseconds) structure. + usec is in the range [0; 999999]. + Return (time_t)-1 and raise an exception on error, return the number of + seconds otherwise. */ +PyAPI_FUNC(time_t) _PyTimeSpec_to_timeval(const _PyTimeSpec *ts, long *usec, - _PyTime_round_t); - -/* Convert a number of seconds, int or float, to a timespec structure. - 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, - long *nsec, - _PyTime_round_t); + _PyTime_round_t round); #endif /* Dummy to force linking. */ -PyAPI_FUNC(void) _PyTime_Init(void); +PyAPI_FUNC(int) _PyTimeSpec_Init(void); #ifdef __cplusplus } diff -r e49efa892efb Lib/test/test_time.py --- a/Lib/test/test_time.py Thu Jul 31 13:07:17 2014 +0200 +++ b/Lib/test/test_time.py Fri Aug 01 01:08:00 2014 +0200 @@ -1,4 +1,5 @@ from test import support +import enum import time import unittest import locale @@ -14,8 +15,13 @@ except ImportError: SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4 TIME_MAXYEAR = (1 << 8 * SIZEOF_INT - 1) - 1 TIME_MINYEAR = -TIME_MAXYEAR - 1 -_PyTime_ROUND_DOWN = 0 -_PyTime_ROUND_UP = 1 + +class _PyTime(enum.IntEnum): + ROUND_DOWN = 0 + ROUND_UP = 1 + +_PyTime_ROUND_DOWN = _PyTime.ROUND_DOWN +_PyTime_ROUND_UP = _PyTime.ROUND_UP class TimeTestCase(unittest.TestCase): @@ -584,6 +590,30 @@ class TestStrftime4dyear(_TestStrftimeYe pass +def get_time_t_limits(): + from _testcapi import pytimespec_from_object + + for time_t_min in (-2**63, -2**31): + try: + pytimespec_from_object(time_t_min - 1, _PyTime_ROUND_UP) + except OverflowError: + pytimespec_from_object(time_t_min, _PyTime_ROUND_UP) + break + else: + raise Exception("unable to compute time_t_min") + + for time_t_max in (2**63 - 1, 2**31 - 1): + try: + pytimespec_from_object(time_t_max + 1, _PyTime_ROUND_UP) + except OverflowError: + pytimespec_from_object(time_t_max, _PyTime_ROUND_UP) + break + else: + raise Exception("unable to compute time_t_max") + + return (time_t_min, time_t_max) + + class TestPytime(unittest.TestCase): def setUp(self): self.invalid_values = ( @@ -593,34 +623,40 @@ class TestPytime(unittest.TestCase): @support.cpython_only def test_time_t(self): - from _testcapi import pytime_object_to_time_t - for obj, time_t, rnd in ( - # Round towards zero - (0, 0, _PyTime_ROUND_DOWN), - (-1, -1, _PyTime_ROUND_DOWN), - (-1.0, -1, _PyTime_ROUND_DOWN), - (-1.9, -1, _PyTime_ROUND_DOWN), - (1.0, 1, _PyTime_ROUND_DOWN), - (1.9, 1, _PyTime_ROUND_DOWN), - # Round away from zero - (0, 0, _PyTime_ROUND_UP), - (-1, -1, _PyTime_ROUND_UP), - (-1.0, -1, _PyTime_ROUND_UP), - (-1.9, -2, _PyTime_ROUND_UP), - (1.0, 1, _PyTime_ROUND_UP), - (1.9, 2, _PyTime_ROUND_UP), + from _testcapi import pytimespec_to_time_t + for timespec, time_t, rnd in ( + # positive + ((0, 0), 0, _PyTime_ROUND_DOWN), + ((0, 0), 0, _PyTime_ROUND_UP), + ((0, 1), 0, _PyTime_ROUND_DOWN), + ((0, 1), 1, _PyTime_ROUND_UP), + ((2, 1), 2, _PyTime_ROUND_DOWN), + ((2, 1), 3, _PyTime_ROUND_UP), + ((3, 999999999), 3, _PyTime_ROUND_DOWN), + ((3, 999999999), 4, _PyTime_ROUND_UP), + # negative + ((-1, 999999999), 0, _PyTime_ROUND_DOWN), + ((-1, 999999999), -1, _PyTime_ROUND_UP), + ((-2, 999999999), -1, _PyTime_ROUND_DOWN), + ((-2, 999999999), -2, _PyTime_ROUND_UP), + ((-3, 1), -2, _PyTime_ROUND_DOWN), + ((-3, 1), -3, _PyTime_ROUND_UP), ): - self.assertEqual(pytime_object_to_time_t(obj, rnd), time_t) + with self.subTest(timespec=timespec, round=rnd, time_t=time_t): + result = pytimespec_to_time_t(timespec, rnd) + val = timespec[0] + timespec[1] * 1e-9 + self.assertEqual(result, time_t, + "%s -> %s != %s" % (val, result, time_t)) - rnd = _PyTime_ROUND_DOWN - for invalid in self.invalid_values: - self.assertRaises(OverflowError, - pytime_object_to_time_t, invalid, rnd) + time_t_min, time_t_max = get_time_t_limits() + + with self.assertRaises(OverflowError): + pytimespec_to_time_t((time_t_max, 1), _PyTime_ROUND_UP) @support.cpython_only - def test_timeval(self): - from _testcapi import pytime_object_to_timeval - for obj, timeval, rnd in ( + def test_from_object(self): + from _testcapi import pytimespec_from_object + for obj, timespec, rnd in ( # Round towards zero (0, (0, 0), _PyTime_ROUND_DOWN), (-1, (-1, 0), _PyTime_ROUND_DOWN), @@ -656,18 +692,45 @@ class TestPytime(unittest.TestCase): (-1.1234560, (-2, 876544), _PyTime_ROUND_UP), (-1.1234561, (-2, 876543), _PyTime_ROUND_UP), ): - with self.subTest(obj=obj, round=rnd, timeval=timeval): - self.assertEqual(pytime_object_to_timeval(obj, rnd), timeval) + with self.subTest(obj=obj, round=rnd, timespec=timespec): + self.assertEqual(pytimespec_from_object(obj, rnd), timespec) rnd = _PyTime_ROUND_DOWN for invalid in self.invalid_values: self.assertRaises(OverflowError, - pytime_object_to_timeval, invalid, rnd) + pytimespec_from_object, invalid, rnd) @support.cpython_only - def test_timespec(self): - from _testcapi import pytime_object_to_timespec - for obj, timespec, rnd in ( + def test_timeval(self): + from _testcapi import pytimespec_to_timeval + for timespec, timeval, rnd in ( + # positive + ((0, 0), (0, 0), _PyTime_ROUND_DOWN), + ((0, 0), (0, 0), _PyTime_ROUND_UP), + ((1, 1), (1, 0), _PyTime_ROUND_DOWN), + ((1, 1), (1, 1), _PyTime_ROUND_UP), + ((1, 999999999), (1, 999999), _PyTime_ROUND_DOWN), + ((1, 999999999), (2, 0), _PyTime_ROUND_UP), + # negative + ((-1, 0), (-1, 0), _PyTime_ROUND_DOWN), + ((-1, 0), (-1, 0), _PyTime_ROUND_UP), + ((-1, 1), (-1, 1), _PyTime_ROUND_DOWN), + ((-1, 1), (-1, 0), _PyTime_ROUND_UP), + ((-1, 999999999), (0, 0), _PyTime_ROUND_DOWN), + ((-1, 999999999), (-1, 999999), _PyTime_ROUND_UP), + ): + with self.subTest(timespec=timespec, round=rnd, timeval=timeval): + self.assertEqual(pytimespec_to_timeval(timespec, rnd), timeval) + + time_t_min, time_t_max = get_time_t_limits() + + with self.assertRaises(OverflowError): + pytimespec_to_timeval((time_t_max, 999999999), _PyTime_ROUND_UP) + + @support.cpython_only + def test_from_object(self): + from _testcapi import pytimespec_from_object, pytimespec_from_double + for secs, timespec, rnd in ( # Round towards zero (0, (0, 0), _PyTime_ROUND_DOWN), (-1, (-1, 0), _PyTime_ROUND_DOWN), @@ -697,13 +760,110 @@ class TestPytime(unittest.TestCase): (-1.1234567890, (-2, 876543211), _PyTime_ROUND_UP), (-1.1234567891, (-2, 876543210), _PyTime_ROUND_UP), ): - with self.subTest(obj=obj, round=rnd, timespec=timespec): - self.assertEqual(pytime_object_to_timespec(obj, rnd), timespec) + with self.subTest(secs=secs, round=rnd, timespec=timespec): + self.assertEqual(pytimespec_from_object(secs, rnd), + timespec) + self.assertEqual(pytimespec_from_double(secs, rnd), + timespec) rnd = _PyTime_ROUND_DOWN for invalid in self.invalid_values: self.assertRaises(OverflowError, - pytime_object_to_timespec, invalid, rnd) + pytimespec_from_object, invalid, rnd) + self.assertRaises(OverflowError, + pytimespec_from_double, invalid, rnd) + + @support.cpython_only + def test_timespec_ms(self): + import _testcapi + from _testcapi import pytimespec_ms + + self.assertEqual(pytimespec_ms((0, 0), _PyTime_ROUND_UP), + 0) + self.assertEqual(pytimespec_ms((0, 1), _PyTime_ROUND_DOWN), + 0) + self.assertEqual(pytimespec_ms((0, 1), _PyTime_ROUND_UP), + 1) + self.assertEqual(pytimespec_ms((5, 10**6 + 1), _PyTime_ROUND_DOWN), + 5001) + self.assertEqual(pytimespec_ms((5, 10**6 + 1), _PyTime_ROUND_UP), + 5002) + + self.assertEqual(pytimespec_ms((0, 0), _PyTime_ROUND_UP), + 0) + self.assertEqual(pytimespec_ms((-1, 1), _PyTime_ROUND_DOWN), + -999) + self.assertEqual(pytimespec_ms((3, 0), _PyTime_ROUND_DOWN), + 3000) + self.assertEqual(pytimespec_ms((-1, 1), _PyTime_ROUND_UP), + -1000) + self.assertEqual(pytimespec_ms((-5, 10**6 + 1), _PyTime_ROUND_DOWN), + -4998) + self.assertEqual(pytimespec_ms((-5, 10**6 + 1), _PyTime_ROUND_UP), + -4999) + + # test underflow + secs = (_testcapi.LONG_MIN - 1) // 1000 + pytimespec_ms((secs + 1, 0), _PyTime_ROUND_DOWN) + self.assertRaises(OverflowError, + pytimespec_ms, (secs, 0), _PyTime_ROUND_DOWN) + + # test overflow + secs, ms = divmod(_testcapi.LONG_MAX - 1, 1000) + ns = ms * 10**6 + 1 + pytimespec_ms((secs, ns), _PyTime_ROUND_DOWN) + self.assertRaises(OverflowError, + pytimespec_ms, (secs, ns), _PyTime_ROUND_UP) + + @support.cpython_only + def test_timespec_add_sub(self): + from _testcapi import pytimespec_add, pytimespec_sub + + # a+b + self.assertEqual(pytimespec_add((1, 2), (5, 7)), + (6, 9)) + self.assertEqual(pytimespec_add((5, 3), (-1, 0)), + (4, 3)) + self.assertEqual(pytimespec_add((1, 0), (0, 999999999)), + (1, 999999999)) + self.assertEqual(pytimespec_add((1, 1), (0, 999999999)), + (2, 0)) + self.assertEqual(pytimespec_add((1, 999999999), (1, 999999999)), + (3, 999999998)) + + # a-b + self.assertEqual(pytimespec_sub((5, 3), (1, 1)), + (4, 2)) + self.assertEqual(pytimespec_sub((5, 3), (-1, 0)), + (6, 3)) + self.assertEqual(pytimespec_sub((0, 0), (0, 1)), + (-1, 999999999)) + + # compute time_t min/max + + time_t_min, time_t_max = get_time_t_limits() + + # FIXME: test overflow + # # test a+b overflow + # self.assertEqual(pytimespec_add((time_t_max, 0), (-1, 0)), + # (time_t_max-1, 0)) + # self.assertEqual(pytimespec_add((-1, 0), (time_t_max, 0)), + # (time_t_max-1, 0)) + # self.assertRaises(OverflowError, + # pytimespec_add, (time_t_max, 0), (1, 0)) + self.assertRaises(OverflowError, + pytimespec_add, (time_t_max, 999999999), (0, 1)) + + # FIXME: test overflow + # # test a-b overflow + # self.assertEqual(pytimespec_sub((time_t_min, 0), (-1, 0)), + # (time_t_min+1, 0)) + # self.assertRaises(OverflowError, + # pytimespec_sub, (time_t_min, 0), (-1, 0)) + # self.assertRaises(OverflowError, + # pytimespec_sub, (time_t_min, 0), (0, 1)) + self.assertRaises(OverflowError, + pytimespec_sub, (time_t_min, 1), (0, 2)) @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") def test_localtime_timezone(self): diff -r e49efa892efb Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/_datetimemodule.c Fri Aug 01 01:08:00 2014 +0200 @@ -2456,11 +2456,15 @@ date_new(PyTypeObject *type, PyObject *a static PyObject * date_local_from_object(PyObject *cls, PyObject *obj) { + _PyTimeSpec ts; + time_t t; struct tm *tm; - time_t t; - - if (_PyTime_ObjectToTime_t(obj, &t, _PyTime_ROUND_DOWN) == -1) - return NULL; + + if (_PyTimeSpec_from_object(&ts, obj, _PyTime_ROUND_DOWN) < 0) + return -1; + t = _PyTimeSpec_to_time_t(&ts, _PyTime_ROUND_DOWN); + if (t == (time_t)-1 && PyErr_Occurred()) + return -1; tm = localtime(&t); if (tm == NULL) { @@ -4088,11 +4092,16 @@ static PyObject * datetime_from_timestamp(PyObject *cls, TM_FUNC f, PyObject *timestamp, PyObject *tzinfo) { + _PyTimeSpec ts; time_t timet; long us; - if (_PyTime_ObjectToTimeval(timestamp, &timet, &us, _PyTime_ROUND_DOWN) == -1) + if (_PyTimeSpec_from_object(&ts, timestamp, _PyTime_ROUND_DOWN) < 0) return NULL; + timet = _PyTimeSpec_to_timeval(&ts, &us, _PyTime_ROUND_DOWN); + if (timet == (time_t)-1 && PyErr_Occurred()) + return NULL; + return datetime_from_timet_and_us(cls, f, timet, (int)us, tzinfo); } @@ -4103,9 +4112,10 @@ datetime_from_timestamp(PyObject *cls, T static PyObject * datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo) { - _PyTime_timeval t; - _PyTime_gettimeofday(&t); - return datetime_from_timet_and_us(cls, f, t.tv_sec, (int)t.tv_usec, + _PyTimeSpec ts; + _PyTimeSpec_get_time(&ts); + return datetime_from_timet_and_us(cls, f, + ts.tv_sec, (int)(ts.tv_nsec / 1000), tzinfo); } diff -r e49efa892efb Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/_testcapimodule.c Fri Aug 01 01:08:00 2014 +0200 @@ -2527,51 +2527,189 @@ check_time_rounding(int round) return 0; } +static int +pytimespec_parse(_PyTimeSpec *ts, PyObject *obj) +{ + ts->tv_sec = _PyLong_AsTime_t(obj); + if (ts->tv_sec == (time_t)-1 && PyErr_Occurred()) + return -1; + if (ts->tv_nsec < 0 || 999999999 < ts->tv_nsec) { + PyErr_SetString(PyExc_ValueError, "ns must be in range [0, 999999999]"); + return -1; + } + return 0; +} + static PyObject * -test_pytime_object_to_time_t(PyObject *self, PyObject *args) +test_pytimespec_to_time_t(PyObject *self, PyObject *args) { - PyObject *obj; + PyObject *secobj; + _PyTimeSpec ts; + int round; time_t sec; - int round; - if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_time_t", &obj, &round)) + + if (!PyArg_ParseTuple(args, "(O!l)i", + &PyLong_Type, &secobj, &ts.tv_nsec, &round)) + return NULL; + if (pytimespec_parse(&ts, secobj) < 0) return NULL; if (check_time_rounding(round) < 0) return NULL; - if (_PyTime_ObjectToTime_t(obj, &sec, round) == -1) + sec = _PyTimeSpec_to_time_t(&ts, round); + if (sec == (time_t)-1 && PyErr_Occurred()) return NULL; - return _PyLong_FromTime_t(sec); + return Py_BuildValue("N", _PyLong_FromTime_t(sec)); } static PyObject * -test_pytime_object_to_timeval(PyObject *self, PyObject *args) +test_pytimespec_to_timeval(PyObject *self, PyObject *args) { - PyObject *obj; + PyObject *secobj; + _PyTimeSpec ts; + int round; time_t sec; long usec; - int round; - if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_timeval", &obj, &round)) + + if (!PyArg_ParseTuple(args, "(O!l)i", + &PyLong_Type, &secobj, &ts.tv_nsec, &round)) + return NULL; + if (pytimespec_parse(&ts, secobj) < 0) return NULL; if (check_time_rounding(round) < 0) return NULL; - if (_PyTime_ObjectToTimeval(obj, &sec, &usec, round) == -1) + sec = _PyTimeSpec_to_timeval(&ts, &usec, round); + if (sec == (time_t)-1 && PyErr_Occurred()) return NULL; return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), usec); } -static PyObject * -test_pytime_object_to_timespec(PyObject *self, PyObject *args) +static PyObject* +_PyTimeSpec_to_object(_PyTimeSpec *ts) { - PyObject *obj; - time_t sec; - long nsec; + PyObject *sec; + sec = _PyLong_FromTime_t(ts->tv_sec); + if (sec == NULL) + return NULL; + return Py_BuildValue("(Ni)", sec, ts->tv_nsec); +} + +static PyObject* +test_pytimespec_from_double(PyObject *self, PyObject *args) +{ + double secs; int round; - if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_timespec", &obj, &round)) + _PyTimeSpec ts; + if (!PyArg_ParseTuple(args, "di", &secs, &round)) return NULL; if (check_time_rounding(round) < 0) return NULL; - if (_PyTime_ObjectToTimespec(obj, &sec, &nsec, round) == -1) + if (_PyTimeSpec_from_double(&ts, secs, round) < 0) { + PyErr_SetString(PyExc_OverflowError, "underflow or overflow"); return NULL; - return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), nsec); + } + return _PyTimeSpec_to_object(&ts); +} + +static PyObject* +test_pytimespec_from_object(PyObject *self, PyObject *args) +{ + PyObject *obj; + int round; + _PyTimeSpec ts; + if (!PyArg_ParseTuple(args, "Oi", &obj, &round)) + return NULL; + if (check_time_rounding(round) < 0) + return NULL; + if (_PyTimeSpec_from_object(&ts, obj, round) < 0) + return NULL; + return _PyTimeSpec_to_object(&ts); +} + +static int +pytimespec_parse2(PyObject *args, + _PyTimeSpec *ts1, _PyTimeSpec *ts2) +{ + PyObject *secs1, *secs2; + + if (!PyArg_ParseTuple(args, "(O!l)(O!l)", + &PyLong_Type, &secs1, &ts1->tv_nsec, + &PyLong_Type, &secs2, &ts2->tv_nsec)) + return -1; + + if (pytimespec_parse(ts1, secs1) < 0) + return -1; + if (pytimespec_parse(ts2, secs2) < 0) + return -1; + + return 0; +} + +static PyObject* +pytimespec_overflow(_PyTimeSpec *ts) +{ + if (ts->tv_sec < 0) + PyErr_SetString(PyExc_OverflowError, "underflow"); + else + PyErr_SetString(PyExc_OverflowError, "overflow"); + return NULL; +} + +static PyObject* +test_pytimespec_add(PyObject *self, PyObject *args) +{ + _PyTimeSpec res, ts1, ts2; + + if (pytimespec_parse2(args, &ts1, &ts2) < 0) + return NULL; + + if (_PyTimeSpec_add(&res, &ts1, &ts2) < 0) + return pytimespec_overflow(&res); + + return _PyTimeSpec_to_object(&res); +} + +static PyObject* +test_pytimespec_sub(PyObject *self, PyObject *args) +{ + _PyTimeSpec res, end, start; + + if (pytimespec_parse2(args, &end, &start) < 0) + return NULL; + + if (_PyTimeSpec_sub(&res, &end, &start) < 0) + return pytimespec_overflow(&res); + + return _PyTimeSpec_to_object(&res); +} + +static PyObject* +test_pytimespec_ms(PyObject *self, PyObject *args) +{ + _PyTimeSpec ts; + PyObject *sec; + int round; + long ms; + + if (!PyArg_ParseTuple(args, "(O!l)i", + &PyLong_Type, &sec, &ts.tv_nsec, &round)) + return NULL; + + if (pytimespec_parse(&ts, sec) < 0) + return NULL; + if (check_time_rounding(round) < 0) + return NULL; + + ms = _PyTimeSpec_ms(&ts, round); + if (ms == LONG_MIN) { + PyErr_SetString(PyExc_OverflowError, "underflow"); + return NULL; + } + if (ms == LONG_MAX) { + PyErr_SetString(PyExc_OverflowError, "overflow"); + return NULL; + } + + return PyLong_FromLong(ms); } static void @@ -3182,9 +3320,13 @@ 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}, + {"pytimespec_from_double", test_pytimespec_from_double, METH_VARARGS}, + {"pytimespec_from_object", test_pytimespec_from_object, METH_VARARGS}, + {"pytimespec_add", test_pytimespec_add, METH_VARARGS}, + {"pytimespec_sub", test_pytimespec_sub, METH_VARARGS}, + {"pytimespec_ms", test_pytimespec_ms, METH_VARARGS}, + {"pytimespec_to_time_t", test_pytimespec_to_time_t, METH_VARARGS}, + {"pytimespec_to_timeval", test_pytimespec_to_timeval, METH_VARARGS}, {"with_tp_del", with_tp_del, METH_VARARGS}, {"test_pymem_alloc0", (PyCFunction)test_pymem_alloc0, METH_NOARGS}, diff -r e49efa892efb Modules/_threadmodule.c --- a/Modules/_threadmodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/_threadmodule.c Fri Aug 01 01:08:00 2014 +0200 @@ -52,14 +52,21 @@ static PyLockStatus acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds) { PyLockStatus r; - _PyTime_timeval curtime; - _PyTime_timeval endtime; + _PyTimeSpec endtime; if (microseconds > 0) { - _PyTime_gettimeofday(&endtime); - endtime.tv_sec += microseconds / (1000 * 1000); - endtime.tv_usec += microseconds % (1000 * 1000); + PY_TIMEOUT_T secs, us; + _PyTimeSpec timeout; + + secs = microseconds / 1000000; + us = microseconds % 1000000; + + timeout.tv_sec = Py_SAFE_DOWNCAST(secs, PY_TIMEOUT_T, time_t); + timeout.tv_nsec = us * 1000; + + _PyTimeSpec_get_time(&endtime); + _PyTimeSpec_add(&endtime, &endtime, &timeout); } @@ -83,9 +90,14 @@ acquire_timed(PyThread_type_lock lock, P /* If we're using a timeout, recompute the timeout after processing * signals, since those can take time. */ if (microseconds > 0) { - _PyTime_gettimeofday(&curtime); - microseconds = ((endtime.tv_sec - curtime.tv_sec) * 1000000 + - (endtime.tv_usec - curtime.tv_usec)); + _PyTimeSpec now, ts; + + _PyTimeSpec_get_time(&now); + _PyTimeSpec_sub(&ts, &endtime, &now); + + microseconds = ts.tv_nsec / 1000; + assert(ts.tv_sec <= (PY_TIMEOUT_MAX - microseconds) * 1000000); + microseconds += ts.tv_sec * 1000000; /* Check for negative values, since those mean block forever. */ diff -r e49efa892efb Modules/gcmodule.c --- a/Modules/gcmodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/gcmodule.c Fri Aug 01 01:08:00 2014 +0200 @@ -25,7 +25,7 @@ #include "Python.h" #include "frameobject.h" /* for PyFrame_ClearFreeList */ -#include "pytime.h" /* for _PyTime_gettimeofday, _PyTime_INTERVAL */ +#include "pytime.h" /* for _PyTime_monotonic */ /* Get an object's GC head */ #define AS_GC(o) ((PyGC_Head *)(o)-1) @@ -908,7 +908,7 @@ collect(int generation, Py_ssize_t *n_co PyGC_Head unreachable; /* non-problematic unreachable trash */ PyGC_Head finalizers; /* objects with, & reachable from, __del__ */ PyGC_Head *gc; - _PyTime_timeval t1; + _PyTimeSpec t1; struct gc_generation_stats *stats = &generation_stats[generation]; @@ -919,7 +919,7 @@ collect(int generation, Py_ssize_t *n_co for (i = 0; i < NUM_GENERATIONS; i++) PySys_FormatStderr(" %zd", gc_list_size(GEN_HEAD(i))); - _PyTime_gettimeofday(&t1); + _PyTimeSpec_get_time(&t1); PySys_WriteStderr("\n"); } @@ -1024,8 +1024,8 @@ collect(int generation, Py_ssize_t *n_co debug_cycle("uncollectable", FROM_GC(gc)); } if (debug & DEBUG_STATS) { - _PyTime_timeval t2; - _PyTime_gettimeofday(&t2); + _PyTimeSpec t2, dt; + _PyTimeSpec_get_time(&t2); if (m == 0 && n == 0) PySys_WriteStderr("gc: done"); @@ -1033,7 +1033,9 @@ collect(int generation, Py_ssize_t *n_co PySys_FormatStderr( "gc: done, %zd unreachable, %zd uncollectable", n+m, n); - PySys_WriteStderr(", %.4fs elapsed\n", _PyTime_INTERVAL(t1, t2)); + _PyTimeSpec_sub(&dt, &t2, &t1); + PySys_WriteStderr(", %.4fs elapsed\n", + dt.tv_sec + dt.tv_nsec * 1e-9); } /* Append instances in the uncollectable set to a Python diff -r e49efa892efb Modules/posixmodule.c --- a/Modules/posixmodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/posixmodule.c Fri Aug 01 01:08:00 2014 +0200 @@ -4880,8 +4880,8 @@ posix_utime(PyObject *self, PyObject *ar } if (times && (times != Py_None)) { - time_t a_sec, m_sec; - long a_nsec, m_nsec; + _PyTimeSpec ts; + if (!PyTuple_CheckExact(times) || (PyTuple_Size(times) != 2)) { PyErr_SetString(PyExc_TypeError, "utime: 'times' must be either" @@ -4889,16 +4889,18 @@ posix_utime(PyObject *self, PyObject *ar goto exit; } utime.now = 0; - if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0), - &a_sec, &a_nsec, _PyTime_ROUND_DOWN) == -1 || - _PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1), - &m_sec, &m_nsec, _PyTime_ROUND_DOWN) == -1) { + + if (_PyTimeSpec_from_object(&ts, PyTuple_GET_ITEM(times, 0), + _PyTime_ROUND_DOWN) < 0) goto exit; - } - utime.atime_s = a_sec; - utime.atime_ns = a_nsec; - utime.mtime_s = m_sec; - utime.mtime_ns = m_nsec; + utime.atime_s = ts.tv_sec; + utime.atime_ns = ts.tv_nsec; + + if (_PyTimeSpec_from_object(&ts, PyTuple_GET_ITEM(times, 1), + _PyTime_ROUND_DOWN) < 0) + goto exit; + utime.mtime_s = ts.tv_sec; + utime.mtime_ns = ts.tv_nsec; } else if (ns) { if (!PyTuple_CheckExact(ns) || (PyTuple_Size(ns) != 2)) { diff -r e49efa892efb Modules/selectmodule.c --- a/Modules/selectmodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/selectmodule.c Fri Aug 01 01:08:00 2014 +0200 @@ -212,15 +212,20 @@ select_select(PyObject *self, PyObject * return NULL; } else { + _PyTimeSpec ts; /* On OpenBSD 5.4, timeval.tv_sec is a long. * Example: long is 64-bit, whereas time_t is 32-bit. */ time_t sec; /* On OS X 64-bit, timeval.tv_usec is an int (and thus still 4 bytes as required), but no longer defined by a long. */ long usec; - if (_PyTime_ObjectToTimeval(tout, &sec, &usec, - _PyTime_ROUND_UP) == -1) + + if (_PyTimeSpec_from_object(&ts, tout, _PyTime_ROUND_UP) < 0) return NULL; + sec = _PyTimeSpec_to_timeval(&ts, &usec, _PyTime_ROUND_UP); + if (sec == (time_t)-1 && PyErr_Occurred()) + return NULL; + #ifdef MS_WINDOWS /* On Windows, timeval.tv_sec is a long (32 bit), * whereas time_t can be 64-bit. */ @@ -2042,9 +2047,12 @@ kqueue_queue_control(kqueue_queue_Object ptimeoutspec = NULL; } else if (PyNumber_Check(otimeout)) { - if (_PyTime_ObjectToTimespec(otimeout, &timeout.tv_sec, - &timeout.tv_nsec, _PyTime_ROUND_UP) == -1) + _PyTimeSpec ts; + + if (_PyTimeSpec_from_object(&ts, otimeout, _PyTime_ROUND_UP) < 0) return NULL; + timeout.tv_sec = ts.tv_sec; + timeout.tv_nsec = ts.tv_nsec; if (timeout.tv_sec < 0) { PyErr_SetString(PyExc_ValueError, diff -r e49efa892efb Modules/signalmodule.c --- a/Modules/signalmodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/signalmodule.c Fri Aug 01 01:08:00 2014 +0200 @@ -946,21 +946,19 @@ signal_sigtimedwait(PyObject *self, PyOb struct timespec buf; sigset_t set; siginfo_t si; - time_t tv_sec; - long tv_nsec; + _PyTimeSpec ts; int err; if (!PyArg_ParseTuple(args, "OO:sigtimedwait", &signals, &timeout)) return NULL; - if (_PyTime_ObjectToTimespec(timeout, &tv_sec, &tv_nsec, - _PyTime_ROUND_DOWN) == -1) + if (_PyTimeSpec_from_object(&ts, timeout, _PyTime_ROUND_DOWN) < 0) return NULL; - buf.tv_sec = tv_sec; - buf.tv_nsec = tv_nsec; + buf.tv_sec = ts.tv_sec; + buf.tv_nsec = ts.tv_nsec; - if (buf.tv_sec < 0 || buf.tv_nsec < 0) { + if (buf.tv_sec < 0) { PyErr_SetString(PyExc_ValueError, "timeout must be non-negative"); return NULL; } diff -r e49efa892efb Modules/socketmodule.c --- a/Modules/socketmodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/socketmodule.c Fri Aug 01 01:08:00 2014 +0200 @@ -592,7 +592,7 @@ internal_setblocking(PySocketSockObject after they've reacquired the interpreter lock. Returns 1 on timeout, -1 on error, 0 otherwise. */ static int -internal_select_ex(PySocketSockObject *s, int writing, double interval) +internal_select_ex(PySocketSockObject *s, int writing, _PyTimeSpec *interval) { int n; @@ -605,7 +605,7 @@ internal_select_ex(PySocketSockObject *s return 0; /* Handling this condition here simplifies the select loops */ - if (interval < 0.0) + if (interval->tv_sec < 0.0) return 1; /* Prefer poll, if available, since you can poll() any fd @@ -613,22 +613,26 @@ internal_select_ex(PySocketSockObject *s #ifdef HAVE_POLL { struct pollfd pollfd; - int timeout; + unsigned long timeout; pollfd.fd = s->sock_fd; pollfd.events = writing ? POLLOUT : POLLIN; - /* s->sock_timeout is in seconds, timeout in ms */ - timeout = (int)(interval * 1000 + 0.5); - n = poll(&pollfd, 1, timeout); + /* timeout is in millisecond, round away from zero */ + timeout = _PyTimeSpec_ms(interval, _PyTime_ROUND_UP); + if (timeout > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, "timeout larger than an int"); + return -1; + } + n = poll(&pollfd, 1, (int)timeout); } #else { /* Construct the arguments to select */ fd_set fds; struct timeval tv; - tv.tv_sec = (int)interval; - tv.tv_usec = (int)((interval - tv.tv_sec) * 1e6); + tv.tv_sec = interval->tv_sec; + tv.tv_usec = (interval->tv_nsec + 500) / 1000; FD_ZERO(&fds); FD_SET(s->sock_fd, &fds); @@ -652,7 +656,9 @@ internal_select_ex(PySocketSockObject *s static int internal_select(PySocketSockObject *s, int writing) { - return internal_select_ex(s, writing, s->sock_timeout); + _PyTimeSpec timeout; + _PyTimeSpec_from_double(&timeout, s->sock_timeout, _PyTime_ROUND_UP); + return internal_select_ex(s, writing, &timeout); } /* @@ -676,13 +682,14 @@ internal_select(PySocketSockObject *s, i #define BEGIN_SELECT_LOOP(s) \ { \ - _PyTime_timeval now, deadline = {0, 0}; \ - double interval = s->sock_timeout; \ + _PyTimeSpec now, deadline = {0, 0}, interval; \ int has_timeout = s->sock_timeout > 0.0; \ + _PyTimeSpec_from_double(&interval, s->sock_timeout, _PyTime_ROUND_UP); \ if (has_timeout) { \ - _PyTime_gettimeofday(&now); \ - deadline = now; \ - _PyTime_ADD_SECONDS(deadline, s->sock_timeout); \ + _PyTimeSpec timeout; \ + _PyTimeSpec_get_time(&deadline); \ + _PyTimeSpec_from_double(&timeout, s->sock_timeout, _PyTime_ROUND_UP); \ + _PyTimeSpec_add(&deadline, &deadline, &timeout); \ } \ while (1) { \ errno = 0; \ @@ -691,8 +698,8 @@ internal_select(PySocketSockObject *s, i if (!has_timeout || \ (!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \ break; \ - _PyTime_gettimeofday(&now); \ - interval = _PyTime_INTERVAL(now, deadline); \ + _PyTimeSpec_get_time(&now); \ + _PyTimeSpec_sub(&interval, &deadline, &now); \ } \ } \ @@ -1981,7 +1988,7 @@ sock_accept(PySocketSockObject *s) BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select_ex(s, 0, interval); + timeout = internal_select_ex(s, 0, &interval); if (!timeout) { #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) if (accept4_works != 0) { @@ -2096,6 +2103,8 @@ sock_settimeout(PySocketSockObject *s, P if (arg == Py_None) timeout = -1.0; else { + _PyTimeSpec ts; + timeout = PyFloat_AsDouble(arg); if (timeout < 0.0) { if (!PyErr_Occurred()) @@ -2103,6 +2112,11 @@ sock_settimeout(PySocketSockObject *s, P "Timeout value out of range"); return NULL; } + if (_PyTimeSpec_from_double(&ts, timeout, _PyTime_ROUND_UP) < 0) { + PyErr_SetString(PyExc_ValueError, + "Timeout value out of range"); + return NULL; + } } s->sock_timeout = timeout; @@ -2591,7 +2605,7 @@ sock_recv_guts(PySocketSockObject *s, ch BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select_ex(s, 0, interval); + timeout = internal_select_ex(s, 0, &interval); if (!timeout) { #ifdef MS_WINDOWS if (len > INT_MAX) @@ -2762,7 +2776,7 @@ sock_recvfrom_guts(PySocketSockObject *s BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS memset(&addrbuf, 0, addrlen); - timeout = internal_select_ex(s, 0, interval); + timeout = internal_select_ex(s, 0, &interval); if (!timeout) { #ifdef MS_WINDOWS if (len > INT_MAX) @@ -2964,7 +2978,7 @@ sock_recvmsg_guts(PySocketSockObject *s, msg.msg_iovlen = iovlen; msg.msg_control = controlbuf; msg.msg_controllen = controllen; - timeout = internal_select_ex(s, 0, interval); + timeout = internal_select_ex(s, 0, &interval); if (!timeout) bytes_received = recvmsg(s->sock_fd, &msg, flags); Py_END_ALLOW_THREADS; @@ -3249,7 +3263,7 @@ sock_send(PySocketSockObject *s, PyObjec BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select_ex(s, 1, interval); + timeout = internal_select_ex(s, 1, &interval); if (!timeout) { #ifdef MS_WINDOWS if (len > INT_MAX) @@ -3403,7 +3417,7 @@ sock_sendto(PySocketSockObject *s, PyObj BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select_ex(s, 1, interval); + timeout = internal_select_ex(s, 1, &interval); if (!timeout) { #ifdef MS_WINDOWS if (len > INT_MAX) @@ -3615,7 +3629,7 @@ sock_sendmsg(PySocketSockObject *s, PyOb BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS; - timeout = internal_select_ex(s, 1, interval); + timeout = internal_select_ex(s, 1, &interval); if (!timeout) bytes_sent = sendmsg(s->sock_fd, &msg, flags); Py_END_ALLOW_THREADS; diff -r e49efa892efb Modules/timemodule.c --- a/Modules/timemodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/timemodule.c Fri Aug 01 01:08:00 2014 +0200 @@ -185,18 +185,17 @@ time_clock_settime(PyObject *self, PyObj { int clk_id; PyObject *obj; - time_t tv_sec; - long tv_nsec; + _PyTimeSpec ts; struct timespec tp; int ret; if (!PyArg_ParseTuple(args, "iO:clock_settime", &clk_id, &obj)) return NULL; - if (_PyTime_ObjectToTimespec(obj, &tv_sec, &tv_nsec, _PyTime_ROUND_DOWN) == -1) + if (_PyTimeSpec_from_object(&ts, obj, _PyTime_ROUND_DOWN) < 0) return NULL; - tp.tv_sec = tv_sec; - tp.tv_nsec = tv_nsec; + tp.tv_sec = ts.tv_sec; + tp.tv_nsec = ts.tv_nsec; ret = clock_settime((clockid_t)clk_id, &tp); if (ret != 0) { @@ -341,7 +340,12 @@ parse_time_t_args(PyObject *args, char * whent = time(NULL); } else { - if (_PyTime_ObjectToTime_t(ot, &whent, _PyTime_ROUND_DOWN) == -1) + _PyTimeSpec ts; + + if (_PyTimeSpec_from_object(&ts, ot, _PyTime_ROUND_DOWN) < 0) + return 0; + whent = _PyTimeSpec_to_time_t(&ts, _PyTime_ROUND_DOWN); + if (whent == (time_t)-1 && PyErr_Occurred()) return 0; } *pwhen = whent; @@ -1534,7 +1538,7 @@ PyInit_time(void) static PyObject* floattime(_Py_clock_info_t *info) { - _PyTime_timeval t; + _PyTimeSpec ts; #ifdef HAVE_CLOCK_GETTIME struct timespec tp; int ret; @@ -1557,8 +1561,8 @@ floattime(_Py_clock_info_t *info) return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); } #endif - _PyTime_gettimeofday_info(&t, info); - return PyFloat_FromDouble((double)t.tv_sec + t.tv_usec * 1e-6); + _PyTimeSpec_get_time_info(&ts, info); + return PyFloat_FromDouble((double)ts.tv_sec + ts.tv_nsec * 1e-9); } diff -r e49efa892efb Python/pythonrun.c --- a/Python/pythonrun.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Python/pythonrun.c Fri Aug 01 01:08:00 2014 +0200 @@ -452,7 +452,8 @@ void if (_PyFaulthandler_Init()) Py_FatalError("Py_Initialize: can't initialize faulthandler"); - _PyTime_Init(); + if (_PyTimeSpec_Init() < 0) + Py_FatalError("Py_Initialize: can't initialize time"); if (initfsencoding(interp) < 0) Py_FatalError("Py_Initialize: unable to load the file system codec"); diff -r e49efa892efb Python/pytime.c --- a/Python/pytime.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Python/pytime.c Fri Aug 01 01:08:00 2014 +0200 @@ -5,10 +5,10 @@ #if defined(__APPLE__) && defined(HAVE_GETTIMEOFDAY) && defined(HAVE_FTIME) /* - * _PyTime_gettimeofday falls back to ftime when getttimeofday fails because the latter - * might fail on some platforms. This fallback is unwanted on MacOSX because - * that makes it impossible to use a binary build on OSX 10.4 on earlier - * releases of the OS. Therefore claim we don't support ftime. + * pygettimeofday() falls back to ftime when getttimeofday fails because the + * latter might fail on some platforms. This fallback is unwanted on MacOSX + * because that makes it impossible to use a binary build on OSX 10.4 on + * earlier releases of the OS. Therefore claim we don't support ftime. */ # undef HAVE_FTIME #endif @@ -18,23 +18,191 @@ extern int ftime(struct timeb *); #endif +#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG +# define TIME_T_MAX PY_LLONG_MAX +# define TIME_T_MIN PY_LLONG_MIN +#elif SIZEOF_TIME_T == SIZEOF_LONG +# define TIME_T_MAX LONG_MAX +# define TIME_T_MIN LONG_MIN +#elif SIZEOF_TIME_T == SIZEOF_INT +# define TIME_T_MAX INT_MAX +# define TIME_T_MIN INT_MIN +#else +# error "unsupported size of time_t" +#endif + +/* one second in milliseconds (ms, 10^-3) */ +#define SEC_IN_MS (1000UL) + +/* one second in microseconds (us, 10^-6) */ +#define SEC_IN_US (1000000UL) + +/* one second in nanoseconds (ns, 10^-9) */ +#define SEC_IN_NS (1000000000UL) + +/* one millisecond (ms, 10^-3) in nanoseconds (ns, 10^-9) */ +#define MS_IN_NS (1000000UL) + +/* one microsecond (us, 10^-6) in nanoseconds (ns, 10^-9) */ +#define US_IN_NS (1000UL) + static void -pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info) +error_time_t_overflow(void) +{ + PyErr_SetString(PyExc_OverflowError, + "timestamp out of range for platform time_t"); +} + +static int +_PyTimeSpec_check_consistency(const _PyTimeSpec *ts) +{ + assert(0 <= ts->tv_nsec); + assert(ts->tv_nsec < SEC_IN_NS); + return 1; +} + +static int +_PyTimeSpec_underflow(_PyTimeSpec *ts) +{ + ts->tv_sec = TIME_T_MIN; + ts->tv_nsec = 0; + return -1; +} + +static int +_PyTimeSpec_overflow(_PyTimeSpec *ts) +{ + ts->tv_sec = TIME_T_MAX; + ts->tv_nsec = SEC_IN_NS - 1; + return -1; +} + +static int +_PyTimeSpec_from_double_err(_PyTimeSpec *ts, + double seconds, _PyTime_round_t round, + int raise) +{ + double intpart, err; + /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ + volatile double floatpart; + + floatpart = modf(seconds, &intpart); + if (floatpart < 0) { + floatpart = 1.0 + floatpart; + intpart -= 1.0; + } + + floatpart *= 1e9; + if (round == _PyTime_ROUND_UP) { + if (intpart >= 0) { + floatpart = ceil(floatpart); + if (floatpart >= 1e9) { + floatpart -= 1e9; + intpart += 1.0; + } + } + else { + floatpart = floor(floatpart); + } + } + + ts->tv_sec = (time_t)intpart; + ts->tv_nsec = (long)floatpart; + assert(_PyTimeSpec_check_consistency(ts)); + + err = intpart - (double)ts->tv_sec; + if (fabs(err) >= 1.0) { + if (raise) + error_time_t_overflow(); + return -1; + } + return 0; +} + +int +_PyTimeSpec_from_double(_PyTimeSpec *ts, + double seconds, _PyTime_round_t round) +{ + return _PyTimeSpec_from_double_err(ts, seconds, round, 0); +} + +int +_PyTimeSpec_add(_PyTimeSpec *res, + const _PyTimeSpec *ts, const _PyTimeSpec *ts2) +{ + assert(_PyTimeSpec_check_consistency(ts)); + assert(_PyTimeSpec_check_consistency(ts2)); + +#if 0 + /* FIXME: the test itself can overflow */ + if (ts2->tv_sec > 0) { + if (ts->tv_sec > TIME_T_MAX - ts2->tv_sec) + return _PyTimeSpec_overflow(res); + } + if (ts2->tv_sec < 0) { + if (ts->tv_sec + ts2->tv_sec < TIME_T_MIN) + return _PyTimeSpec_underflow(res); + } +#endif + + res->tv_sec = ts->tv_sec + ts2->tv_sec; + res->tv_nsec = ts->tv_nsec + ts2->tv_nsec; + while (res->tv_nsec >= SEC_IN_NS) { + res->tv_nsec -= SEC_IN_NS; + if (res->tv_sec == TIME_T_MAX) + return _PyTimeSpec_overflow(res); + res->tv_sec++; + } + assert(_PyTimeSpec_check_consistency(res)); + return 0; +} + +int +_PyTimeSpec_sub(_PyTimeSpec *res, + const _PyTimeSpec *end, const _PyTimeSpec *start) +{ + assert(_PyTimeSpec_check_consistency(end)); + assert(_PyTimeSpec_check_consistency(start)); + +#if 0 + /* FIXME: the test itself can overflow */ + if (end->tv_sec > TIME_T_MAX + start->tv_sec) + return _PyTimeSpec_overflow(res); + if (end->tv_sec < TIME_T_MIN + start->tv_sec) + return _PyTimeSpec_underflow(res); +#endif + + res->tv_sec = end->tv_sec - start->tv_sec; + if (end->tv_nsec < start->tv_nsec) { + if (end->tv_sec == TIME_T_MIN) + return _PyTimeSpec_underflow(res); + res->tv_sec--; + res->tv_nsec = SEC_IN_NS + end->tv_nsec; + } + else { + res->tv_nsec = end->tv_nsec; + } + res->tv_nsec -= start->tv_nsec; + return 0; +} + +static int +pygettimeofday(_PyTimeSpec *ts, _Py_clock_info_t *info, int raise) { #ifdef MS_WINDOWS FILETIME system_time; ULARGE_INTEGER large; - ULONGLONG microseconds; + ULONGLONG nanoseconds; GetSystemTimeAsFileTime(&system_time); large.u.LowPart = system_time.dwLowDateTime; large.u.HighPart = system_time.dwHighDateTime; - /* 11,644,473,600,000,000: number of microseconds between + /* 11,644,473,600,000,000,000: number of nanoseconds between the 1st january 1601 and the 1st january 1970 (369 years + 89 leap days). */ - microseconds = large.QuadPart / 10 - 11644473600000000; - tp->tv_sec = microseconds / 1000000; - tp->tv_usec = microseconds % 1000000; + nanoseconds = large.QuadPart * 100 - 11644473600000000000; + ts->tv_sec = nanoseconds / SEC_IN_NS; + ts->tv_nsec = nanoseconds % SEC_IN_NS; if (info) { DWORD timeAdjustment, timeIncrement; BOOL isTimeAdjustmentDisabled; @@ -46,8 +214,10 @@ pygettimeofday(_PyTime_timeval *tp, _Py_ info->resolution = timeIncrement * 1e-7; info->adjustable = 1; } + assert(_PyTimeSpec_check_consistency(&ts)); + return 0; #else - /* There are three ways to get the time: + /* There are four ways to get the time: (1) gettimeofday() -- resolution in microseconds (2) ftime() -- resolution in milliseconds (3) time() -- resolution in seconds @@ -56,68 +226,100 @@ pygettimeofday(_PyTime_timeval *tp, _Py_ fail, so we fall back on ftime() or time(). Note: clock resolution does not imply clock accuracy! */ +#if defined(HAVE_GETTIMEOFDAY) || defined(HAVE_FTIME) + int err; +#endif #ifdef HAVE_GETTIMEOFDAY - int err; + struct timeval tv; +#endif +#ifdef HAVE_FTIME + struct timeb tb; +#endif + + /* test gettimeofday() */ +#ifdef HAVE_GETTIMEOFDAY #ifdef GETTIMEOFDAY_NO_TZ - err = gettimeofday(tp); + err = gettimeofday(&tv); #else - err = gettimeofday(tp, (struct timezone *)NULL); + err = gettimeofday(&tv, (struct timezone *)NULL); #endif - if (err == 0) { + if (err && raise) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + if (!err) { + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * US_IN_NS; if (info) { info->implementation = "gettimeofday()"; info->resolution = 1e-6; info->monotonic = 0; info->adjustable = 1; } - return; + assert(_PyTimeSpec_check_consistency(ts)); + return 0; } #endif /* HAVE_GETTIMEOFDAY */ -#if defined(HAVE_FTIME) - { - struct timeb t; - ftime(&t); - tp->tv_sec = t.time; - tp->tv_usec = t.millitm * 1000; + /* test ftime() */ +#ifdef HAVE_FTIME + err = ftime(&tb); + if (err && raise) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + if (!err) { + ts->tv_sec = tb.time; + ts->tv_nsec = tb.millitm * MS_IN_NS; if (info) { info->implementation = "ftime()"; info->resolution = 1e-3; info->monotonic = 0; info->adjustable = 1; } + assert(_PyTimeSpec_check_consistency(ts)); + return 0; } -#else /* !HAVE_FTIME */ - tp->tv_sec = time(NULL); - tp->tv_usec = 0; +#endif /* !HAVE_FTIME */ + + errno = 0; + ts->tv_sec = time(NULL); + if (ts->tv_sec == (time_t)-1 && errno != 0) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + ts->tv_nsec = 0; if (info) { info->implementation = "time()"; info->resolution = 1.0; info->monotonic = 0; info->adjustable = 1; } -#endif /* !HAVE_FTIME */ + assert(_PyTimeSpec_check_consistency(ts)); + return 0; + +error: + if (!raise) + Py_FatalError("pygettimeofday() failed"); + ts->tv_sec = 0; + ts->tv_nsec = 0; + return -1; #endif /* MS_WINDOWS */ } void -_PyTime_gettimeofday(_PyTime_timeval *tp) +_PyTimeSpec_get_time(_PyTimeSpec *tp) { - pygettimeofday(tp, NULL); + /* _PyTimeSpec_Init() checked that the system clock works. */ + (void)pygettimeofday(tp, NULL, 0); } void -_PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info) +_PyTimeSpec_get_time_info(_PyTimeSpec *tp, _Py_clock_info_t *info) { - pygettimeofday(tp, info); -} - -static void -error_time_t_overflow(void) -{ - PyErr_SetString(PyExc_OverflowError, - "timestamp out of range for platform time_t"); + /* _PyTimeSpec_Init() checked that the system clock works. */ + (void)pygettimeofday(tp, info, 0); } time_t @@ -150,103 +352,110 @@ PyObject * #endif } -static int -_PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, - double denominator, _PyTime_round_t round) +int +_PyTimeSpec_from_object(_PyTimeSpec *ts, PyObject *obj, _PyTime_round_t round) { - assert(denominator <= LONG_MAX); if (PyFloat_Check(obj)) { - double d, intpart, err; - /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ - volatile double floatpart; - - d = PyFloat_AsDouble(obj); - floatpart = modf(d, &intpart); - if (floatpart < 0) { - floatpart = 1.0 + floatpart; - intpart -= 1.0; - } - - floatpart *= denominator; - if (round == _PyTime_ROUND_UP) { - if (intpart >= 0) { - floatpart = ceil(floatpart); - if (floatpart >= denominator) { - floatpart = 0.0; - intpart += 1.0; - } - } - else { - floatpart = floor(floatpart); - } - } - - *sec = (time_t)intpart; - err = intpart - (double)*sec; - if (err <= -1.0 || err >= 1.0) { - error_time_t_overflow(); + double seconds = PyFloat_AsDouble(obj); + if (seconds == -1 && PyErr_Occurred()) return -1; - } - - *numerator = (long)floatpart; - return 0; + return _PyTimeSpec_from_double_err(ts, seconds, round, 1); } else { - *sec = _PyLong_AsTime_t(obj); - if (*sec == (time_t)-1 && PyErr_Occurred()) + ts->tv_sec = _PyLong_AsTime_t(obj); + ts->tv_nsec = 0; + if (ts->tv_sec == (time_t)-1 && PyErr_Occurred()) return -1; - *numerator = 0; + assert(_PyTimeSpec_check_consistency(ts)); return 0; } } -int -_PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) +long +_PyTimeSpec_ms(const _PyTimeSpec *ts, _PyTime_round_t round) { - if (PyFloat_Check(obj)) { - double d, intpart, err; + long ms; - d = PyFloat_AsDouble(obj); + assert(_PyTimeSpec_check_consistency(ts)); + + if ((ts->tv_sec >= 0) ^ (round == _PyTime_ROUND_DOWN)) + ms = (ts->tv_nsec + (MS_IN_NS - 1)) / MS_IN_NS; + else + ms = ts->tv_nsec / MS_IN_NS; + + if (ts->tv_sec > (time_t)((LONG_MAX - ms) / SEC_IN_MS)) + return LONG_MAX; + if (ts->tv_sec < (time_t)LONG_MIN / (time_t)SEC_IN_MS) + return LONG_MIN; + + ms += ts->tv_sec * (time_t)SEC_IN_MS; + return ms; +} + +static time_t +_PyTimeSpec_to_denominator(const _PyTimeSpec *ts, + long *numerator, long denominator, + _PyTime_round_t round) +{ + time_t sec; + long divisor; + + assert(_PyTimeSpec_check_consistency(ts)); + + sec = ts->tv_sec; + + divisor = SEC_IN_NS / denominator; + + if (sec < 0) { if (round == _PyTime_ROUND_UP) { - if (d >= 0) - d = ceil(d); - else - d = floor(d); + *numerator = ts->tv_nsec / divisor; + assert(*numerator >= 0); } - (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; + else { + *numerator = (long)(ts->tv_nsec + divisor - 1) / (long)divisor; + if (*numerator >= denominator) { + sec++; + *numerator -= denominator; + } } - return 0; } else { - *sec = _PyLong_AsTime_t(obj); - if (*sec == (time_t)-1 && PyErr_Occurred()) - return -1; - return 0; + if (round == _PyTime_ROUND_UP) + *numerator = (ts->tv_nsec + divisor - 1) / divisor; + else + *numerator = ts->tv_nsec / divisor; + if (*numerator >= denominator) { + if (sec == TIME_T_MAX) { + error_time_t_overflow(); + return (time_t)-1; + } + sec++; + *numerator -= denominator; + } } + return sec; +} + +time_t +_PyTimeSpec_to_time_t(const _PyTimeSpec *ts, _PyTime_round_t round) +{ + long num; + return _PyTimeSpec_to_denominator(ts, &num, 1, round); +} + +time_t +_PyTimeSpec_to_timeval(const _PyTimeSpec *ts, + long *usec, _PyTime_round_t round) +{ + return _PyTimeSpec_to_denominator(ts, usec, SEC_IN_US, round); } int -_PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec, - _PyTime_round_t round) +_PyTimeSpec_Init(void) { - return _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round); + _PyTimeSpec ts; + /* ensure that the system clock works */ + if (pygettimeofday(&ts, NULL, 1) < 0) + return -1; + return 0; } - -int -_PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec, - _PyTime_round_t round) -{ - return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); -} - -void -_PyTime_Init() -{ - /* Do nothing. Needed to force linking. */ -}