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* \* (i1) + *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 roundhalftoeven.
++++
 ``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 roundhalftoeven.
++++
 ``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 nonIEEE 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: ia)
 # 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__, 1e20)
+ self.assertRaises(OverflowError, day.__truediv__, 1e10)
+ self.assertRaises(OverflowError, day.__truediv__, 9e10)
+
+ @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 roundhalftoeven
+ 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. Halfinteger 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)