Skip to content

Commit c5ac825

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

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
@@ -3416,6 +3416,32 @@ def test_fromisoformat_utc(self):
34163416

34173417
self.assertIs(dt.tzinfo, timezone.utc)
34183418

3419+
def test_fromisoformat_utc_subsecond_offset(self):
3420+
# A UTC offset whose whole-second part is zero but with a non-zero
3421+
# microsecond part must be preserved, not collapsed to UTC.
3422+
for us in (1, -1, 999999, -999999):
3423+
with self.subTest(microseconds=us):
3424+
tz = timezone(timedelta(microseconds=us))
3425+
dt = self.theclass(2020, 6, 15, 12, 34, 56, tzinfo=tz)
3426+
rt = self.theclass.fromisoformat(dt.isoformat())
3427+
self.assertEqual(rt.utcoffset(), timedelta(microseconds=us))
3428+
self.assertEqual(rt, dt)
3429+
self.assertIsNot(rt.tzinfo, timezone.utc)
3430+
3431+
tz = timezone(timedelta(hours=5, minutes=30, seconds=15,
3432+
microseconds=123456))
3433+
dt = self.theclass(2020, 6, 15, 12, 34, 56, tzinfo=tz)
3434+
rt = self.theclass.fromisoformat(dt.isoformat())
3435+
self.assertEqual(rt.utcoffset(), tz.utcoffset(None))
3436+
self.assertEqual(rt, dt)
3437+
3438+
for tstr in ('2020-06-15T12:34:56+00:00',
3439+
'2020-06-15T12:34:56+00:00:00.000000',
3440+
'2020-06-15T12:34:56Z'):
3441+
with self.subTest(tstr=tstr):
3442+
self.assertIs(self.theclass.fromisoformat(tstr).tzinfo,
3443+
timezone.utc)
3444+
34193445
def test_fromisoformat_subclass(self):
34203446
class DateTimeSubclass(self.theclass):
34213447
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
@@ -1637,8 +1637,8 @@ tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds)
16371637
{
16381638
PyObject *tzinfo;
16391639
if (rv == 1) {
1640-
// Create a timezone from offset in seconds (0 returns UTC)
1641-
if (tzoffset == 0) {
1640+
// Create a timezone from the offset (a zero offset returns UTC)
1641+
if (tzoffset == 0 && tz_useconds == 0) {
16421642
return Py_NewRef(CONST_UTC(NO_STATE));
16431643
}
16441644

0 commit comments

Comments
 (0)