Skip to content

Commit cf8051b

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

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

38043804
self.assertIs(dt.tzinfo, timezone.utc)
38053805

3806+
def test_fromisoformat_utc_subsecond_offset(self):
3807+
# A UTC offset whose whole-second part is zero but with a non-zero
3808+
# microsecond part must be preserved, not collapsed to UTC.
3809+
for us in (1, -1, 999999, -999999):
3810+
with self.subTest(microseconds=us):
3811+
tz = timezone(timedelta(microseconds=us))
3812+
dt = self.theclass(2020, 6, 15, 12, 34, 56, tzinfo=tz)
3813+
rt = self.theclass.fromisoformat(dt.isoformat())
3814+
self.assertEqual(rt.utcoffset(), timedelta(microseconds=us))
3815+
self.assertEqual(rt, dt)
3816+
self.assertIsNot(rt.tzinfo, timezone.utc)
3817+
3818+
tz = timezone(timedelta(hours=5, minutes=30, seconds=15,
3819+
microseconds=123456))
3820+
dt = self.theclass(2020, 6, 15, 12, 34, 56, tzinfo=tz)
3821+
rt = self.theclass.fromisoformat(dt.isoformat())
3822+
self.assertEqual(rt.utcoffset(), tz.utcoffset(None))
3823+
self.assertEqual(rt, dt)
3824+
3825+
for tstr in ('2020-06-15T12:34:56+00:00',
3826+
'2020-06-15T12:34:56+00:00:00.000000',
3827+
'2020-06-15T12:34:56Z'):
3828+
with self.subTest(tstr=tstr):
3829+
self.assertIs(self.theclass.fromisoformat(tstr).tzinfo,
3830+
timezone.utc)
3831+
38063832
def test_fromisoformat_subclass(self):
38073833
class DateTimeSubclass(self.theclass):
38083834
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
@@ -1668,8 +1668,8 @@ tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds)
16681668
{
16691669
PyObject *tzinfo;
16701670
if (rv == 1) {
1671-
// Create a timezone from offset in seconds (0 returns UTC)
1672-
if (tzoffset == 0) {
1671+
// Create a timezone from the offset (a zero offset returns UTC)
1672+
if (tzoffset == 0 && tz_useconds == 0) {
16731673
return Py_NewRef(CONST_UTC(NO_STATE));
16741674
}
16751675

0 commit comments

Comments
 (0)