bpo-37642: Update acceptable offsets in timezone (GH-14878)
This fixes an inconsistency between the Python and C implementations of
the datetime module. The pure python version of the code was not
accepting offsets greater than 23:59 but less than 24:00. This is an
accidental legacy of the original implementation, which was put in place
before tzinfo allowed sub-minute time zone offsets.
GH-14878
diff --git a/Lib/datetime.py b/Lib/datetime.py
index d4c7a1f..0adf1dd 100644
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -2269,7 +2269,7 @@
raise TypeError("fromutc() argument must be a datetime instance"
" or None")
- _maxoffset = timedelta(hours=23, minutes=59)
+ _maxoffset = timedelta(hours=24, microseconds=-1)
_minoffset = -_maxoffset
@staticmethod
@@ -2293,8 +2293,11 @@
return f'UTC{sign}{hours:02d}:{minutes:02d}'
timezone.utc = timezone._create(timedelta(0))
-timezone.min = timezone._create(timezone._minoffset)
-timezone.max = timezone._create(timezone._maxoffset)
+# bpo-37642: These attributes are rounded to the nearest minute for backwards
+# compatibility, even though the constructor will accept a wider range of
+# values. This may change in the future.
+timezone.min = timezone._create(-timedelta(hours=23, minutes=59))
+timezone.max = timezone._create(timedelta(hours=23, minutes=59))
_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
# Some time zone algebra. For a datetime x, let
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 99b620c..d0101c9 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -388,6 +388,31 @@
tz_copy = copy.deepcopy(tz)
self.assertIs(tz_copy, tz)
+ def test_offset_boundaries(self):
+ # Test timedeltas close to the boundaries
+ time_deltas = [
+ timedelta(hours=23, minutes=59),
+ timedelta(hours=23, minutes=59, seconds=59),
+ timedelta(hours=23, minutes=59, seconds=59, microseconds=999999),
+ ]
+ time_deltas.extend([-delta for delta in time_deltas])
+
+ for delta in time_deltas:
+ with self.subTest(test_type='good', delta=delta):
+ timezone(delta)
+
+ # Test timedeltas on and outside the boundaries
+ bad_time_deltas = [
+ timedelta(hours=24),
+ timedelta(hours=24, microseconds=1),
+ ]
+ bad_time_deltas.extend([-delta for delta in bad_time_deltas])
+
+ for delta in bad_time_deltas:
+ with self.subTest(test_type='bad', delta=delta):
+ with self.assertRaises(ValueError):
+ timezone(delta)
+
#############################################################################
# Base class for testing a particular aspect of timedelta, time, date and