diff -r ed73006bac42 Lib/datetime.py --- a/Lib/datetime.py Tue Mar 13 13:50:34 2012 +0100 +++ b/Lib/datetime.py Tue Mar 13 13:51:18 2012 +0100 @@ -18,6 +18,7 @@ Thanks to Tim Peters for suggesting usin import time as _time import math as _math +import decimal def _cmp(x, y): return 0 if x == y else 1 if x > y else -1 @@ -333,7 +334,7 @@ class timedelta: # Get rid of all fractions, and normalize s and us. # Take a deep breath . - if isinstance(days, float): + if isinstance(days, (float, decimal.Decimal)): dayfrac, days = _math.modf(days) daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.)) assert daysecondswhole == int(daysecondswhole) # can't overflow @@ -349,7 +350,7 @@ class timedelta: assert abs(s) <= 24 * 3600 # days isn't referenced again before redefinition - if isinstance(seconds, float): + if isinstance(seconds, (float, decimal.Decimal)): secondsfrac, seconds = _math.modf(seconds) assert seconds == int(seconds) seconds = int(seconds) @@ -373,7 +374,9 @@ class timedelta: assert abs(usdouble) < 2.1e6 # exact value not critical # secondsfrac isn't referenced again - if isinstance(microseconds, float): + if isinstance(microseconds, (float, decimal.Decimal)): + if isinstance(microseconds, decimal.Decimal): + microseconds = float(microseconds) microseconds += usdouble microseconds = round(microseconds, 0) seconds, microseconds = divmod(microseconds, 1e6) @@ -400,7 +403,7 @@ class timedelta: assert abs(microseconds) < 3.1e6 # Just a little bit of carrying possible for microseconds and seconds. - assert isinstance(microseconds, float) + assert isinstance(microseconds, (float, decimal.Decimal)) assert int(microseconds) == microseconds us = int(microseconds) seconds, us = divmod(us, 1000000) diff -r ed73006bac42 Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py Tue Mar 13 13:50:34 2012 +0100 +++ b/Lib/test/datetimetester.py Tue Mar 13 13:51:18 2012 +0100 @@ -3,8 +3,9 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ +import decimal +import pickle import sys -import pickle import unittest from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod @@ -313,6 +314,15 @@ class TestTimeDelta(HarmlessMixedCompari eq(td(seconds=0.001), td(milliseconds=1)) eq(td(milliseconds=0.001), td(microseconds=1)) + # Check Decimal args to constructor + D = decimal.Decimal + eq(td(weeks=D('1.0')/D(7)), td(days=1)) + eq(td(days=D('1.0')/D(24)), td(hours=1)) + eq(td(hours=D('1.0')/D(60)), td(minutes=1)) + eq(td(minutes=D('1.0')/D(60)), td(seconds=1)) + eq(td(seconds=D('0.001')), td(milliseconds=1)) + eq(td(milliseconds=D('0.001')), td(microseconds=1)) + def test_computations(self): eq = self.assertEqual td = timedelta diff -r ed73006bac42 Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Tue Mar 13 13:50:34 2012 +0100 +++ b/Modules/_datetimemodule.c Tue Mar 13 13:51:18 2012 +0100 @@ -2001,6 +2001,12 @@ accum(const char* tag, PyObject *sofar, { PyObject *prod; PyObject *sum; + double dnum; + double fracpart; + double intpart; + PyObject *x; + PyObject *y; + assert(num != NULL); @@ -2013,67 +2019,60 @@ accum(const char* tag, PyObject *sofar, return sum; } - if (PyFloat_Check(num)) { - double dnum; - double fracpart; - double intpart; - PyObject *x; - PyObject *y; - - /* The Plan: decompose num into an integer part and a - * fractional part, num = intpart + fracpart. - * Then num * factor == - * intpart * factor + fracpart * factor - * and the LHS can be computed exactly in long arithmetic. - * The RHS is again broken into an int part and frac part. - * and the frac part is added into *leftover. - */ - dnum = PyFloat_AsDouble(num); - if (dnum == -1.0 && PyErr_Occurred()) - return NULL; - fracpart = modf(dnum, &intpart); - x = PyLong_FromDouble(intpart); - if (x == NULL) - return NULL; - - prod = PyNumber_Multiply(x, factor); - Py_DECREF(x); - if (prod == NULL) - return NULL; - - sum = PyNumber_Add(sofar, prod); - Py_DECREF(prod); - if (sum == NULL) - return NULL; - - if (fracpart == 0.0) - return sum; - /* So far we've lost no information. Dealing with the - * fractional part requires float arithmetic, and may - * lose a little info. - */ - assert(PyLong_Check(factor)); - dnum = PyLong_AsDouble(factor); - - dnum *= fracpart; - fracpart = modf(dnum, &intpart); - x = PyLong_FromDouble(intpart); - if (x == NULL) { - Py_DECREF(sum); - return NULL; + /* The Plan: decompose num into an integer part and a + * fractional part, num = intpart + fracpart. + * Then num * factor == + * intpart * factor + fracpart * factor + * and the LHS can be computed exactly in long arithmetic. + * The RHS is again broken into an int part and frac part. + * and the frac part is added into *leftover. + */ + dnum = PyFloat_AsDouble(num); + if (dnum == -1.0 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Format(PyExc_TypeError, + "unsupported type for timedelta %s component: %s", + tag, Py_TYPE(num)->tp_name); } - - y = PyNumber_Add(sum, x); + return NULL; + } + fracpart = modf(dnum, &intpart); + x = PyLong_FromDouble(intpart); + if (x == NULL) + return NULL; + + prod = PyNumber_Multiply(x, factor); + Py_DECREF(x); + if (prod == NULL) + return NULL; + + sum = PyNumber_Add(sofar, prod); + Py_DECREF(prod); + if (sum == NULL) + return NULL; + + if (fracpart == 0.0) + return sum; + /* So far we've lost no information. Dealing with the + * fractional part requires float arithmetic, and may + * lose a little info. + */ + assert(PyLong_Check(factor)); + dnum = PyLong_AsDouble(factor); + + dnum *= fracpart; + fracpart = modf(dnum, &intpart); + x = PyLong_FromDouble(intpart); + if (x == NULL) { Py_DECREF(sum); - Py_DECREF(x); - *leftover += fracpart; - return y; + return NULL; } - PyErr_Format(PyExc_TypeError, - "unsupported type for timedelta %s component: %s", - tag, Py_TYPE(num)->tp_name); - return NULL; + y = PyNumber_Add(sum, x); + Py_DECREF(sum); + Py_DECREF(x); + *leftover += fracpart; + return y; } static PyObject *