Skip to content

Commit f8bbeac

Browse files
committed
gh-154892: Fix PyLong_AsLong() error checks in _zoneinfo
1 parent 11d0da5 commit f8bbeac

3 files changed

Lines changed: 37 additions & 3 deletions

File tree

Lib/test/test_zoneinfo/test_zoneinfo.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,37 @@ def __add__(self, other):
499499
self.assertEqual(dt_fromutc.fold, 1)
500500
self.assertEqual(dt.fold, 0)
501501

502+
def test_datetime_subclass_negative_components(self):
503+
"""Regression test for gh-154892.
504+
505+
Datetime subclasses may return -1 for time components. The C
506+
accelerator should treat -1 as a valid PyLong_AsLong() result unless
507+
an exception is set, matching the pure-Python implementation.
508+
"""
509+
class MinusOneDateTime(datetime):
510+
@property
511+
def hour(self):
512+
return -1
513+
514+
@property
515+
def minute(self):
516+
return -1
517+
518+
@property
519+
def second(self):
520+
return -1
521+
522+
zi = self.zone_from_key("UTC")
523+
dt = MinusOneDateTime(2024, 1, 1, tzinfo=zi)
524+
525+
self.assertEqual(dt.utcoffset(), ZERO)
526+
self.assertEqual(dt.dst(), ZERO)
527+
self.assertEqual(dt.tzname(), "UTC")
528+
self.assertEqual(
529+
zi.fromutc(dt),
530+
datetime(2024, 1, 1, tzinfo=zi),
531+
)
532+
502533

503534
class ZoneInfoDatetimeSubclassTest(DatetimeSubclassMixin, ZoneInfoTest):
504535
pass
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed a bug in the C accelerator for :mod:`zoneinfo` where ``datetime``
2+
subclasses returning ``-1`` for ``hour``, ``minute``, or ``second`` could
3+
incorrectly raise an error due to ``PyLong_AsLong()`` sentinel handling.

Modules/_zoneinfo.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,7 +2311,7 @@ get_local_timestamp(PyObject *dt, int64_t *local_ts)
23112311
}
23122312
hour = PyLong_AsLong(num);
23132313
Py_DECREF(num);
2314-
if (hour == -1) {
2314+
if (hour == -1 && PyErr_Occurred()) {
23152315
return -1;
23162316
}
23172317

@@ -2321,7 +2321,7 @@ get_local_timestamp(PyObject *dt, int64_t *local_ts)
23212321
}
23222322
minute = PyLong_AsLong(num);
23232323
Py_DECREF(num);
2324-
if (minute == -1) {
2324+
if (minute == -1 && PyErr_Occurred()) {
23252325
return -1;
23262326
}
23272327

@@ -2331,7 +2331,7 @@ get_local_timestamp(PyObject *dt, int64_t *local_ts)
23312331
}
23322332
second = PyLong_AsLong(num);
23332333
Py_DECREF(num);
2334-
if (second == -1) {
2334+
if (second == -1 && PyErr_Occurred()) {
23352335
return -1;
23362336
}
23372337
}

0 commit comments

Comments
 (0)