8000 GH-90750: Use datetime.fromisocalendar in _strptime (#103802) · python/cpython@a5308e1 · GitHub
[go: up one dir, main page]

Skip to content

Commit a5308e1

Browse files
authored
GH-90750: Use datetime.fromisocalendar in _strptime (#103802)
Use datetime.fromisocalendar in _strptime This unifies the ISO → Gregorian conversion logic and improves handling of invalid ISO weeks.
1 parent b701dce commit a5308e1

File tree

3 files changed

+19
-19
lines changed

3 files changed

+19
-19
lines changed

Lib/_strptime.py

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -290,22 +290,6 @@ def _calc_julian_from_U_or_W(year, week_of_year, day_of_week, week_starts_Mon):
290290
return 1 + days_to_week + day_of_week
291291

292292

293-
def _calc_julian_from_V(iso_year, iso_week, iso_weekday):
294-
"""Calculate the Julian day based on the ISO 8601 year, week, and weekday.
295-
ISO weeks start on Mondays, with week 01 being the week containing 4 Jan.
296-
ISO week days range from 1 (Monday) to 7 (Sunday).
297-
"""
298-
correction = datetime_date(iso_year, 1, 4).isoweekday() + 3
299-
ordinal = (iso_week * 7) + iso_weekday - correction
300-
# ordinal may be negative or 0 now, which means the date is in the previous
301-
# calendar year
302-
if ordinal < 1:
303-
ordinal += datetime_date(iso_year, 1, 1).toordinal()
304-
iso_year -= 1
305-
ordinal -= datetime_date(iso_year, 1, 1).toordinal()
306-
return iso_year, ordinal
307-
308-
309293
def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
310294
"""Return a 2-tuple consisting of a time struct and an int containing
311295
the number of microseconds based on the input string and the
@@ -483,7 +467,8 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
483467
else:
484468
tz = value
485469
break
486-
# Deal with the cases where ambiguities arize
470+
471+
# Deal with the cases where ambiguities arise
487472
# don't assume default values for ISO week/year
488473
if year is None and iso_year is not None:
489474
if iso_week is None or weekday is None:
@@ -511,7 +496,6 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
511496
elif year is None:
512497
year = 1900
513498

514-
515499
# If we know the week of the year and what day of that week, we can figure
516500
# out the Julian day of the year.
517501
if julian is None and weekday is not None:
@@ -520,7 +504,10 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
520504
julian = _calc_julian_from_U_or_W(year, week_of_year, weekday,
521505
week_starts_Mon)
522506
elif iso_year is not None and iso_week is not None:
523-
year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1)
507+
datetime_result = datetime_date.fromisocalendar(iso_year, iso_week, weekday + 1)
508+
year = datetime_result.year
509+
month = datetime_result.month
510+
day = datetime_result.day
524511
if julian is not None and julian <= 0:
525512
year -= 1
526513
yday = 366 if calendar.isleap(year) else 365

Lib/test/test_strptime.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,16 @@ def test_ValueError(self):
242242
# 5. Julian/ordinal day (%j) is specified with %G, but not %Y
243243
with self.assertRaises(ValueError):
244244
_strptime._strptime("1999 256", "%G %j")
245+
# 6. Invalid ISO weeks
246+
invalid_iso_weeks = [
247+
"2019-00-1",
248+
"2019-54-1",
249+
"2021-53-1",
250+
]
251+
for invalid_iso_dtstr in invalid_iso_weeks:
252+
with self.subTest(invalid_iso_dtstr):
253+
with self.assertRaises(ValueError):
254+
_strptime._strptime(invalid_iso_dtstr, "%G-%V-%u")
245255

246256

247257
def test_strptime_exception_context(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Use :meth:`datetime.datetime.fromisocalendar` in the implementation of
2+
:meth:`datetime.datetime.strptime`, which should now accept only valid ISO
3+
dates. (Patch by Paul Ganssle)

0 commit comments

Comments
 (0)
0