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

mark.dickinson python-checkins at python.org
Wed Apr 21 00:32:49 CEST 2010


Author: mark.dickinson
Date: Wed Apr 21 00:32:49 2010
New Revision: 80290

Log:
Issue #2706:  Add support for dividing a timedelta by another timedelta.

Adds support for the three division operations:

  - timedelta / timedelta -> float
  - timedelta // timedelta -> int
  - timedelta % timedelta -> timedelta

also adds support for divmod(timedelta, timedelta).

Patch by Alexander Belopolsky.


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	Wed Apr 21 00:32:49 2010
@@ -220,8 +220,20 @@
 |                                | In general, *t1* \* i == *t1* \* (i-1) + *t1* |
 |                                | is true. (1)                                  |
 +--------------------------------+-----------------------------------------------+
-| ``t1 = t2 // i``               | The floor is computed and the remainder (if   |
-|                                | any) is thrown away. (3)                      |
+| ``f = t2 / t3``                | Division (3) of *t2* by *t3*.  Returns a      |
+|                                | :class:`float` object.                        |
++--------------------------------+-----------------------------------------------+
+| ``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)                       |
++--------------------------------+-----------------------------------------------+
+| ``t1 = t2 % t3``               | The remainder is computed as a                |
+|                                | :class:`timedelta` object. (3)                |
++--------------------------------+-----------------------------------------------+
+| ``q, r = divmod(t1, t2)``      | Computes the quotient and the remainder:      |
+|                                | ``q = t1 // t2`` (3) and ``r = t1 % t2``.     |
+|                                | q is an integer and r is a :class:`timedelta` |
+|                                | object.                                       |
 +--------------------------------+-----------------------------------------------+
 | ``+t1``                        | Returns a :class:`timedelta` object with the  |
 |                                | same value. (2)                               |
@@ -252,6 +264,12 @@
 certain additions and subtractions with :class:`date` and :class:`datetime`
 objects (see below).
 
+.. 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.
+
+
 Comparisons of :class:`timedelta` objects are supported with the
 :class:`timedelta` object representing the smaller duration considered to be the
 smaller timedelta. In order to stop mixed-type comparisons from falling back to

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	Wed Apr 21 00:32:49 2010
@@ -7,7 +7,7 @@
 import pickle
 import unittest
 
-from operator import lt, le, gt, ge, eq, ne
+from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
 
 from test import support
 
@@ -469,6 +469,58 @@
         self.assertEqual(str(t3), str(t4))
         self.assertEqual(t4.as_hours(), -1)
 
+    def test_division(self):
+        t = timedelta(hours=1, minutes=24, seconds=19)
+        second = timedelta(seconds=1)
+        self.assertEqual(t / second, 5059.0)
+        self.assertEqual(t // second, 5059)
+
+        t = timedelta(minutes=2, seconds=30)
+        minute = timedelta(minutes=1)
+        self.assertEqual(t / minute, 2.5)
+        self.assertEqual(t // minute, 2)
+
+        zerotd = timedelta(0)
+        self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
+        self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
+
+        self.assertRaises(TypeError, truediv, t, 2)
+        # note: floor division of a timedelta by an integer *is*
+        # currently permitted.
+
+    def test_remainder(self):
+        t = timedelta(minutes=2, seconds=30)
+        minute = timedelta(minutes=1)
+        r = t % minute
+        self.assertEqual(r, timedelta(seconds=30))
+
+        t = timedelta(minutes=-2, seconds=30)
+        r = t %  minute
+        self.assertEqual(r, timedelta(seconds=30))
+
+        zerotd = timedelta(0)
+        self.assertRaises(ZeroDivisionError, mod, t, zerotd)
+
+        self.assertRaises(TypeError, mod, t, 10)
+
+    def test_divmod(self):
+        t = timedelta(minutes=2, seconds=30)
+        minute = timedelta(minutes=1)
+        q, r = divmod(t, minute)
+        self.assertEqual(q, 2)
+        self.assertEqual(r, timedelta(seconds=30))
+
+        t = timedelta(minutes=-2, seconds=30)
+        q, r = divmod(t, minute)
+        self.assertEqual(q, -2)
+        self.assertEqual(r, timedelta(seconds=30))
+
+        zerotd = timedelta(0)
+        self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
+
+        self.assertRaises(TypeError, divmod, t, 10)
+
+
 #############################################################################
 # date tests
 

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Wed Apr 21 00:32:49 2010
@@ -966,6 +966,10 @@
 Extension Modules
 -----------------
 
+- Issue #2706: Allow division of a timedelta by another timedelta:
+  timedelta / timedelta, timedelta % timedelta, timedelta // timedelta
+  and divmod(timedelta, timedelta) are all supported.
+
 - Issue #8314: Fix unsigned long long bug in libffi on Sparc v8.
 
 - Issue #8300: When passing a non-integer argument to struct.pack with any

Modified: python/branches/py3k/Modules/datetimemodule.c
==============================================================================
--- python/branches/py3k/Modules/datetimemodule.c	(original)
+++ python/branches/py3k/Modules/datetimemodule.c	Wed Apr 21 00:32:49 2010
@@ -1666,6 +1666,52 @@
 }
 
 static PyObject *
+divide_timedelta_timedelta(PyDateTime_Delta *left, PyDateTime_Delta *right)
+{
+	PyObject *pyus_left;
+	PyObject *pyus_right;
+	PyObject *result;
+
+	pyus_left = delta_to_microseconds(left);
+	if (pyus_left == NULL)
+		return NULL;
+
+	pyus_right = delta_to_microseconds(right);
+	if (pyus_right == NULL)	{
+		Py_DECREF(pyus_left);
+		return NULL;
+	}
+
+	result = PyNumber_FloorDivide(pyus_left, pyus_right);
+	Py_DECREF(pyus_left);
+	Py_DECREF(pyus_right);
+	return result;
+}
+
+static PyObject *
+truedivide_timedelta_timedelta(PyDateTime_Delta *left, PyDateTime_Delta *right)
+{
+	PyObject *pyus_left;
+	PyObject *pyus_right;
+	PyObject *result;
+
+	pyus_left = delta_to_microseconds(left);
+	if (pyus_left == NULL)
+		return NULL;
+
+	pyus_right = delta_to_microseconds(right);
+	if (pyus_right == NULL)	{
+		Py_DECREF(pyus_left);
+		return NULL;
+	}
+
+	result = PyNumber_TrueDivide(pyus_left, pyus_right);
+	Py_DECREF(pyus_left);
+	Py_DECREF(pyus_right);
+	return result;
+}
+
+static PyObject *
 delta_add(PyObject *left, PyObject *right)
 {
 	PyObject *result = Py_NotImplemented;
@@ -1810,6 +1856,27 @@
 			result = divide_timedelta_int(
 					(PyDateTime_Delta *)left,
 					right);
+		else if (PyDelta_Check(right))
+			result = divide_timedelta_timedelta(
+					(PyDateTime_Delta *)left,
+					(PyDateTime_Delta *)right);
+	}
+
+	if (result == Py_NotImplemented)
+		Py_INCREF(result);
+	return result;
+}
+
+static PyObject *
+delta_truedivide(PyObject *left, PyObject *right)
+{
+	PyObject *result = Py_NotImplemented;
+
+	if (PyDelta_Check(left)) {
+		if (PyDelta_Check(right))
+			result = truedivide_timedelta_timedelta(
+					(PyDateTime_Delta *)left,
+					(PyDateTime_Delta *)right);
 	}
 
 	if (result == Py_NotImplemented)
@@ -1817,6 +1884,83 @@
 	return result;
 }
 
+static PyObject *
+delta_remainder(PyObject *left, PyObject *right)
+{
+	PyObject *pyus_left;
+	PyObject *pyus_right;
+	PyObject *pyus_remainder;
+	PyObject *remainder;
+
+	if (!PyDelta_Check(left) || !PyDelta_Check(right)) {
+		Py_INCREF(Py_NotImplemented);
+		return Py_NotImplemented;
+	}
+
+	pyus_left = delta_to_microseconds((PyDateTime_Delta *)left);
+	if (pyus_left == NULL)
+		return NULL;
+
+	pyus_right = delta_to_microseconds((PyDateTime_Delta *)right);
+	if (pyus_right == NULL) {
+		Py_DECREF(pyus_left);
+		return NULL;
+	}
+
+	pyus_remainder = PyNumber_Remainder(pyus_left, pyus_right);
+	Py_DECREF(pyus_left);
+	Py_DECREF(pyus_right);
+	if (pyus_remainder == NULL)
+		return NULL;
+
+	remainder = microseconds_to_delta(pyus_remainder);
+	if (remainder == NULL) {
+		Py_DECREF(divmod);
+		return NULL;
+	}
+
+	return remainder;
+}
+
+static PyObject *
+delta_divmod(PyObject *left, PyObject *right)
+{
+	PyObject *pyus_left;
+	PyObject *pyus_right;
+	PyObject *divmod;
+	PyObject *microseconds, *delta;
+
+	if (!PyDelta_Check(left) || !PyDelta_Check(right)) {
+		Py_INCREF(Py_NotImplemented);
+		return Py_NotImplemented;
+	}
+
+	pyus_left = delta_to_microseconds((PyDateTime_Delta *)left);
+	if (pyus_left == NULL)
+		return NULL;
+
+	pyus_right = delta_to_microseconds((PyDateTime_Delta *)right);
+	if (pyus_right == NULL) {
+		Py_DECREF(pyus_left);
+		return NULL;
+	}
+
+	divmod = PyNumber_Divmod(pyus_left, pyus_right);
+	Py_DECREF(pyus_left);
+	Py_DECREF(pyus_right);
+	if (divmod == NULL)
+		return NULL;
+
+	microseconds = PyTuple_GetItem(divmod, 1);
+	delta = microseconds_to_delta(microseconds);
+	if (delta == NULL) {
+		Py_DECREF(divmod);
+		return NULL;
+	}
+	PyTuple_SetItem(divmod, 1, delta);
+	return divmod;
+}
+
 /* Fold in the value of the tag ("seconds", "weeks", etc) component of a
  * timedelta constructor.  sofar is the # of microseconds accounted for
  * so far, and there are factor microseconds per current unit, the number
@@ -2108,8 +2252,8 @@
 	delta_add,				/* nb_add */
 	delta_subtract,				/* nb_subtract */
 	delta_multiply,				/* nb_multiply */
-	0,					/* nb_remainder */
-	0,					/* nb_divmod */
+	delta_remainder,			/* nb_remainder */
+	delta_divmod,				/* nb_divmod */
 	0,					/* nb_power */
 	(unaryfunc)delta_negative,		/* nb_negative */
 	(unaryfunc)delta_positive,		/* nb_positive */
@@ -2135,7 +2279,7 @@
 	0,					/*nb_inplace_xor*/
 	0,					/*nb_inplace_or*/
 	delta_divide,				/* nb_floor_divide */
-	0,					/* nb_true_divide */
+	delta_truedivide,			/* nb_true_divide */
 	0,					/* nb_inplace_floor_divide */
 	0,					/* nb_inplace_true_divide */
 };


More information about the Python-checkins mailing list