From 7000e9e01bb784dfc23a70a9d736613ae83c8dad Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 9 May 2010 09:30:06 +0000 Subject: Issue #8644: Improve accuracy of timedelta.total_seconds method. (Backport of r80979 to py3k.) Thanks Alexander Belopolsky. --- Doc/library/datetime.rst | 8 ++++++-- Lib/test/test_datetime.py | 9 ++++++++- Misc/NEWS | 2 ++ Modules/datetimemodule.c | 22 +++++++++++++++++++--- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 16429de..d612e87 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -270,8 +270,12 @@ 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``. + Return the total number of seconds contained in the duration. + Equivalent to ``(td.microseconds + (td.seconds + td.days * 24 * + 3600) * 10**6) / 10**6`` computed with true division enabled. + + Note that for very large time intervals (greater than 270 years on + most platforms) this method will lose microsecond accuracy. .. versionadded:: 2.7 diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index 52387a0..4715ce1 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -2,7 +2,7 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ - +from __future__ import division import os import pickle import cPickle @@ -269,6 +269,13 @@ 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(), + ((24*3600*td.days + td.seconds)*10**6 + + td.microseconds)/10**6) def test_carries(self): t1 = timedelta(days=100, diff --git a/Misc/NEWS b/Misc/NEWS index ac28fad..6536dcc 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -200,6 +200,8 @@ Library Extension Modules ----------------- +- Issue #8644: Improved accuracy of timedelta.total_seconds(). + - Use Clang 2.7's static analyzer to find places to clean up some code. - Build the ossaudio extension on GNU/kFreeBSD. diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c index fa7ef0b..7105c69 100644 --- a/Modules/datetimemodule.c +++ b/Modules/datetimemodule.c @@ -2096,9 +2096,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 * -- cgit v0.12