Skip to content

Commit 6e7de86

Browse files
miss-islingtontonghuarootStanFromIreland
authored
[3.14] gh-152079: Fix _datetime.fromisoformat() mishandling a sub-second tz offset (GH-152087) (#152175)
(cherry picked from commit 6f9c76d) Co-authored-by: tonghuaroot (童话) <tonghuaroot@gmail.com> Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent 7b16d6c commit 6e7de86

3 files changed

Lines changed: 31 additions & 2 deletions

File tree

Lib/test/datetimetester.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3626,6 +3626,32 @@ def test_fromisoformat_utc(self):
36263626

36273627
self.assertIs(dt.tzinfo, timezone.utc)
36283628

3629+
def test_fromisoformat_utc_subsecond_offset(self):
3630+
# A UTC offset whose whole-second part is zero but with a non-zero
3631+
# microsecond part must be preserved, not collapsed to UTC.
3632+
for us in (1, -1, 999999, -999999):
3633+
with self.subTest(microseconds=us):
3634+
tz = timezone(timedelta(microseconds=us))
3635+
dt = self.theclass(2020, 6, 15, 12, 34, 56, tzinfo=tz)
3636+
rt = self.theclass.fromisoformat(dt.isoformat())
3637+
self.assertEqual(rt.utcoffset(), timedelta(microseconds=us))
3638+
self.assertEqual(rt, dt)
3639+
self.assertIsNot(rt.tzinfo, timezone.utc)
3640+
3641+
tz = timezone(timedelta(hours=5, minutes=30, seconds=15,
3642+
microseconds=123456))
3643+
dt = self.theclass(2020, 6, 15, 12, 34, 56, tzinfo=tz)
3644+
rt = self.theclass.fromisoformat(dt.isoformat())
3645+
self.assertEqual(rt.utcoffset(), tz.utcoffset(None))
3646+
self.assertEqual(rt, dt)
3647+
3648+
for tstr in ('2020-06-15T12:34:56+00:00',
3649+
'2020-06-15T12:34:56+00:00:00.000000',
3650+
'2020-06-15T12:34:56Z'):
3651+
with self.subTest(tstr=tstr):
3652+
self.assertIs(self.theclass.fromisoformat(tstr).tzinfo,
3653+
timezone.utc)
3654+
36293655
def test_fromisoformat_subclass(self):
36303656
class DateTimeSubclass(self.theclass):
36313657
pass
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :meth:`datetime.datetime.fromisoformat` in the C implementation dropping
2+
the sub-second part of a UTC offset whose whole-second part is zero, matching
3+
the pure-Python implementation.

Modules/_datetimemodule.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,8 +1660,8 @@ tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds)
16601660
{
16611661
PyObject *tzinfo;
16621662
if (rv == 1) {
1663-
// Create a timezone from offset in seconds (0 returns UTC)
1664-
if (tzoffset == 0) {
1663+
// Create a timezone from the offset (a zero offset returns UTC)
1664+
if (tzoffset == 0 && tz_useconds == 0) {
16651665
return Py_NewRef(CONST_UTC(NO_STATE));
16661666
}
16671667

0 commit comments

Comments
 (0)