Skip to content

Commit a3f53a0

Browse files
miss-islingtonGPHemsleyserhiy-storchaka
authored
[3.12] bpo-36959: Fix error messages for invalid ISO format string in _strptime() (GH-13408) (GH-113495)
Previously some error messages complained about incompatible combinations of directives that are not contained in the format string. (cherry picked from commit 4b2c3e8) Co-authored-by: Gordon P. Hemsley <[email protected]> Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 2f5bfc7 commit a3f53a0

File tree

3 files changed

+61
-40
lines changed

3 files changed

+61
-40
lines changed

Lib/_strptime.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,6 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
342342
tz = -1
343343
gmtoff = None
344344
gmtoff_fraction = 0
345-
# Default to -1 to signify that values not known; not critical to have,
346-
# though
347345
iso_week = week_of_year = None
348346
week_of_year_start = None
349347
# weekday and julian defaulted to None so as to signal need to calculate
@@ -470,17 +468,17 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
470468

471469
# Deal with the cases where ambiguities arise
472470
# don't assume default values for ISO week/year
473-
if year is None and iso_year is not None:
474-
if iso_week is None or weekday is None:
475-
raise ValueError("ISO year directive '%G' must be used with "
476-
"the ISO week directive '%V' and a weekday "
477-
"directive ('%A', '%a', '%w', or '%u').")
471+
if iso_year is not None:
478472
if julian is not None:
479473
raise ValueError("Day of the year directive '%j' is not "
480474
"compatible with ISO year directive '%G'. "
481475
"Use '%Y' instead.")
482-
elif week_of_year is None and iso_week is not None:
483-
if weekday is None:
476+
elif iso_week is None or weekday is None:
477+
raise ValueError("ISO year directive '%G' must be used with "
478+
"the ISO week directive '%V' and a weekday "
479+
"directive ('%A', '%a', '%w', or '%u').")
480+
elif iso_week is not None:
481+
if year is None or weekday is None:
484482
raise ValueError("ISO week directive '%V' must be used with "
485483
"the ISO year directive '%G' and a weekday "
486484
"directive ('%A', '%a', '%w', or '%u').")
@@ -490,11 +488,12 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
490488
"instead.")
491489

492490
leap_year_fix = False
493-
if year is None and month == 2 and day == 29:
494-
year = 1904 # 1904 is first leap year of 20th century
495-
leap_year_fix = True
496-
elif year is None:
497-
year = 1900
491+
if year is None:
492+
if month == 2 and day == 29:
493+
year = 1904 # 1904 is first leap year of 20th century
494+
leap_year_fix = True
495+
else:
496+
year = 1900
498497

499498
# If we know the week of the year and what day of that week, we can figure
500499
# out the Julian day of the year.

Lib/test/test_strptime.py

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -224,35 +224,55 @@ def test_ValueError(self):
224224
else:
225225
self.fail("'%s' did not raise ValueError" % bad_format)
226226

227+
msg_week_no_year_or_weekday = r"ISO week directive '%V' must be used with " \
228+
r"the ISO year directive '%G' and a weekday directive " \
229+
r"\('%A', '%a', '%w', or '%u'\)."
230+
msg_week_not_compatible = r"ISO week directive '%V' is incompatible with " \
231+
r"the year directive '%Y'. Use the ISO year '%G' instead."
232+
msg_julian_not_compatible = r"Day of the year directive '%j' is not " \
233+
r"compatible with ISO year directive '%G'. Use '%Y' instead."
234+
msg_year_no_week_or_weekday = r"ISO year directive '%G' must be used with " \
235+
r"the ISO week directive '%V' and a weekday directive " \
236+
r"\('%A', '%a', '%w', or '%u'\)."
237+
238+
locale_time = _strptime.LocaleTime()
239+
227240
# Ambiguous or incomplete cases using ISO year/week/weekday directives
228-
# 1. ISO week (%V) is specified, but the year is specified with %Y
229-
# instead of %G
230-
with self.assertRaises(ValueError):
231-
_strptime._strptime("1999 50", "%Y %V")
232-
# 2. ISO year (%G) and ISO week (%V) are specified, but weekday is not
233-
with self.assertRaises(ValueError):
234-
_strptime._strptime("1999 51", "%G %V")
235-
# 3. ISO year (%G) and weekday are specified, but ISO week (%V) is not
236-
for w in ('A', 'a', 'w', 'u'):
237-
with self.assertRaises(ValueError):
238-
_strptime._strptime("1999 51","%G %{}".format(w))
239-
# 4. ISO year is specified alone (e.g. time.strptime('2015', '%G'))
240-
with self.assertRaises(ValueError):
241-
_strptime._strptime("2015", "%G")
242-
# 5. Julian/ordinal day (%j) is specified with %G, but not %Y
243-
with self.assertRaises(ValueError):
244-
_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",
241+
subtests = [
242+
# 1. ISO week (%V) is specified, but the year is specified with %Y
243+
# instead of %G
244+
("1999 50", "%Y %V", msg_week_no_year_or_weekday),
245+
("1999 50 5", "%Y %V %u", msg_week_not_compatible),
246+
# 2. ISO year (%G) and ISO week (%V) are specified, but weekday is not
247+
("1999 51", "%G %V", msg_year_no_week_or_weekday),
248+
# 3. ISO year (%G) and weekday are specified, but ISO week (%V) is not
249+
("1999 {}".format(locale_time.f_weekday[5]), "%G %A",
250+
msg_year_no_week_or_weekday),
251+
("1999 {}".format(locale_time.a_weekday[5]), "%G %a",
252+
msg_year_no_week_or_weekday),
253+
("1999 5", "%G %w", msg_year_no_week_or_weekday),
254+
("1999 5", "%G %u", msg_year_no_week_or_weekday),
255+
# 4. ISO year is specified alone (e.g. time.strptime('2015', '%G'))
256+
("2015", "%G", msg_year_no_week_or_weekday),
257+
# 5. Julian/ordinal day (%j) is specified with %G, but not %Y
258+
("1999 256", "%G %j", msg_julian_not_compatible),
259+
("1999 50 5 256", "%G %V %u %j", msg_julian_not_compatible),
260+
# ISO week specified alone
261+
("50", "%V", msg_week_no_year_or_weekday),
262+
# ISO year is unspecified, falling back to year
263+
("50 5", "%V %u", msg_week_no_year_or_weekday),
264+
# 6. Invalid ISO weeks
265+
("2019-00-1", "%G-%V-%u",
266+
"time data '2019-00-1' does not match format '%G-%V-%u'"),
267+
("2019-54-1", "%G-%V-%u",
268+
"time data '2019-54-1' does not match format '%G-%V-%u'"),
269+
("2021-53-1", "%G-%V-%u", "Invalid week: 53"),
250270
]
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")
255271

272+
for (data_string, format, message) in subtests:
273+
with self.subTest(data_string=data_string, format=format):
274+
with self.assertRaisesRegex(ValueError, message):
275+
_strptime._strptime(data_string, format)
256276

257277
def test_strptime_exception_context(self):
258278
# check that this doesn't chain exceptions needlessly (see #17572)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix some error messages for invalid ISO format string combinations in ``strptime()`` that referred to directives not contained in the format string.
2+
Patch by Gordon P. Hemsley.

0 commit comments

Comments
 (0)