From 13f93325d3fe27722a24167aa14647848d8681f4 Mon Sep 17 00:00:00 2001
From: Jeff Epler <jepler@gmail.com>
Date: Mon, 22 Mar 2021 10:48:14 -0500
Subject: [PATCH] total_seconds: Fix precision problem

Since CircuitPython floats have very small precision, the "total_seconds"
method was not very useful for large timedeltas.

Instead of always returning a float, use pure-integer arithmetic (and
return an int) if either:
 * the length of time is big (1<<21 is 2097152 seconds or about 24 days)
 * the number of microseconds is zero

Otherwise, for small values with nonzero microseconds, use floating-point
math.

The cut-off point was chosen because in CircuitPython float arithmetic,
2097151.0+0.5 is different from 2097151.0, but 2097152.0+0.5 and
2097152.0 are *the same float*.
---
 adafruit_datetime.py | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/adafruit_datetime.py b/adafruit_datetime.py
index cb4fef9..f3c442f 100755
--- a/adafruit_datetime.py
+++ b/adafruit_datetime.py
@@ -429,9 +429,15 @@ def microseconds(self):
     # Instance methods
     def total_seconds(self):
         """Return the total number of seconds contained in the duration."""
-        return (
-            (self._days * 86400 + self._seconds) * 10 ** 6 + self._microseconds
-        ) / 10 ** 6
+        # If the duration is less than a threshold duration, and microseconds
+        # is nonzero, then the result is a float.  Otherwise, the result is a
+        # (possibly long) integer.  This differs from standard Python where the
+        # result is always a float, because the precision of CircuitPython
+        # floats is considerably smaller than on standard Python.
+        seconds = self._days * 86400 + self._seconds
+        if self._microseconds != 0 and abs(seconds) < (1 << 21):
+            seconds += self._microseconds / 10 ** 6
+        return seconds
 
     def __repr__(self):
         args = []