From e57d1b6321917ab5c6cb4380de20fd9a92bcf020 Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Fri, 16 Jun 2023 14:49:17 +0100 Subject: [PATCH 01/17] Add NEWS.d entry --- .../next/Library/2023-06-16-14-49-00.gh-issue-102450.BoEfh3.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2023-06-16-14-49-00.gh-issue-102450.BoEfh3.rst diff --git a/Misc/NEWS.d/next/Library/2023-06-16-14-49-00.gh-issue-102450.BoEfh3.rst b/Misc/NEWS.d/next/Library/2023-06-16-14-49-00.gh-issue-102450.BoEfh3.rst new file mode 100644 index 00000000000000..8013aba21e2a64 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-06-16-14-49-00.gh-issue-102450.BoEfh3.rst @@ -0,0 +1 @@ +Add missing ISO-8601 24:00 alternative to midnight of next day to :meth:`datetime.datetime.fromisoformat()` and :meth:`datetime.time.fromisoformat()`. From d711dafdb23d23ed248686b122a5b4a33c33b462 Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Fri, 16 Jun 2023 11:26:18 +0100 Subject: [PATCH 02/17] Allow ISO-8601 24:00 alternative to midnight on datetime.time.fromisoformat() --- Modules/_datetimemodule.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 8562e0ca0bbbab..63e9167ff2a26a 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -5004,6 +5004,10 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { return NULL; } + if (hour == 24 && minute == 0 && second == 0 && microsecond == 0) { + hour = 0; + } + PyObject *t; if ( (PyTypeObject *)cls == TIME_TYPE(NO_STATE)) { t = new_time(hour, minute, second, microsecond, tzinfo, 0); From f32aa970e7a738c3b4402ef0c5799bbdacd84408 Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Fri, 16 Jun 2023 11:54:23 +0100 Subject: [PATCH 03/17] Allow ISO-8601 24:00 alternative to midnight on datetime.datetime.fromisoformat() --- Modules/_datetimemodule.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 63e9167ff2a26a..c8d95bc3f97ba5 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -5865,6 +5865,22 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) goto error; } + if ( + (hour == 24 && minute == 0 && second == 0 && microsecond == 0) && // provided alternate to midnight of next day + (month <= 12 && day <= days_in_month(year, month)) // month and day component was previously valid + ) { + // Calculate midnight of the next day + hour = 0; + day += 1; + if (day > days_in_month(year, month)) { + day = 1; + month += 1; + if (month > 12) { + month = 1; + year += 1; + } + } + } PyObject *dt = new_datetime_subclass_ex(year, month, day, hour, minute, second, microsecond, tzinfo, cls); From d52bf410ce63f84e3d74b11a1c5f1c93e0194ea8 Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Fri, 16 Jun 2023 14:52:20 +0100 Subject: [PATCH 04/17] Add NEWS.d entry --- .../next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst diff --git a/Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst b/Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst new file mode 100644 index 00000000000000..8013aba21e2a64 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst @@ -0,0 +1 @@ +Add missing ISO-8601 24:00 alternative to midnight of next day to :meth:`datetime.datetime.fromisoformat()` and :meth:`datetime.time.fromisoformat()`. From 699dfcb50f439b05cd01997a1b82aa6d37e3c45f Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Fri, 16 Jun 2023 15:55:20 +0100 Subject: [PATCH 05/17] Improve error message when hour is 24 and minute/second/microsecond is not 0 --- Modules/_datetimemodule.c | 43 +++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index c8d95bc3f97ba5..0b53f4635c4fa8 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -5004,8 +5004,12 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { return NULL; } - if (hour == 24 && minute == 0 && second == 0 && microsecond == 0) { - hour = 0; + if (hour == 24) { + if (minute == 0 && second == 0 && microsecond == 0) { + hour = 0; + } else { + goto invalid_iso_midnight; + } } PyObject *t; @@ -5019,6 +5023,10 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { Py_DECREF(tzinfo); return t; +invalid_iso_midnight: + PyErr_SetString(PyExc_ValueError, "minute, second, and microsecond must be 0 when hour is 24"); + return NULL; + invalid_string_error: PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", tstr); return NULL; @@ -5865,20 +5873,21 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) goto error; } - if ( - (hour == 24 && minute == 0 && second == 0 && microsecond == 0) && // provided alternate to midnight of next day - (month <= 12 && day <= days_in_month(year, month)) // month and day component was previously valid - ) { - // Calculate midnight of the next day - hour = 0; - day += 1; - if (day > days_in_month(year, month)) { - day = 1; - month += 1; - if (month > 12) { - month = 1; - year += 1; + if ((hour == 24) && (month <= 12 && day <= days_in_month(year, month))) { + if (minute == 0 && second == 0 && microsecond == 0) { + // Calculate midnight of the next day + hour = 0; + day += 1; + if (day > days_in_month(year, month)) { + day = 1; + month += 1; + if (month > 12) { + month = 1; + year += 1; + } } + } else { + goto invalid_iso_midnight; } } PyObject *dt = new_datetime_subclass_ex(year, month, day, hour, minute, @@ -5888,6 +5897,10 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) Py_DECREF(dtstr_clean); return dt; +invalid_iso_midnight: + PyErr_SetString(PyExc_ValueError, "minute, second, and microsecond must be 0 when hour is 24"); + return NULL; + invalid_string_error: PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", dtstr); From 6e46433245a346e223c6998642340d0d399df9fa Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Sat, 17 Jun 2023 22:09:13 +0100 Subject: [PATCH 06/17] Add tests for 24:00 fromisoformat --- Lib/test/datetimetester.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index aef24e11393f6a..f2c9672296804e 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3342,6 +3342,9 @@ def test_fromisoformat_datetime_examples(self): ('2025-01-02T03:04:05,678+00:00:10', self.theclass(2025, 1, 2, 3, 4, 5, 678000, tzinfo=timezone(timedelta(seconds=10)))), + ('2025-01-02T24:00:00', self.theclass(2025, 1, 3, 0, 0, 0)), + ('2025-01-31T24:00:00', self.theclass(2025, 2, 1, 0, 0, 0)), + ('2025-12-31T24:00:00', self.theclass(2026, 1, 1, 0, 0, 0)) ] for input_str, expected in examples: @@ -3378,6 +3381,11 @@ def test_fromisoformat_fails_datetime(self): '2009-04-19T12:30:45.123456-05:00a', # Extra text '2009-04-19T12:30:45.123-05:00a', # Extra text '2009-04-19T12:30:45-05:00a', # Extra text + '2009-04-19T24:00:00.000001', # Has non-zero microseconds on 24:00 + '2009-04-19T24:00:01.000000', # Has non-zero seconds on 24:00 + '2009-04-19T24:01:00.000000', # Has non-zero minutes on 24:00 + '2009-04-32T24:00:00.000000', # Day is invalid before wrapping due to 24:00 + '2009-13-01T24:00:00.000000', # Month is invalid before wrapping due to 24:00 ] for bad_str in bad_strs: From 040da2c341404eeadb050939db51c15b01ac57c6 Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Sat, 17 Jun 2023 22:59:18 +0100 Subject: [PATCH 07/17] Remove duplicate call to days_in_month() by storing in variable --- Modules/_datetimemodule.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 0b53f4635c4fa8..8cb62ee8933f4e 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -5873,22 +5873,26 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) goto error; } - if ((hour == 24) && (month <= 12 && day <= days_in_month(year, month))) { - if (minute == 0 && second == 0 && microsecond == 0) { - // Calculate midnight of the next day - hour = 0; - day += 1; - if (day > days_in_month(year, month)) { - day = 1; - month += 1; - if (month > 12) { - month = 1; - year += 1; + if ((hour == 24) && (month <= 12)) { + int d_in_month = days_in_month(year, month); + if (day <= d_in_month) { + if (minute == 0 && second == 0 && microsecond == 0) { + // Calculate midnight of the next day + hour = 0; + day += 1; + if (day > d_in_month) { + day = 1; + month += 1; + if (month > 12) { + month = 1; + year += 1; + } } + } else { + goto invalid_iso_midnight; } - } else { - goto invalid_iso_midnight; } + } PyObject *dt = new_datetime_subclass_ex(year, month, day, hour, minute, second, microsecond, tzinfo, cls); From 4833d68082da51b62752eddffc1eeb9881c3c46f Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Sat, 17 Jun 2023 22:59:35 +0100 Subject: [PATCH 08/17] Add Python implementation --- Lib/_pydatetime.py | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index f8e121eb79a04d..098004fcbb91ab 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -463,6 +463,16 @@ def _parse_isoformat_time(tstr): time_comps = _parse_hh_mm_ss_ff(timestr) + hour, minute, second, microsecond = time_comps + became_next_day = False + if (hour == 24): + if not all(time_comp == 0 for time_comp in time_comps[1:]): + raise ValueError("minute, second, and microsecond must be 0 when hour is 24") + + hour = 0 + time_comps[0] = hour + became_next_day = True + tzi = None if tz_pos == len_str and tstr[-1] == 'Z': tzi = timezone.utc @@ -488,14 +498,14 @@ def _parse_isoformat_time(tstr): else: tzsign = -1 if tstr[tz_pos - 1] == '-' else 1 - td = timedelta(hours=tz_comps[0], minutes=tz_comps[1], - seconds=tz_comps[2], microseconds=tz_comps[3]) + td = timedelta(hours=hour, minutes=minute, + seconds=second, microseconds=microsecond) tzi = timezone(tzsign * td) time_comps.append(tzi) - return time_comps + return time_comps, became_next_day # tuple[int, int, int] -> tuple[int, int, int] version of date.fromisocalendar def _isoweek_to_gregorian(year, week, day): @@ -1588,7 +1598,7 @@ def fromisoformat(cls, time_string): time_string = time_string.removeprefix('T') try: - return cls(*_parse_isoformat_time(time_string)) + return cls(*_parse_isoformat_time(time_string)[0]) except Exception: raise ValueError(f'Invalid isoformat string: {time_string!r}') @@ -1902,10 +1912,25 @@ def fromisoformat(cls, date_string): if tstr: try: - time_components = _parse_isoformat_time(tstr) + time_components, became_next_day = _parse_isoformat_time(tstr) except ValueError: raise ValueError( f'Invalid isoformat string: {date_string!r}') from None + else: + if became_next_day: + year, month, day = date_components + + # Only wrap day/month when it was previously valid + if month <= 12 and day <= (days_in_month := _days_in_month(year, month)): + # Calculate midnight of the next day + day += 1 + if day > days_in_month: + day = 1 + month += 1 + if month > 12: + month = 1 + year += 1 + date_components = [year, month, day] else: time_components = [0, 0, 0, 0, None] From c71764f4200ba8c62d5cffda67b82eb0615c7aaa Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Sat, 17 Jun 2023 23:10:39 +0100 Subject: [PATCH 09/17] Fix Lint --- Lib/_pydatetime.py | 1 - Modules/_datetimemodule.c | 1 - 2 files changed, 2 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 098004fcbb91ab..9a16e52109be30 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -1919,7 +1919,6 @@ def fromisoformat(cls, date_string): else: if became_next_day: year, month, day = date_components - # Only wrap day/month when it was previously valid if month <= 12 and day <= (days_in_month := _days_in_month(year, month)): # Calculate midnight of the next day diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 8cb62ee8933f4e..3b7340e69bb455 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -5892,7 +5892,6 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) goto invalid_iso_midnight; } } - } PyObject *dt = new_datetime_subclass_ex(year, month, day, hour, minute, second, microsecond, tzinfo, cls); From 0b92a1cf1739d21298da0766b373d656a478a0e8 Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Sat, 17 Jun 2023 23:36:06 +0100 Subject: [PATCH 10/17] Fix differing error msg in datetime.fromisoformat implementations when 24hrs has non-zero time component(s) --- Lib/_pydatetime.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 9a16e52109be30..2aac4fc061ce45 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -465,13 +465,14 @@ def _parse_isoformat_time(tstr): hour, minute, second, microsecond = time_comps became_next_day = False + error_from_components = False if (hour == 24): - if not all(time_comp == 0 for time_comp in time_comps[1:]): - raise ValueError("minute, second, and microsecond must be 0 when hour is 24") - - hour = 0 - time_comps[0] = hour - became_next_day = True + if all(time_comp == 0 for time_comp in time_comps[1:]): + hour = 0 + time_comps[0] = hour + became_next_day = True + else: + error_from_components = True tzi = None if tz_pos == len_str and tstr[-1] == 'Z': @@ -505,7 +506,7 @@ def _parse_isoformat_time(tstr): time_comps.append(tzi) - return time_comps, became_next_day + return time_comps, became_next_day, error_from_components # tuple[int, int, int] -> tuple[int, int, int] version of date.fromisocalendar def _isoweek_to_gregorian(year, week, day): @@ -1912,11 +1913,14 @@ def fromisoformat(cls, date_string): if tstr: try: - time_components, became_next_day = _parse_isoformat_time(tstr) + time_components, became_next_day, error_from_components = _parse_isoformat_time(tstr) except ValueError: raise ValueError( f'Invalid isoformat string: {date_string!r}') from None else: + if error_from_components: + raise ValueError("minute, second, and microsecond must be 0 when hour is 24") + if became_next_day: year, month, day = date_components # Only wrap day/month when it was previously valid From c9db98ba1c33b7ab60b7fb71395a7e379819af11 Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Sun, 18 Jun 2023 08:42:31 +0100 Subject: [PATCH 11/17] Fix using time components inside tzinfo in Python implementation --- Lib/_pydatetime.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 2aac4fc061ce45..154e6ebb9c5131 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -499,8 +499,8 @@ def _parse_isoformat_time(tstr): else: tzsign = -1 if tstr[tz_pos - 1] == '-' else 1 - td = timedelta(hours=hour, minutes=minute, - seconds=second, microseconds=microsecond) + td = timedelta(hours=tz_comps[0], minutes=tz_comps[1], + seconds=tz_comps[2], microseconds=tz_comps[3]) tzi = timezone(tzsign * td) From 004a5fffa10c522f628a94125c18e827ef9faa99 Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Sun, 18 Jun 2023 08:44:04 +0100 Subject: [PATCH 12/17] Don't parse tzinfo in C implementation when invalid iso midnight --- Modules/_datetimemodule.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 3b7340e69bb455..58b365334869da 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -4997,13 +4997,6 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { goto invalid_string_error; } - PyObject *tzinfo = tzinfo_from_isoformat_results(rv, tzoffset, - tzimicrosecond); - - if (tzinfo == NULL) { - return NULL; - } - if (hour == 24) { if (minute == 0 && second == 0 && microsecond == 0) { hour = 0; @@ -5012,6 +5005,13 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { } } + PyObject *tzinfo = tzinfo_from_isoformat_results(rv, tzoffset, + tzimicrosecond); + + if (tzinfo == NULL) { + return NULL; + } + PyObject *t; if ( (PyTypeObject *)cls == TIME_TYPE(NO_STATE)) { t = new_time(hour, minute, second, microsecond, tzinfo, 0); From f182e2e2bd1f389e4f76ef14a4331bf0fb79da18 Mon Sep 17 00:00:00 2001 From: TizzySaurus Date: Sun, 18 Jun 2023 08:45:05 +0100 Subject: [PATCH 13/17] Remove duplicated variable in datetime test assertion line --- Lib/test/datetimetester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index f2c9672296804e..3c4b3a4099ed70 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -4320,7 +4320,7 @@ def test_fromisoformat_timezone(self): with self.subTest(tstr=tstr): t_rt = self.theclass.fromisoformat(tstr) - assert t == t_rt, t_rt + assert t == t_rt def test_fromisoformat_timespecs(self): time_bases = [ From 1b65671e346d454915578c5e502891dc40a60a4d Mon Sep 17 00:00:00 2001 From: Izan Robinson Date: Wed, 25 Sep 2024 09:44:41 +0100 Subject: [PATCH 14/17] Add self to acknowledgements --- Misc/ACKS | 1 + .../next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst | 1 + 2 files changed, 2 insertions(+) diff --git a/Misc/ACKS b/Misc/ACKS index ef0f403950255b..b2529601a2f71a 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1553,6 +1553,7 @@ Carl Robben Ben Roberts Mark Roberts Andy Robinson +Izan "TizzySaurus" Robinson Jim Robinson Yolanda Robla Daniel Rocco diff --git a/Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst b/Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst index 8013aba21e2a64..b56954e6811dac 100644 --- a/Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst +++ b/Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst @@ -1 +1,2 @@ Add missing ISO-8601 24:00 alternative to midnight of next day to :meth:`datetime.datetime.fromisoformat()` and :meth:`datetime.time.fromisoformat()`. +Patch by Izan "TizzySaurus" Robinson (tizzysaurus@gmail.com) \ No newline at end of file From ae85372add0f6e82a93ff28075820e007425ae38 Mon Sep 17 00:00:00 2001 From: Izan Robinson Date: Wed, 25 Sep 2024 09:52:33 +0100 Subject: [PATCH 15/17] Remove duplicate NEWS entry --- .../next/Library/2023-06-16-14-49-00.gh-issue-102450.BoEfh3.rst | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Misc/NEWS.d/next/Library/2023-06-16-14-49-00.gh-issue-102450.BoEfh3.rst diff --git a/Misc/NEWS.d/next/Library/2023-06-16-14-49-00.gh-issue-102450.BoEfh3.rst b/Misc/NEWS.d/next/Library/2023-06-16-14-49-00.gh-issue-102450.BoEfh3.rst deleted file mode 100644 index 8013aba21e2a64..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-06-16-14-49-00.gh-issue-102450.BoEfh3.rst +++ /dev/null @@ -1 +0,0 @@ -Add missing ISO-8601 24:00 alternative to midnight of next day to :meth:`datetime.datetime.fromisoformat()` and :meth:`datetime.time.fromisoformat()`. From 9d02f82532796ee52578ce7a29f5aa0e43be6585 Mon Sep 17 00:00:00 2001 From: Izan Robinson Date: Wed, 25 Sep 2024 09:55:05 +0100 Subject: [PATCH 16/17] Linting --- .../Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst b/Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst index b56954e6811dac..abfad5fa63b777 100644 --- a/Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst +++ b/Misc/NEWS.d/next/Library/2023-06-16-14-52-00.gh-issue-102450.MfeR6A.rst @@ -1,2 +1,2 @@ -Add missing ISO-8601 24:00 alternative to midnight of next day to :meth:`datetime.datetime.fromisoformat()` and :meth:`datetime.time.fromisoformat()`. -Patch by Izan "TizzySaurus" Robinson (tizzysaurus@gmail.com) \ No newline at end of file +Add missing ISO-8601 24:00 alternative to midnight of next day to :meth:`datetime.datetime.fromisoformat` and :meth:`datetime.time.fromisoformat`. +Patch by Izan "TizzySaurus" Robinson (tizzysaurus@gmail.com) From 2ccb2a220c4e60d3fccbc2e3feab9796fd3b9d2e Mon Sep 17 00:00:00 2001 From: Izan Robinson Date: Wed, 25 Sep 2024 11:06:18 +0100 Subject: [PATCH 17/17] Add missing test case for when wrapping the year makes it invalid (too large) --- Lib/test/datetimetester.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 3c4b3a4099ed70..16aff186eb69f7 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3386,6 +3386,7 @@ def test_fromisoformat_fails_datetime(self): '2009-04-19T24:01:00.000000', # Has non-zero minutes on 24:00 '2009-04-32T24:00:00.000000', # Day is invalid before wrapping due to 24:00 '2009-13-01T24:00:00.000000', # Month is invalid before wrapping due to 24:00 + '9999-12-31T24:00:00.000000', # Year is invalid after wrapping due to 24:00 ] for bad_str in bad_strs: