Index: Lib/test/test_datetime.py =================================================================== --- Lib/test/test_datetime.py (revision 81495) +++ Lib/test/test_datetime.py (working copy) @@ -225,7 +225,35 @@ eq(c//1000, td(0, 0, 1)) eq(a//10, td(0, 7*24*360)) eq(a//3600000, td(0, 0, 7*24*1000)) + eq(a/0.5, td(14)) + eq(b/0.5, td(0, 120)) + eq(a/7, td(1)) + eq(b/10, td(0, 6)) + eq(c/1000, td(0, 0, 1)) + eq(a/10, td(0, 7*24*360)) + eq(a/3600000, td(0, 0, 7*24*1000)) + # Multiplication by float + us = td(microseconds=1) + eq((3*us) * 0.5, 2*us) + eq((5*us) * 0.5, 2*us) + eq(0.5 * (3*us), 2*us) + eq(0.5 * (5*us), 2*us) + eq((-3*us) * 0.5, -2*us) + eq((-5*us) * 0.5, -2*us) + + # Division by int and float + eq((3*us) / 2, 2*us) + eq((5*us) / 2, 2*us) + eq((-3*us) / 2.0, -2*us) + eq((-5*us) / 2.0, -2*us) + eq((3*us) / -2, -2*us) + eq((5*us) / -2, -2*us) + eq((3*us) / -2.0, -2*us) + eq((5*us) / -2.0, -2*us) + for i in range(-10, 10): + eq((i*us/3)//us, round(i/3)) + def test_disallowed_computations(self): a = timedelta(42) @@ -236,15 +264,6 @@ self.assertRaises(TypeError, lambda: i+a) self.assertRaises(TypeError, lambda: i-a) - # Mul/div by float isn't supported. - x = 2.3 - self.assertRaises(TypeError, lambda: a*x) - self.assertRaises(TypeError, lambda: x*a) - self.assertRaises(TypeError, lambda: a/x) - self.assertRaises(TypeError, lambda: x/a) - self.assertRaises(TypeError, lambda: a // x) - self.assertRaises(TypeError, lambda: x // a) - # Division of int by timedelta doesn't make sense. # Division by zero doesn't make sense. zero = 0 @@ -410,6 +429,13 @@ self.assertRaises(OverflowError, lambda: -timedelta.max) + day = timedelta(1) + self.assertRaises(OverflowError, day.__mul__, 10**9) + self.assertRaises(OverflowError, day.__mul__, 1e9) + self.assertRaises(OverflowError, day.__truediv__, 1e-20) + self.assertRaises(OverflowError, day.__truediv__, 1e-10) + self.assertRaises(OverflowError, day.__truediv__, 9e-10) + def test_microsecond_rounding(self): td = timedelta eq = self.assertEqual @@ -489,7 +515,7 @@ self.assertRaises(ZeroDivisionError, truediv, t, zerotd) self.assertRaises(ZeroDivisionError, floordiv, t, zerotd) - self.assertRaises(TypeError, truediv, t, 2) + # self.assertRaises(TypeError, truediv, t, 2) # note: floor division of a timedelta by an integer *is* # currently permitted. Index: Modules/datetimemodule.c =================================================================== --- Modules/datetimemodule.c (revision 81495) +++ Modules/datetimemodule.c (working copy) @@ -151,6 +151,87 @@ return (long)x; } +/* Nearest integer to m / n for integers m and n. Half-integer results + * are rounded to even. Equivalent to the following Python code: + * + * def divide_nearest(m, n): + * q, r = divmod(m, n) + * # round-half-to-even + * if 2*r > n or 2*r == n and q % 2 == 1: + * q += 1 + * return q + */ +static PyObject * +divide_nearest(PyObject *m, PyObject *n) +{ + PyObject *q = NULL; + PyObject *r = NULL, *temp; + PyObject *two = NULL, *rx2 = NULL; + int incr = 0, cmp; + + temp = PyNumber_Divmod(m, n); + if (temp == NULL) + return NULL; + q = PyTuple_GET_ITEM(temp, 0); + r = PyTuple_GET_ITEM(temp, 1); + Py_INCREF(q); + Py_INCREF(r); + Py_DECREF(temp); + two = PyLong_FromLong(2); + if (two == NULL) + goto error; + rx2 = PyNumber_Multiply(r, two); + if (rx2 == NULL) + goto error; + /* r * 2 >= n ? */ + cmp = PyObject_RichCompareBool(rx2, n, Py_GT); + if (cmp == -1) + goto error; + if (cmp) + incr = 1; + else { + /* r * 2 == n ? */ + cmp = PyObject_RichCompareBool(rx2, n, Py_EQ); + if (cmp == -1) + goto error; + if (cmp) { + PyObject *qmod2 = PyNumber_Remainder(q, two); + if (qmod2 == NULL) + goto error; + /* q % 2 == 1 ? */ + cmp = PyObject_IsTrue(qmod2); + if (cmp == -1) + goto error; + if (cmp) + incr = 1; + Py_DECREF(qmod2); + } + } + if (incr) { + PyObject *one = PyLong_FromLong(1); + if (one == NULL) + goto error; + temp = PyNumber_Add(q, one); + Py_DECREF(one); + if (temp == NULL) + goto error; + Py_DECREF(q); + q = temp; + } + Py_DECREF(r); + Py_XDECREF(two); + Py_XDECREF(rx2); + + return q; + error: + Py_DECREF(q); + Py_DECREF(r); + Py_XDECREF(two); + Py_XDECREF(rx2); + + return NULL; +} + /* --------------------------------------------------------------------------- * General calendrical helper functions */ @@ -1645,6 +1726,35 @@ } static PyObject * +multiply_float_timedelta(PyObject *floatobj, PyDateTime_Delta *delta) +{ + PyObject *result = NULL; + PyObject *pyus_in, *temp, *pyus_out; + PyObject *ratio = NULL; + + pyus_in = delta_to_microseconds(delta); + if (pyus_in == NULL) + return NULL; + ratio = PyObject_CallMethod(floatobj, "as_integer_ratio", NULL); + if (ratio == NULL) + goto error; + temp = PyNumber_Multiply(pyus_in, PyTuple_GET_ITEM(ratio, 0)); + Py_DECREF(pyus_in); + if (temp == NULL) + goto error; + pyus_out = divide_nearest(temp, PyTuple_GET_ITEM(ratio, 1)); + Py_DECREF(temp); + if (pyus_out == NULL) + goto error; + result = microseconds_to_delta(pyus_out); + Py_DECREF(pyus_out); + error: + Py_XDECREF(ratio); + + return result; +} + +static PyObject * divide_timedelta_int(PyDateTime_Delta *delta, PyObject *intobj) { PyObject *pyus_in; @@ -1712,6 +1822,53 @@ } static PyObject * +truedivide_timedelta_float(PyDateTime_Delta *delta, PyObject *f) +{ + PyObject *result = NULL; + PyObject *pyus_in, *temp, *pyus_out; + PyObject *ratio = NULL; + + pyus_in = delta_to_microseconds(delta); + if (pyus_in == NULL) + return NULL; + ratio = PyObject_CallMethod(f, "as_integer_ratio", NULL); + if (ratio == NULL) + goto error; + temp = PyNumber_Multiply(pyus_in, PyTuple_GET_ITEM(ratio, 1)); + Py_DECREF(pyus_in); + if (temp == NULL) + goto error; + pyus_out = divide_nearest(temp, PyTuple_GET_ITEM(ratio, 0)); + Py_DECREF(temp); + if (pyus_out == NULL) + goto error; + result = microseconds_to_delta(pyus_out); + Py_DECREF(pyus_out); + error: + Py_XDECREF(ratio); + + return result; +} + +static PyObject * +truedivide_timedelta_int(PyDateTime_Delta *delta, PyObject *i) +{ + PyObject *result; + PyObject *pyus_in, *pyus_out; + pyus_in = delta_to_microseconds(delta); + if (pyus_in == NULL) + return NULL; + pyus_out = divide_nearest(pyus_in, i); + Py_DECREF(pyus_in); + if (pyus_out == NULL) + return NULL; + result = microseconds_to_delta(pyus_out); + Py_DECREF(pyus_out); + + return result; +} + +static PyObject * delta_add(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; @@ -1835,10 +1992,16 @@ if (PyLong_Check(right)) result = multiply_int_timedelta(right, (PyDateTime_Delta *) left); + else if (PyFloat_Check(right)) + result = multiply_float_timedelta(right, + (PyDateTime_Delta *) left); } else if (PyLong_Check(left)) result = multiply_int_timedelta(left, - (PyDateTime_Delta *) right); + (PyDateTime_Delta *) right); + else if (PyFloat_Check(left)) + result = multiply_float_timedelta(left, + (PyDateTime_Delta *) right); if (result == Py_NotImplemented) Py_INCREF(result); @@ -1877,6 +2040,12 @@ result = truedivide_timedelta_timedelta( (PyDateTime_Delta *)left, (PyDateTime_Delta *)right); + else if (PyFloat_Check(right)) + result = truedivide_timedelta_float( + (PyDateTime_Delta *)left, right); + else if (PyLong_Check(right)) + result = truedivide_timedelta_int( + (PyDateTime_Delta *)left, right); } if (result == Py_NotImplemented)