From 891ac09aad6800d545ba5540201ddcee6d9356ca Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sun, 2 Mar 2025 04:43:53 +0400 Subject: [PATCH 1/4] Add checks for edge cases in `datetime.timestamp` and `datetime.astimezone` methods --- Lib/_pydatetime.py | 5 ++++- Modules/_datetimemodule.c | 9 ++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index fcf4416f331092..47b8dc657e86cc 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -1990,7 +1990,10 @@ def _mktime(self): t = (self - epoch) // timedelta(0, 1) def local(u): y, m, d, hh, mm, ss = _time.localtime(u)[:6] - return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1) + try: + return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1) + except (ValueError, OverflowError): + return u # Our goal is to solve t = local(u) for u. a = local(t) - t diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 07d7089be09d20..d8a12aef49e5fc 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -5415,6 +5415,7 @@ local(long long u) { struct tm local_time; time_t t; + long long old_u = u; u -= epoch; t = u; if (t != u) { @@ -5424,7 +5425,13 @@ local(long long u) } if (_PyTime_localtime(t, &local_time) != 0) return -1; - return utc_to_seconds(local_time.tm_year + 1900, + + // Check edge cases + int year = local_time.tm_year + 1900; + if (year < MINYEAR || year > MAXYEAR) { + return old_u; + } + return utc_to_seconds(year, local_time.tm_mon + 1, local_time.tm_mday, local_time.tm_hour, From c836cb3dcbc19533e7c93f438b6fc3c6d968c256 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Mon, 3 Mar 2025 00:41:26 +0400 Subject: [PATCH 2/4] Add tests --- Lib/test/datetimetester.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index ceeac9435dcb85..ca7b5e54605f04 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3122,6 +3122,18 @@ def dst(self, dt): return 1 with self.assertRaises(TypeError): dt_broken.astimezone() + dt_big = self.theclass(9999, 12, 31, 23, 59, 59) + dt_big_utc = dt_big.replace(hour=19, + tzinfo=timezone(timedelta(hours=-4), 'EDT')) + self.assertEqual(dt_big_utc, + dt_big.replace(tzinfo=timezone.utc).astimezone()) + + other_tz = timezone(timedelta(hours=+4), '+04') + dt_other_tz = dt_big.replace(tzinfo=other_tz) + dt_utc_tz = dt_big.replace(tzinfo=timezone.utc) + self.assertEqual(dt_other_tz, dt_other_tz.astimezone(tz=other_tz)) + self.assertEqual(dt_utc_tz, dt_utc_tz.astimezone(tz=timezone.utc)) + def test_subclass_datetime(self): class C(self.theclass): From a65ef2fa8385fd71a204f639fe13e422bd8ac3dd Mon Sep 17 00:00:00 2001 From: donBarbos Date: Mon, 3 Mar 2025 00:58:36 +0400 Subject: [PATCH 3/4] Add Py_DECREF --- Modules/_datetimemodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index d8a12aef49e5fc..d1fba6ed6eae70 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -5429,6 +5429,7 @@ local(long long u) // Check edge cases int year = local_time.tm_year + 1900; if (year < MINYEAR || year > MAXYEAR) { + Py_DECREF(year); return old_u; } return utc_to_seconds(year, From 8697ff3ca24b832cd447313c554b3bd890a6d3aa Mon Sep 17 00:00:00 2001 From: donBarbos Date: Mon, 3 Mar 2025 06:44:07 +0400 Subject: [PATCH 4/4] Wrap line --- Lib/_pydatetime.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 47b8dc657e86cc..522501286160b9 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -1991,7 +1991,8 @@ def _mktime(self): def local(u): y, m, d, hh, mm, ss = _time.localtime(u)[:6] try: - return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1) + return ((datetime(y, m, d, hh, mm, ss) - epoch) // + timedelta(0, 1)) except (ValueError, OverflowError): return u