mirror of
https://github.com/python/cpython
synced 2024-10-14 08:51:20 +00:00
bpo-36959: Fix error messages for invalid ISO format string in _strptime() (GH-13408)
Previously some error messages complained about incompatible combinations of directives that are not contained in the format string. Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
e5cce70df7
commit
4b2c3e8e43
|
@ -342,8 +342,6 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
|
||||||
tz = -1
|
tz = -1
|
||||||
gmtoff = None
|
gmtoff = None
|
||||||
gmtoff_fraction = 0
|
gmtoff_fraction = 0
|
||||||
# Default to -1 to signify that values not known; not critical to have,
|
|
||||||
# though
|
|
||||||
iso_week = week_of_year = None
|
iso_week = week_of_year = None
|
||||||
week_of_year_start = None
|
week_of_year_start = None
|
||||||
# weekday and julian defaulted to None so as to signal need to calculate
|
# 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"):
|
||||||
|
|
||||||
# Deal with the cases where ambiguities arise
|
# Deal with the cases where ambiguities arise
|
||||||
# don't assume default values for ISO week/year
|
# don't assume default values for ISO week/year
|
||||||
if year is None and iso_year is not None:
|
if iso_year is not None:
|
||||||
if iso_week is None or weekday is None:
|
|
||||||
raise ValueError("ISO year directive '%G' must be used with "
|
|
||||||
"the ISO week directive '%V' and a weekday "
|
|
||||||
"directive ('%A', '%a', '%w', or '%u').")
|
|
||||||
if julian is not None:
|
if julian is not None:
|
||||||
raise ValueError("Day of the year directive '%j' is not "
|
raise ValueError("Day of the year directive '%j' is not "
|
||||||
"compatible with ISO year directive '%G'. "
|
"compatible with ISO year directive '%G'. "
|
||||||
"Use '%Y' instead.")
|
"Use '%Y' instead.")
|
||||||
elif week_of_year is None and iso_week is not None:
|
elif iso_week is None or weekday is None:
|
||||||
if weekday is None:
|
raise ValueError("ISO year directive '%G' must be used with "
|
||||||
|
"the ISO week directive '%V' and a weekday "
|
||||||
|
"directive ('%A', '%a', '%w', or '%u').")
|
||||||
|
elif iso_week is not None:
|
||||||
|
if year is None or weekday is None:
|
||||||
raise ValueError("ISO week directive '%V' must be used with "
|
raise ValueError("ISO week directive '%V' must be used with "
|
||||||
"the ISO year directive '%G' and a weekday "
|
"the ISO year directive '%G' and a weekday "
|
||||||
"directive ('%A', '%a', '%w', or '%u').")
|
"directive ('%A', '%a', '%w', or '%u').")
|
||||||
|
@ -490,11 +488,12 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
|
||||||
"instead.")
|
"instead.")
|
||||||
|
|
||||||
leap_year_fix = False
|
leap_year_fix = False
|
||||||
if year is None and month == 2 and day == 29:
|
if year is None:
|
||||||
year = 1904 # 1904 is first leap year of 20th century
|
if month == 2 and day == 29:
|
||||||
leap_year_fix = True
|
year = 1904 # 1904 is first leap year of 20th century
|
||||||
elif year is None:
|
leap_year_fix = True
|
||||||
year = 1900
|
else:
|
||||||
|
year = 1900
|
||||||
|
|
||||||
# If we know the week of the year and what day of that week, we can figure
|
# If we know the week of the year and what day of that week, we can figure
|
||||||
# out the Julian day of the year.
|
# out the Julian day of the year.
|
||||||
|
|
|
@ -224,35 +224,55 @@ def test_ValueError(self):
|
||||||
else:
|
else:
|
||||||
self.fail("'%s' did not raise ValueError" % bad_format)
|
self.fail("'%s' did not raise ValueError" % bad_format)
|
||||||
|
|
||||||
# Ambiguous or incomplete cases using ISO year/week/weekday directives
|
msg_week_no_year_or_weekday = r"ISO week directive '%V' must be used with " \
|
||||||
# 1. ISO week (%V) is specified, but the year is specified with %Y
|
r"the ISO year directive '%G' and a weekday directive " \
|
||||||
# instead of %G
|
r"\('%A', '%a', '%w', or '%u'\)."
|
||||||
with self.assertRaises(ValueError):
|
msg_week_not_compatible = r"ISO week directive '%V' is incompatible with " \
|
||||||
_strptime._strptime("1999 50", "%Y %V")
|
r"the year directive '%Y'. Use the ISO year '%G' instead."
|
||||||
# 2. ISO year (%G) and ISO week (%V) are specified, but weekday is not
|
msg_julian_not_compatible = r"Day of the year directive '%j' is not " \
|
||||||
with self.assertRaises(ValueError):
|
r"compatible with ISO year directive '%G'. Use '%Y' instead."
|
||||||
_strptime._strptime("1999 51", "%G %V")
|
msg_year_no_week_or_weekday = r"ISO year directive '%G' must be used with " \
|
||||||
# 3. ISO year (%G) and weekday are specified, but ISO week (%V) is not
|
r"the ISO week directive '%V' and a weekday directive " \
|
||||||
for w in ('A', 'a', 'w', 'u'):
|
r"\('%A', '%a', '%w', or '%u'\)."
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
_strptime._strptime("1999 51","%G %{}".format(w))
|
|
||||||
# 4. ISO year is specified alone (e.g. time.strptime('2015', '%G'))
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
_strptime._strptime("2015", "%G")
|
|
||||||
# 5. Julian/ordinal day (%j) is specified with %G, but not %Y
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
_strptime._strptime("1999 256", "%G %j")
|
|
||||||
# 6. Invalid ISO weeks
|
|
||||||
invalid_iso_weeks = [
|
|
||||||
"2019-00-1",
|
|
||||||
"2019-54-1",
|
|
||||||
"2021-53-1",
|
|
||||||
]
|
|
||||||
for invalid_iso_dtstr in invalid_iso_weeks:
|
|
||||||
with self.subTest(invalid_iso_dtstr):
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
_strptime._strptime(invalid_iso_dtstr, "%G-%V-%u")
|
|
||||||
|
|
||||||
|
locale_time = _strptime.LocaleTime()
|
||||||
|
|
||||||
|
# Ambiguous or incomplete cases using ISO year/week/weekday directives
|
||||||
|
subtests = [
|
||||||
|
# 1. ISO week (%V) is specified, but the year is specified with %Y
|
||||||
|
# instead of %G
|
||||||
|
("1999 50", "%Y %V", msg_week_no_year_or_weekday),
|
||||||
|
("1999 50 5", "%Y %V %u", msg_week_not_compatible),
|
||||||
|
# 2. ISO year (%G) and ISO week (%V) are specified, but weekday is not
|
||||||
|
("1999 51", "%G %V", msg_year_no_week_or_weekday),
|
||||||
|
# 3. ISO year (%G) and weekday are specified, but ISO week (%V) is not
|
||||||
|
("1999 {}".format(locale_time.f_weekday[5]), "%G %A",
|
||||||
|
msg_year_no_week_or_weekday),
|
||||||
|
("1999 {}".format(locale_time.a_weekday[5]), "%G %a",
|
||||||
|
msg_year_no_week_or_weekday),
|
||||||
|
("1999 5", "%G %w", msg_year_no_week_or_weekday),
|
||||||
|
("1999 5", "%G %u", msg_year_no_week_or_weekday),
|
||||||
|
# 4. ISO year is specified alone (e.g. time.strptime('2015', '%G'))
|
||||||
|
("2015", "%G", msg_year_no_week_or_weekday),
|
||||||
|
# 5. Julian/ordinal day (%j) is specified with %G, but not %Y
|
||||||
|
("1999 256", "%G %j", msg_julian_not_compatible),
|
||||||
|
("1999 50 5 256", "%G %V %u %j", msg_julian_not_compatible),
|
||||||
|
# ISO week specified alone
|
||||||
|
("50", "%V", msg_week_no_year_or_weekday),
|
||||||
|
# ISO year is unspecified, falling back to year
|
||||||
|
("50 5", "%V %u", msg_week_no_year_or_weekday),
|
||||||
|
# 6. Invalid ISO weeks
|
||||||
|
("2019-00-1", "%G-%V-%u",
|
||||||
|
"time data '2019-00-1' does not match format '%G-%V-%u'"),
|
||||||
|
("2019-54-1", "%G-%V-%u",
|
||||||
|
"time data '2019-54-1' does not match format '%G-%V-%u'"),
|
||||||
|
("2021-53-1", "%G-%V-%u", "Invalid week: 53"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for (data_string, format, message) in subtests:
|
||||||
|
with self.subTest(data_string=data_string, format=format):
|
||||||
|
with self.assertRaisesRegex(ValueError, message):
|
||||||
|
_strptime._strptime(data_string, format)
|
||||||
|
|
||||||
def test_strptime_exception_context(self):
|
def test_strptime_exception_context(self):
|
||||||
# check that this doesn't chain exceptions needlessly (see #17572)
|
# check that this doesn't chain exceptions needlessly (see #17572)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix some error messages for invalid ISO format string combinations in ``strptime()`` that referred to directives not contained in the format string.
|
||||||
|
Patch by Gordon P. Hemsley.
|
Loading…
Reference in a new issue