Commit 7c186e2a authored by Mark Dickinson's avatar Mark Dickinson

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.
parent 50eb60e6
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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,27 @@ 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)
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 @@ delta_divide(PyObject *left, PyObject *right)
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 */
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment