]> granicus.if.org Git - python/commitdiff
Issue #8644: Improve accuracy of timedelta.total_seconds, by doing intermediate
authorMark Dickinson <dickinsm@gmail.com>
Sat, 8 May 2010 14:35:02 +0000 (14:35 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Sat, 8 May 2010 14:35:02 +0000 (14:35 +0000)
computations with integer arithmetic instead of floating point.
td.total_seconds() now agrees with td / timedelta(seconds = 1).

Thanks Alexander Belopolsky for the patch.

Doc/library/datetime.rst
Lib/test/test_datetime.py
Misc/NEWS
Modules/datetimemodule.c

index 758e4d243db9825c1c3aad51a665926da2334ba2..908a7920274e99901daa6953eeeadf223c1cd9aa 100644 (file)
@@ -287,7 +287,10 @@ Instance methods:
 .. method:: timedelta.total_seconds()
 
    Return the total number of seconds contained in the duration. Equivalent to
-   ``td.microseconds / 1000000 + td.seconds + td.days * 24 * 3600``.
+   ``td / timedelta(seconds=1)``.
+
+   Note that for very large time intervals (greater than 270 years on
+   most platforms) this method will lose microsecond accuracy.
 
    .. versionadded:: 3.2
 
index a5b53fbc87c602c416470e3699dc32c0e99980b9..f65fbca3751d117bf69736fa47ce14eb4273b4d4 100644 (file)
@@ -264,6 +264,11 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
         for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
             td = timedelta(seconds=total_seconds)
             self.assertEqual(td.total_seconds(), total_seconds)
+        # Issue8644: Test that td.total_seconds() has the same
+        # accuracy as td / timedelta(seconds=1).
+        for ms in [-1, -2, -123]:
+            td = timedelta(microseconds=ms)
+            self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
 
     def test_carries(self):
         t1 = timedelta(days=100,
index 77aa054652904e550ffd713809785565ff8a6751..7a37f795314d1fd8fd13f437c4fd08149ca76098 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -1105,6 +1105,11 @@ Library
 Extension Modules
 -----------------
 
+- Issue #8644: The accuracy of td.total_seconds() has been improved (by
+  calculating with integer arithmetic instead of float arithmetic internally):
+  the result is now always correctly rounded, and is equivalent to td /
+  timedelta(seconds=1).
+
 - Issue #2706: Allow division of a timedelta by another timedelta:
   timedelta / timedelta, timedelta % timedelta, timedelta // timedelta
   and divmod(timedelta, timedelta) are all supported.
index 83dab2c93508b899c98325e68f94cbc028d8d707..d4714bdd5e32a5be26eb29b61b26413af0e53b1c 100644 (file)
@@ -2211,9 +2211,25 @@ delta_getstate(PyDateTime_Delta *self)
 static PyObject *
 delta_total_seconds(PyObject *self)
 {
-       return PyFloat_FromDouble(GET_TD_MICROSECONDS(self) / 1000000.0 +
-                                 GET_TD_SECONDS(self) +
-                                 GET_TD_DAYS(self) * 24.0 * 3600.0);
+       PyObject *total_seconds;
+       PyObject *total_microseconds;
+       PyObject *one_million;
+
+       total_microseconds = delta_to_microseconds((PyDateTime_Delta *)self);
+       if (total_microseconds == NULL)
+               return NULL;
+
+       one_million = PyLong_FromLong(1000000L);
+       if (one_million == NULL) {
+               Py_DECREF(total_microseconds);
+               return NULL;
+       }
+
+       total_seconds = PyNumber_TrueDivide(total_microseconds, one_million);
+
+       Py_DECREF(total_microseconds);
+       Py_DECREF(one_million);
+       return total_seconds;
 }
 
 static PyObject *