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 Victor Stinner, adapted for py3k and extended by Alexander
Belopolsky.
This commit is contained in:
Mark Dickinson 2010-04-20 22:32:49 +00:00
parent 50eb60e6bf
commit 7c186e2a18
4 changed files with 224 additions and 6 deletions

View File

@ -220,8 +220,20 @@ Supported operations:
| | 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 @@ In addition to the operations listed above :class:`timedelta` objects support
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

View File

@ -7,7 +7,7 @@ import os
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 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
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

View File

@ -966,6 +966,10 @@ Library
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

View File

@ -1665,6 +1665,52 @@ divide_timedelta_int(PyDateTime_Delta *delta, PyObject *intobj)
return result;
}
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)
{
@ -1810,6 +1856,10 @@ delta_divide(PyObject *left, PyObject *right)
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)
@ -1817,6 +1867,100 @@ delta_divide(PyObject *left, PyObject *right)
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)
Py_INCREF(result);
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 @@ static PyNumberMethods delta_as_number = {
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 @@ static PyNumberMethods delta_as_number = {
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 */
};