[Python-checkins] r81625 - in python/branches/py3k: Doc/library/datetime.rst Lib/test/test_datetime.py Misc/NEWS Modules/datetimemodule.c

alexander.belopolsky python-checkins at python.org
Mon May 31 19:33:47 CEST 2010


Author: alexander.belopolsky
Date: Mon May 31 19:33:47 2010
New Revision: 81625

Log:
Issue #1289118: datetime.timedelta objects can now be multiplied by float
and divided by float and int objects.


Modified:
   python/branches/py3k/Doc/library/datetime.rst
   python/branches/py3k/Lib/test/test_datetime.py
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/Modules/datetimemodule.c

Modified: python/branches/py3k/Doc/library/datetime.rst
==============================================================================
--- python/branches/py3k/Doc/library/datetime.rst	(original)
+++ python/branches/py3k/Doc/library/datetime.rst	Mon May 31 19:33:47 2010
@@ -220,12 +220,20 @@
 |                                | In general, *t1* \* i == *t1* \* (i-1) + *t1* |
 |                                | is true. (1)                                  |
 +--------------------------------+-----------------------------------------------+
+| ``t1 = t2 * f or t1 = f * t2`` | Delta multiplied by a float. The result is    |
+|                                | rounded to the nearest multiple of            |
+|                                | timedelta.resolution using round-half-to-even.|
++--------------------------------+-----------------------------------------------+
 | ``f = t2 / t3``                | Division (3) of *t2* by *t3*.  Returns a      |
 |                                | :class:`float` object.                        |
 +--------------------------------+-----------------------------------------------+
+| ``t1 = t2 / f or t1 = t2 / i`` | Delta divided by a float or an int. The result|
+|                                | is rounded to the nearest multiple of         |
+|                                | timedelta.resolution using round-half-to-even.|
++--------------------------------+-----------------------------------------------+
 | ``t1 = t2 // i`` or            | The floor is computed and the remainder (if   |
 | ``t1 = t2 // t3``              | any) is thrown away.  In the second case, an  |
-|                                | integer is returned (3)                       |
+|                                | integer is returned. (3)                      |
 +--------------------------------+-----------------------------------------------+
 | ``t1 = t2 % t3``               | The remainder is computed as a                |
 |                                | :class:`timedelta` object. (3)                |
@@ -267,7 +275,9 @@
 .. versionadded:: 3.2
    Floor division and true division of a :class:`timedelta` object by
    another :class:`timedelta` object are now supported, as are
-   remainder operations and the :func:`divmod` function.
+   remainder operations and the :func:`divmod` function.  True
+   division and multiplication of a :class:`timedelta` object by
+   a :class:`float` object are now supported.
 
 
 Comparisons of :class:`timedelta` objects are supported with the

Modified: python/branches/py3k/Lib/test/test_datetime.py
==============================================================================
--- python/branches/py3k/Lib/test/test_datetime.py	(original)
+++ python/branches/py3k/Lib/test/test_datetime.py	Mon May 31 19:33:47 2010
@@ -25,6 +25,16 @@
 OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
 
 
+# XXX Copied from test_float.
+INF = float("inf")
+NAN = float("nan")
+
+# decorator for skipping tests on non-IEEE 754 platforms
+requires_IEEE_754 = unittest.skipUnless(
+    float.__getformat__("double").startswith("IEEE"),
+    "test requires IEEE 754 doubles")
+
+
 #############################################################################
 # module tests
 
@@ -225,6 +235,36 @@
         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))
+        for i in range(-10, 10):
+            eq((i*us/-3)//us, round(i/-3))
 
     def test_disallowed_computations(self):
         a = timedelta(42)
@@ -236,20 +276,19 @@
             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
         self.assertRaises(TypeError, lambda: zero // a)
         self.assertRaises(ZeroDivisionError, lambda: a // zero)
+        self.assertRaises(ZeroDivisionError, lambda: a / zero)
+        self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
+
+    @requires_IEEE_754
+    def test_disallowed_special(self):
+        a = timedelta(42)
+        self.assertRaises(ValueError, a.__mul__, NAN)
+        self.assertRaises(ValueError, a.__truediv__, NAN)
 
     def test_basic_attributes(self):
         days, seconds, us = 1, 7, 31
@@ -410,6 +449,19 @@
 
         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)
+
+    @requires_IEEE_754
+    def _test_overflow_special(self):
+        day = timedelta(1)
+        self.assertRaises(OverflowError, day.__mul__, INF)
+        self.assertRaises(OverflowError, day.__mul__, -INF)
+
     def test_microsecond_rounding(self):
         td = timedelta
         eq = self.assertEqual
@@ -489,7 +541,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.
 

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Mon May 31 19:33:47 2010
@@ -398,6 +398,11 @@
 Library
 -------
 
+- Issue #1289118: datetime.timedelta objects can now be multiplied by float
+  and divided by float and int objects.  Results are rounded to the nearest
+  multiple of timedelta.resolution with ties resolved using round-half-to-even
+  method.
+
 - Issue #7150: Raise OverflowError if the result of adding or subtracting
   timedelta from date or datetime falls outside of the MINYEAR:MAXYEAR range.
 

Modified: python/branches/py3k/Modules/datetimemodule.c
==============================================================================
--- python/branches/py3k/Modules/datetimemodule.c	(original)
+++ python/branches/py3k/Modules/datetimemodule.c	Mon May 31 19:33:47 2010
@@ -152,6 +152,25 @@
     return (long)x;
 }
 
+/* Nearest integer to m / n for integers m and n. Half-integer results
+ * are rounded to even.
+ */
+static PyObject *
+divide_nearest(PyObject *m, PyObject *n)
+{
+    PyObject *result;
+    PyObject *temp;
+
+    temp = _PyLong_Divmod_Near(m, n);
+    if (temp == NULL)
+        return NULL;
+    result = PyTuple_GET_ITEM(temp, 0);
+    Py_INCREF(result);
+    Py_DECREF(temp);
+
+    return result;
+}
+
 /* ---------------------------------------------------------------------------
  * General calendrical helper functions
  */
@@ -1648,6 +1667,37 @@
 }
 
 static PyObject *
+multiply_float_timedelta(PyObject *floatobj, PyDateTime_Delta *delta)
+{
+    PyObject *result = NULL;
+    PyObject *pyus_in = NULL, *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);
+    pyus_in = NULL;
+    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(pyus_in);
+    Py_XDECREF(ratio);
+
+    return result;
+}
+
+static PyObject *
 divide_timedelta_int(PyDateTime_Delta *delta, PyObject *intobj)
 {
     PyObject *pyus_in;
@@ -1715,6 +1765,55 @@
 }
 
 static PyObject *
+truedivide_timedelta_float(PyDateTime_Delta *delta, PyObject *f)
+{
+    PyObject *result = NULL;
+    PyObject *pyus_in = NULL, *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);
+    pyus_in = NULL;
+    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(pyus_in);
+    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;
@@ -1838,10 +1937,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);
@@ -1880,6 +1985,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)


More information about the Python-checkins mailing list