diff --git a/Include/pytime.h b/Include/pytime.h --- a/Include/pytime.h +++ b/Include/pytime.h @@ -45,6 +45,15 @@ typedef unsigned PY_LONG_LONG _PyTime_fr typedef size_t _PyTime_fraction_t; #endif +PyAPI_FUNC(PyObject*) _PyLong_FromTime_t(time_t value); + +#if defined(HAVE_LONG_LONG) +# define _PyLong_FromTimeFraction_t PyLong_FromLongLong +#else +# define _PyLong_FromTimeFraction_t PyLong_FromSize_t +#endif + + typedef struct { /* timestamp = seconds + numerator / denominator */ @@ -56,21 +65,28 @@ typedef struct /* the timestamp resolution is 1/divisor */ } _PyTime_t; -/* Change the denominator of a timestamp */ -PyAPI_FUNC(void) _PyTime_SetDenominator( +/* Change the denominator of a timestamp. + Return 0 on success, raise a OverflowError and return -1 if the number of + seconds doesn't fit into time_t. */ +PyAPI_FUNC(int) _PyTime_SetDenominator( _PyTime_t *ts, _PyTime_fraction_t new_denominator); -PyAPI_FUNC(PyObject*) _PyLong_FromTime_t(time_t value); +PyAPI_FUNC(void) _PyTime_FromTime_t(_PyTime_t *ts, time_t seconds); -PyAPI_FUNC(void) _PyTime_FromTime_t(_PyTime_t *ts, time_t seconds); +/* Convert a timestamp to time_t (number of seconds). + Raise a OverflowError and return (time_t)-1 if the number of seconds doesn't + fit into time_t. */ +PyAPI_FUNC(time_t) _PyTime_AsTime_t(const _PyTime_t *ts); #ifdef HAVE_GETTIMEOFDAY /* Set a timestamp from a timeval structure (microsecond resolution) */ PyAPI_FUNC(void) _PyTime_FromTimeval(_PyTime_t *ts, struct timeval *tv); -/* Convert a timestamp to a timeval structure (microsecond resolution) */ -PyAPI_FUNC(void) _PyTime_AsTimeval( +/* Convert a timestamp to a timeval structure (microsecond resolution). + Return 0 on success, raise a OverflowError and return -1 if the number of + seconds doesn't fit into time_t. */ +PyAPI_FUNC(int) _PyTime_AsTimeval( const _PyTime_t *ts, struct timeval *tv); #endif @@ -79,8 +95,10 @@ PyAPI_FUNC(void) _PyTime_AsTimeval( /* Set a timestamp from a timespec structure (nanosecond resolution) */ PyAPI_FUNC(void) _PyTime_FromTimespec(_PyTime_t *ts, struct timespec *tv); -/* Convert a timestamp to a timespec structure (nanosecond resolution) */ -PyAPI_FUNC(void) _PyTime_AsTimespec( +/* Convert a timestamp to a timespec structure (nanosecond resolution). + Return 0 on success, raise a OverflowError and return -1 if the number of + seconds doesn't fit into time_t. */ +PyAPI_FUNC(int) _PyTime_AsTimespec( const _PyTime_t *ts, struct timespec *tv); #endif diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -1,8 +1,10 @@ +import decimal import locale import platform import sys import sysconfig from test import support +import _testcapi import time import unittest @@ -346,7 +348,6 @@ class TimeTestCase(unittest.TestCase): self.assertAlmostEqual(dt, 0.1, delta=0.2) def test_timestamp(self): - import decimal calls = [ (time.time,), (time.mktime, time.localtime()), @@ -521,13 +522,74 @@ class TestAsctime4dyear(_TestAsctimeYear class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear): pass +class TestPytime(unittest.TestCase): + def setUp(self): + self.decode = _testcapi.pytime_fromobject + self.encode = _testcapi.pytime_convert + + def test_int(self): + self.assertEqual(self.decode(-42), + (-42, 0, 1)) + self.assertEqual(self.decode(42), + (42, 0, 1)) + self.assertEqual(self.encode((3, 14, 100), int), + 3) + self.assertEqual(self.encode((8, 4, 10), int), + 8) + self.assertEqual(self.encode((8, 5, 10), int), + 8) + self.assertEqual(self.encode((8, 7, 10), int), + 8) + self.assertEqual(self.encode((-2, 0, 100), int), + -2) + self.assertEqual(self.encode((-2, 3, 10), int), + -2) + self.assertEqual(self.encode((-2, 5, 10), int), + -2) + self.assertEqual(self.encode((-2, 7, 10), int), + -2) + for value in (-5, 5): + ts = self.decode(value) + self.assertEqual(self.encode(ts, int), value) + + def test_float(self): + for value in (-1.9, 1.1, 1.9, 3.14): + ts = self.decode(value) + self.assertEqual(self.encode(ts, type(value)), value) + self.assertEqual(self.decode(-3.14), + (-4, 1846835937, 2147483648)) + self.assertEqual(self.decode(3.14), + (3, 300647710, 2147483648)) + self.assertEqual(self.decode(2+1e-9), + (2, 2, 2147483648)) + self.assertEqual(self.encode((3, 14, 100), float), + 3.14) + + def test_decimal(self): + self.assertEqual(self.decode(decimal.Decimal('3.14')), + (3, 140000000, 1000000000)) + self.assertEqual(self.decode(decimal.Decimal('-3.14')), + (-4, 860000000, 1000000000)) + self.assertEqual(self.decode(decimal.Decimal('2.000000001')), + (2, 1, 1000000000)) + self.assertEqual(self.encode((3, 14, 100), decimal.Decimal), + decimal.Decimal('3.14')) + for value in (decimal.Decimal('3.14'), decimal.Decimal('-1.9')): + ts = self.decode(value) + self.assertEqual(self.encode(ts, type(value)), value) + + def test_overflow(self): + self.assertRaises(OverflowError, self.decode, 2**100) + def test_main(): support.run_unittest( TimeTestCase, TestLocale, TestAsctime4dyear, - TestStrftime4dyear) + TestStrftime4dyear, + TestPytime + ) if __name__ == "__main__": test_main() diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2412,6 +2412,50 @@ run_in_subinterp(PyObject *self, PyObjec return PyLong_FromLong(r); } +static PyObject* +test_pytime_from_object(PyObject *self, PyObject *obj) +{ + _PyTime_t ts; + PyObject *seconds, *numerator, *denominator; + + if (_PyTime_FromObject(&ts, obj) == -1) + return NULL; + + seconds = _PyLong_FromTime_t(ts.seconds); + numerator = _PyLong_FromTimeFraction_t(ts.numerator); + denominator = _PyLong_FromTimeFraction_t(ts.denominator); + if (PyErr_Occurred()) { + Py_XDECREF(seconds); + Py_XDECREF(numerator); + Py_XDECREF(denominator); + return NULL; + } + return Py_BuildValue("(NNN)", seconds, numerator, denominator); +} + +static PyObject* +test_pytime_convert(PyObject *self, PyObject *args) +{ + _PyTime_t ts; + long seconds; + unsigned PY_LONG_LONG numerator, denominator; + PyObject *timestamp; + + if (!PyArg_ParseTuple(args, "(lKK)O:pytime_convert", + &seconds, &numerator, &denominator, ×tamp)) + return NULL; + + if (denominator == 0) { + PyErr_SetString(PyExc_ValueError, "denominator cannot be zero"); + return NULL; + } + + ts.seconds = (time_t)seconds; + ts.numerator = numerator; + ts.denominator = denominator; + return _PyTime_Convert(&ts, timestamp); +} + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, @@ -2502,6 +2546,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_fromobject", test_pytime_from_object, METH_O}, + {"pytime_convert", test_pytime_convert, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3694,46 +3694,55 @@ done: else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { PyErr_SetString(PyExc_TypeError, "utime() arg 2 must be a tuple (atime, mtime)"); - Py_DECREF(opath); - return NULL; + goto error; } else { - if (_PyTime_FromObject(&atime, PyTuple_GET_ITEM(arg, 0)) == -1) { - Py_DECREF(opath); - return NULL; - } - if (_PyTime_FromObject(&mtime, PyTuple_GET_ITEM(arg, 1)) == -1) { - Py_DECREF(opath); - return NULL; - } - } - - Py_BEGIN_ALLOW_THREADS + if (_PyTime_FromObject(&atime, PyTuple_GET_ITEM(arg, 0)) == -1) + goto error; + if (_PyTime_FromObject(&mtime, PyTuple_GET_ITEM(arg, 1)) == -1) + goto error; + } + { #ifdef HAVE_UTIMENSAT struct timespec buf[2]; - _PyTime_AsTimespec(&atime, &buf[0]); - _PyTime_AsTimespec(&mtime, &buf[1]); + if (_PyTime_AsTimespec(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimespec(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS res = utimensat(AT_FDCWD, path, buf, 0); + Py_END_ALLOW_THREADS #elif defined(HAVE_UTIMES) struct timeval buf[2]; - _PyTime_AsTimeval(&atime, &buf[0]); - _PyTime_AsTimeval(&mtime, &buf[1]); + if (_PyTime_AsTimeval(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimeval(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS res = utimes(path, buf); + Py_END_ALLOW_THREADS #elif defined(HAVE_UTIME_H) /* XXX should define struct utimbuf instead, above */ struct utimbuf buf; - buf.actime = atime; - buf.modtime = mtime; + buf.actime = _PyTime_AsTime_t(&atime); + buf.modtime = _PyTime_AsTime_t(&mtime); + if (PyErr_Occurred()) + goto error; + Py_BEGIN_ALLOW_THREADS res = utime(path, &buf); + Py_END_ALLOW_THREADS #else time_t buf[2]; - buf[0] = atime; - buf[1] = mtime; + buf[0] = _PyTime_AsTime_t(&atime); + buf[1] = _PyTime_AsTime_t(&mtime); + if (PyErr_Occurred()) + goto error; + Py_BEGIN_ALLOW_THREADS res = utime(path, buf); -#endif - } - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS +#endif + } if (res < 0) { return posix_error_with_allocated_filename(opath); @@ -3741,6 +3750,10 @@ done: Py_DECREF(opath); Py_INCREF(Py_None); return Py_None; + +error: + Py_DECREF(opath); + return NULL; #undef UTIME_EXTRACT #endif /* MS_WINDOWS */ } @@ -3780,21 +3793,27 @@ posix_futimes(PyObject *self, PyObject * if (_PyTime_FromObject(&mtime, PyTuple_GET_ITEM(arg, 1)) == -1) { return NULL; } - Py_BEGIN_ALLOW_THREADS { #ifdef HAVE_FUTIMENS struct timespec buf[2]; - _PyTime_AsTimespec(&atime, &buf[0]); - _PyTime_AsTimespec(&mtime, &buf[1]); + if (_PyTime_AsTimespec(&atime, &buf[0]) == -1) + return NULL; + if (_PyTime_AsTimespec(&mtime, &buf[1]) == -1) + return NULL; + Py_BEGIN_ALLOW_THREADS res = futimens(fd, buf); + Py_END_ALLOW_THREADS #else struct timeval buf[2]; - _PyTime_AsTimeval(&atime, &buf[0]); - _PyTime_AsTimeval(&mtime, &buf[1]); + if (_PyTime_AsTimeval(&atime, &buf[0]) == -1) + return NULL; + if (_PyTime_AsTimeval(&mtime, &buf[1]) == -1) + return NULL; + Py_BEGIN_ALLOW_THREADS res = futimes(fd, buf); + Py_END_ALLOW_THREADS #endif } - Py_END_ALLOW_THREADS } if (res < 0) return posix_error(); @@ -3833,34 +3852,41 @@ posix_lutimes(PyObject *self, PyObject * return NULL; } else { - if (_PyTime_FromObject(&atime, PyTuple_GET_ITEM(arg, 0)) == -1) { - Py_DECREF(opath); - return NULL; - } - if (_PyTime_FromObject(&mtime, PyTuple_GET_ITEM(arg, 1)) == -1) { - Py_DECREF(opath); - return NULL; - } - Py_BEGIN_ALLOW_THREADS + if (_PyTime_FromObject(&atime, PyTuple_GET_ITEM(arg, 0)) == -1) + goto error; + if (_PyTime_FromObject(&mtime, PyTuple_GET_ITEM(arg, 1)) == -1) + goto error; + { #ifdef HAVE_UTIMENSAT struct timespec buf[2]; - _PyTime_AsTimespec(&atime, &buf[0]); - _PyTime_AsTimespec(&mtime, &buf[1]); + if (_PyTime_AsTimespec(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimespec(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS res = utimensat(AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); + Py_END_ALLOW_THREADS #else struct timeval buf[2]; - _PyTime_AsTimeval(&atime, &buf[0]); - _PyTime_AsTimeval(&mtime, &buf[1]); + if (_PyTime_AsTimeval(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimeval(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS res = lutimes(path, buf); + Py_END_ALLOW_THREADS #endif } - Py_END_ALLOW_THREADS } Py_DECREF(opath); if (res < 0) return posix_error(); Py_RETURN_NONE; + +error: + Py_DECREF(opath); + return NULL; } #endif @@ -9721,36 +9747,42 @@ posix_futimesat(PyObject *self, PyObject return NULL; } else { - if (_PyTime_FromObject(&atime, PyTuple_GET_ITEM(arg, 0)) == -1) { - Py_DECREF(opath); - return NULL; - } - if (_PyTime_FromObject(&mtime, PyTuple_GET_ITEM(arg, 1)) == -1) { - Py_DECREF(opath); - return NULL; - } - - Py_BEGIN_ALLOW_THREADS + if (_PyTime_FromObject(&atime, PyTuple_GET_ITEM(arg, 0)) == -1) + goto error; + if (_PyTime_FromObject(&mtime, PyTuple_GET_ITEM(arg, 1)) == -1) + goto error; + { #ifdef HAVE_UTIMENSAT struct timespec buf[2]; - _PyTime_AsTimespec(&atime, &buf[0]); - _PyTime_AsTimespec(&mtime, &buf[1]); + if (_PyTime_AsTimespec(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimespec(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS res = utimensat(dirfd, path, buf, 0); + Py_END_ALLOW_THREADS #else struct timeval buf[2]; - _PyTime_AsTimeval(&atime, &buf[0]); - _PyTime_AsTimeval(&mtime, &buf[1]); + if (_PyTime_AsTimeval(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimeval(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS res = futimesat(dirfd, path, buf); + Py_END_ALLOW_THREADS #endif } - Py_END_ALLOW_THREADS } Py_DECREF(opath); if (res < 0) { return posix_error(); } Py_RETURN_NONE; + +error: + Py_DECREF(opath); + return NULL; } #endif diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -204,44 +204,32 @@ select_select(PyObject *self, PyObject * #endif /* SELECT_USES_HEAP */ PyObject *ifdlist, *ofdlist, *efdlist; PyObject *ret = NULL; - PyObject *tout = Py_None; fd_set ifdset, ofdset, efdset; - double timeout; + PyObject *timeout_obj = Py_None; + _PyTime_t timeout; struct timeval tv, *tvp; - long seconds; int imax, omax, emax, max; int n; /* convert arguments */ if (!PyArg_UnpackTuple(args, "select", 3, 4, - &ifdlist, &ofdlist, &efdlist, &tout)) + &ifdlist, &ofdlist, &efdlist, &timeout_obj)) return NULL; - if (tout == Py_None) + if (timeout_obj == Py_None) tvp = (struct timeval *)0; - else if (!PyNumber_Check(tout)) { - PyErr_SetString(PyExc_TypeError, - "timeout must be a float or None"); - return NULL; - } else { - timeout = PyFloat_AsDouble(tout); - if (timeout == -1 && PyErr_Occurred()) + if (_PyTime_FromObject(&timeout, timeout_obj) == -1) return NULL; - if (timeout > (double)LONG_MAX) { - PyErr_SetString(PyExc_OverflowError, - "timeout period too long"); + + if (_PyTime_AsTimeval(&timeout, &tv) == -1) return NULL; - } - if (timeout < 0) { + + if (tv.tv_sec < 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; } diff --git a/Python/pytime.c b/Python/pytime.c --- a/Python/pytime.c +++ b/Python/pytime.c @@ -23,25 +23,39 @@ extern int ftime(struct timeb *); static __int64 secs_between_epochs = 11644473600; #endif +#if SIZEOF_TIME_T == SIZEOF_LONG +# define PY_TIME_T_MAX LONG_MAX +#elif SIZEOF_TIME_T == SIZEOF_LONG_LONG +# define PY_TIME_T_MAX PY_LLONG_MAX +#else +# error "Unknown size_t size" +#endif + + #define MICROSECONDS 1000000 #define NANOSECONDS 1000000000 -void +int _PyTime_SetDenominator(_PyTime_t *ts, _PyTime_fraction_t new_denominator) { _PyTime_fraction_t k; + int ret = 0; assert(new_denominator != 0); if (ts->denominator == new_denominator) - return; + return 0; if (ts->numerator) { if (ts->numerator > ts->denominator) { k = ts->numerator / ts->denominator; - /* ignore integer overflow */ - ts->seconds += k; + if (k > (_PyTime_fraction_t)(PY_TIME_T_MAX - ts->seconds)) { + PyErr_SetString(PyExc_OverflowError, + "number of seconds doesn't fit in time_t"); + return -1; + } + ts->seconds += (time_t)k; ts->numerator %= ts->denominator; } if (new_denominator >= ts->denominator) { @@ -54,6 +68,26 @@ _PyTime_SetDenominator(_PyTime_t *ts, } } ts->denominator = new_denominator; + return ret; +} + +time_t +_PyTime_AsTime_t(const _PyTime_t *ts) +{ + _PyTime_fraction_t k; + time_t seconds; + + seconds = ts->seconds; + if (ts->numerator > ts->denominator) { + k = ts->numerator / ts->denominator; + if (k > (_PyTime_fraction_t)(PY_TIME_T_MAX - seconds)) { + PyErr_SetString(PyExc_OverflowError, + "number of seconds doesn't fit in time_t"); + return (time_t)-1; + } + seconds += (time_t)k; + } + return seconds; } void @@ -73,14 +107,16 @@ _PyTime_FromTimeval(_PyTime_t *ts, struc ts->denominator = MICROSECONDS; } -void +int _PyTime_AsTimeval(const _PyTime_t *ts, struct timeval *tv) { _PyTime_t microsec; microsec = *ts; - _PyTime_SetDenominator(µsec, MICROSECONDS); + if (_PyTime_SetDenominator(µsec, MICROSECONDS) == -1) + return -1; tv->tv_sec = microsec.seconds; tv->tv_usec = (long)microsec.numerator; + return 0; } #endif @@ -93,14 +129,16 @@ _PyTime_FromTimespec(_PyTime_t *ts, stru ts->denominator = NANOSECONDS; } -void +int _PyTime_AsTimespec(const _PyTime_t *ts, struct timespec *tv) { _PyTime_t nanosec; nanosec = *ts; - _PyTime_SetDenominator(&nanosec, NANOSECONDS); + if (_PyTime_SetDenominator(&nanosec, NANOSECONDS) == -1) + return -1; tv->tv_sec = nanosec.seconds; tv->tv_nsec = (long)nanosec.numerator; + return 0; } #endif @@ -195,12 +233,6 @@ _PyLong_AsTime_t(PyObject *obj) #endif } -#if defined(HAVE_LONG_LONG) -# define _PyLong_FromTimeFraction_t PyLong_FromLongLong -#else -# define _PyLong_FromTimeFraction_t PyLong_FromSize_t -#endif - /* Convert a timestamp to a PyFloat object */ static PyObject* _PyTime_AsFloat(const _PyTime_t *ts) @@ -220,6 +252,7 @@ _PyTime_AsLong(const _PyTime_t *ts) a = _PyLong_FromTime_t(ts->seconds); if (a == NULL) return NULL; + /* round to nearest with ties going to nearest even integer */ b = _PyLong_FromTimeFraction_t(ts->numerator / ts->denominator); if (b == NULL) { Py_DECREF(a); @@ -402,6 +435,29 @@ error: return NULL; } +int +is_decimal_type(PyObject *obj) +{ + PyObject *module, *name; + _Py_IDENTIFIER(__name__); + _Py_IDENTIFIER(__module__); + + if (!PyType_Check(obj)) + return 0; + + module = _PyObject_GetAttrId(obj, &PyId___module__); + name = _PyObject_GetAttrId(obj, &PyId___name__); + if (module != NULL && PyUnicode_Check(module) + && name != NULL && PyUnicode_Check(name)) { + if (PyUnicode_CompareWithASCIIString(module, "decimal") == 0 + && PyUnicode_CompareWithASCIIString(name, "Decimal") == 0) + return 1; + } + else + PyErr_Clear(); + return 0; +} + PyObject* _PyTime_Convert(const _PyTime_t *ts, PyObject *format) { @@ -413,22 +469,8 @@ _PyTime_Convert(const _PyTime_t *ts, PyO if ((PyTypeObject *)format == &PyLong_Type) return _PyTime_AsLong(ts); - if (PyType_Check(format)) { - PyObject *module, *name; - _Py_IDENTIFIER(__name__); - _Py_IDENTIFIER(__module__); - - module = _PyObject_GetAttrId(format, &PyId___module__); - name = _PyObject_GetAttrId(format, &PyId___name__); - if (module != NULL && PyUnicode_Check(module) - && name != NULL && PyUnicode_Check(name)) { - if (PyUnicode_CompareWithASCIIString(module, "decimal") == 0 - && PyUnicode_CompareWithASCIIString(name, "Decimal") == 0) - return _PyTime_AsDecimal(ts); - } - else - PyErr_Clear(); - } + if (is_decimal_type(format)) + return _PyTime_AsDecimal(ts); PyErr_Format(PyExc_ValueError, "Unknown timestamp format: %R", format); return NULL; @@ -438,24 +480,49 @@ int _PyTime_FromObject(_PyTime_t *ts, PyObject *obj) { time_t seconds; - double d, mod; + double d, mod, intpart; + int exp; - d = PyFloat_AsDouble(obj); - if (!PyErr_Occurred()) { + if (PyFloat_Check(obj)) { + d = PyFloat_AsDouble(obj); + if (PyErr_Occurred()) + return -1; ts->seconds = (time_t)d; - mod = fmod(d, 1.0); + mod = modf(d, &intpart); + frexp(d, &exp); + if (mod < 0.0 && mod != 0.0) { + mod += 1.0; + ts->seconds -= 1; + } + printf("exp: %i", exp); + exp = 53-exp; + exp = Py_MIN(exp, 0); + exp = Py_MAX(exp, 53); + printf(" -> %i\n", exp); + mod = ldexp(mod, exp); + ts->numerator = (_PyTime_fraction_t)mod; + ts->denominator = (_PyTime_fraction_t)1 << exp; + } + else if (is_decimal_type((PyObject*)Py_TYPE(obj))) { + d = PyFloat_AsDouble(obj); + if (PyErr_Occurred()) + return -1; + ts->seconds = (time_t)d; + mod = modf(d, &intpart); + if (mod < 0.0 && mod != 0.0) { + mod += 1.0; + ts->seconds -= 1; + } mod *= (double)NANOSECONDS; - ts->numerator = (long)mod; + ts->numerator = (long)(mod + 0.5); ts->denominator = NANOSECONDS; - return 0; } - else - PyErr_Clear(); - - seconds = _PyLong_AsTime_t(obj); - if (seconds == (time_t)-1 && PyErr_Occurred()) - return -1; - _PyTime_FromTime_t(ts, seconds); + else { + seconds = _PyLong_AsTime_t(obj); + if (seconds == (time_t)-1 && PyErr_Occurred()) + return -1; + _PyTime_FromTime_t(ts, seconds); + } return 0; }