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:
parent
50eb60e6bf
commit
7c186e2a18
|
@ -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,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 */
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue