From 1c1a5d434870b0912a7edceee9b2a7e4336a6bf6 Mon Sep 17 00:00:00 2001 From: "Gordon P. Hemsley" Date: Sat, 18 May 2019 15:44:22 -0400 Subject: [PATCH 1/4] Simplify leap year check in _strptime --- Lib/_strptime.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Lib/_strptime.py b/Lib/_strptime.py index f4f3c0b80c1d05..571a183b88b3df 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -505,11 +505,12 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): "instead.") leap_year_fix = False - if year is None and month == 2 and day == 29: - year = 1904 # 1904 is first leap year of 20th century - leap_year_fix = True - elif year is None: - year = 1900 + if year is None: + if month == 2 and day == 29: + year = 1904 # 1904 is first leap year of 20th century + leap_year_fix = True + else: + year = 1900 # If we know the week of the year and what day of that week, we can figure From 4e29c31f40859fe444b81586135374976482c309 Mon Sep 17 00:00:00 2001 From: "Gordon P. Hemsley" Date: Sat, 18 May 2019 15:47:55 -0400 Subject: [PATCH 2/4] bpo-36959: Fix checks for invalid ISO date formats in _strptime --- Lib/_strptime.py | 28 ++++----- Lib/test/test_strptime.py | 60 +++++++++++++------ .../2019-05-18-15-50-14.bpo-36959.ew6WZ4.rst | 5 ++ 3 files changed, 61 insertions(+), 32 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-05-18-15-50-14.bpo-36959.ew6WZ4.rst diff --git a/Lib/_strptime.py b/Lib/_strptime.py index 571a183b88b3df..57e0418b1b404a 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -358,8 +358,6 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): tz = -1 gmtoff = None gmtoff_fraction = 0 - # Default to -1 to signify that values not known; not critical to have, - # though iso_week = week_of_year = None week_of_year_start = None # weekday and julian defaulted to None so as to signal need to calculate @@ -483,19 +481,11 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): else: tz = value break - # Deal with the cases where ambiguities arize + + # Deal with the cases where ambiguities arise # don't assume default values for ISO week/year - if year is None and 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: - raise ValueError("Day of the year directive '%j' is not " - "compatible with ISO year directive '%G'. " - "Use '%Y' instead.") - elif week_of_year is None and iso_week is not None: - if weekday is None: + if iso_year is None and iso_week is not None: + if year is None or weekday is None: raise ValueError("ISO week directive '%V' must be used with " "the ISO year directive '%G' and a weekday " "directive ('%A', '%a', '%w', or '%u').") @@ -503,6 +493,16 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): raise ValueError("ISO week directive '%V' is incompatible with " "the year directive '%Y'. Use the ISO year '%G' " "instead.") + elif iso_year is not None: + if julian is not None: + raise ValueError("Day of the year directive '%j' is not " + "compatible with ISO year directive '%G'. " + "Use '%Y' instead.") + elif 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').") + leap_year_fix = False if year is None: diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 623da401eee4ff..2d1726133eda18 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -219,25 +219,49 @@ def test_ValueError(self): else: self.fail("'%s' did not raise ValueError" % bad_format) - # Ambiguous or incomplete cases using ISO year/week/weekday directives - # 1. ISO week (%V) is specified, but the year is specified with %Y - # instead of %G - with self.assertRaises(ValueError): - _strptime._strptime("1999 50", "%Y %V") - # 2. ISO year (%G) and ISO week (%V) are specified, but weekday is not - with self.assertRaises(ValueError): - _strptime._strptime("1999 51", "%G %V") - # 3. ISO year (%G) and weekday are specified, but ISO week (%V) is not - for w in ('A', 'a', 'w', '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") + msg_week_no_year_or_weekday = r"ISO week directive '%V' must be used with " \ + r"the ISO year directive '%G' and a weekday directive " \ + r"\('%A', '%a', '%w', or '%u'\)." + msg_week_not_compatible = r"ISO week directive '%V' is incompatible with " \ + r"the year directive '%Y'. Use the ISO year '%G' instead." + msg_julian_not_compatible = r"Day of the year directive '%j' is not " \ + r"compatible with ISO year directive '%G'. Use '%Y' instead." + msg_year_no_week_or_weekday = r"ISO year directive '%G' must be used with " \ + r"the ISO week directive '%V' and a weekday directive " \ + r"\('%A', '%a', '%w', or '%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), + ] + + 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): # check that this doesn't chain exceptions needlessly (see #17572) diff --git a/Misc/NEWS.d/next/Library/2019-05-18-15-50-14.bpo-36959.ew6WZ4.rst b/Misc/NEWS.d/next/Library/2019-05-18-15-50-14.bpo-36959.ew6WZ4.rst new file mode 100644 index 00000000000000..44511758a695ff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-05-18-15-50-14.bpo-36959.ew6WZ4.rst @@ -0,0 +1,5 @@ +Fix checks for invalid combinations of ISO format strings in strptime. + +(This will change ValueError messages for certain invalid combinations.) + +Patch by Gordon P. Hemsley. From a7244ae5867a4482b4996cc1393ff8c29cef75ec Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 26 Dec 2023 20:56:32 +0200 Subject: [PATCH 3/4] Simplify conditions. --- Lib/_strptime.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Lib/_strptime.py b/Lib/_strptime.py index 65a0e67a041d1a..798cf9f9d3fffe 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -468,16 +468,7 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): # Deal with the cases where ambiguities arise # don't assume default values for ISO week/year - if iso_year is None and iso_week is not None: - if year is None or weekday is None: - raise ValueError("ISO week directive '%V' must be used with " - "the ISO year directive '%G' and a weekday " - "directive ('%A', '%a', '%w', or '%u').") - else: - raise ValueError("ISO week directive '%V' is incompatible with " - "the year directive '%Y'. Use the ISO year '%G' " - "instead.") - elif iso_year is not None: + if iso_year is not None: if julian is not None: raise ValueError("Day of the year directive '%j' is not " "compatible with ISO year directive '%G'. " @@ -486,7 +477,15 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): 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 " + "the ISO year directive '%G' and a weekday " + "directive ('%A', '%a', '%w', or '%u').") + else: + raise ValueError("ISO week directive '%V' is incompatible with " + "the year directive '%Y'. Use the ISO year '%G' " + "instead.") leap_year_fix = False if year is None: From 95a52679db9569af6b6237676ec9b19ef1468c9b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 26 Dec 2023 21:08:53 +0200 Subject: [PATCH 4/4] Update 2019-05-18-15-50-14.bpo-36959.ew6WZ4.rst --- .../next/Library/2019-05-18-15-50-14.bpo-36959.ew6WZ4.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2019-05-18-15-50-14.bpo-36959.ew6WZ4.rst b/Misc/NEWS.d/next/Library/2019-05-18-15-50-14.bpo-36959.ew6WZ4.rst index 44511758a695ff..1ac05a730a2086 100644 --- a/Misc/NEWS.d/next/Library/2019-05-18-15-50-14.bpo-36959.ew6WZ4.rst +++ b/Misc/NEWS.d/next/Library/2019-05-18-15-50-14.bpo-36959.ew6WZ4.rst @@ -1,5 +1,2 @@ -Fix checks for invalid combinations of ISO format strings in strptime. - -(This will change ValueError messages for certain invalid combinations.) - +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.