cpython: 383c0238b5b0 (original) (raw)
--- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1989,6 +1989,42 @@ class TestDateTime(TestDate): self.assertEqual(t.second, 0) self.assertEqual(t.microsecond, 7812)
- def test_timestamp_limits(self):
# minimum timestamp[](#l1.8)
min_dt = self.theclass.min.replace(tzinfo=timezone.utc)[](#l1.9)
min_ts = min_dt.timestamp()[](#l1.10)
# date 0001-01-01 00:00:00+00:00: timestamp=-62135596800[](#l1.11)
self.assertEqual(self.theclass.fromtimestamp(min_ts, tz=timezone.utc),[](#l1.12)
min_dt)[](#l1.13)
# maximum timestamp: set seconds to zero to avoid rounding issues[](#l1.15)
max_dt = self.theclass.max.replace(tzinfo=timezone.utc,[](#l1.16)
second=0, microsecond=0)[](#l1.17)
max_ts = max_dt.timestamp()[](#l1.18)
# date 9999-12-31 23:59:00+00:00: timestamp 253402300740[](#l1.19)
self.assertEqual(self.theclass.fromtimestamp(max_ts, tz=timezone.utc),[](#l1.20)
max_dt)[](#l1.21)
# number of seconds greater than 1 year: make sure that the new date[](#l1.23)
# is not valid in datetime.datetime limits[](#l1.24)
delta = 3600 * 24 * 400[](#l1.25)
# too small[](#l1.27)
ts = min_ts - delta[](#l1.28)
# converting a Python int to C time_t can raise a OverflowError,[](#l1.29)
# especially on 32-bit platforms.[](#l1.30)
with self.assertRaises((ValueError, OverflowError)):[](#l1.31)
self.theclass.fromtimestamp(ts)[](#l1.32)
with self.assertRaises((ValueError, OverflowError)):[](#l1.33)
self.theclass.utcfromtimestamp(ts)[](#l1.34)
# too big[](#l1.36)
ts = max_dt.timestamp() + delta[](#l1.37)
with self.assertRaises((ValueError, OverflowError)):[](#l1.38)
self.theclass.fromtimestamp(ts)[](#l1.39)
with self.assertRaises((ValueError, OverflowError)):[](#l1.40)
self.theclass.utcfromtimestamp(ts)[](#l1.41)
+ def test_insane_fromtimestamp(self): # It's possible that some platform maps time_t to double, # and that this test will fail there. This test should
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -57,6 +57,9 @@ Extension Modules Library ------- +- Issue #29100: Fix datetime.fromtimestamp() regression introduced in Python
- Issue #29519: Fix weakref spewing exceptions during interpreter shutdown when used with a rare combination of multiprocessing and custom codecs.
--- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -120,6 +120,8 @@ static PyTypeObject PyDateTime_TimeType; static PyTypeObject PyDateTime_TZInfoType; static PyTypeObject PyDateTime_TimeZoneType; +static int check_tzinfo_subclass(PyObject *p); + _Py_IDENTIFIER(as_integer_ratio); _Py_IDENTIFIER(fromutc); _Py_IDENTIFIER(isoformat); @@ -400,8 +402,7 @@ check_date_args(int year, int month, int { if (year < MINYEAR || year > MAXYEAR) {
PyErr_SetString(PyExc_ValueError,[](#l3.16)
"year is out of range");[](#l3.17)
} if (month < 1 || month > 12) { @@ -672,6 +673,10 @@ new_date_ex(int year, int month, int day { PyDateTime_Date *self;PyErr_Format(PyExc_ValueError, "year %i is out of range", year);[](#l3.18) return -1;[](#l3.19)
- if (check_date_args(year, month, day) < 0) {
return NULL;[](#l3.27)
- }
+ self = (PyDateTime_Date *) (type->tp_alloc(type, 0)); if (self != NULL) set_date_fields(self, year, month, day); @@ -689,6 +694,16 @@ new_datetime_ex2(int year, int month, in PyDateTime_DateTime *self; char aware = tzinfo != Py_None;
- if (check_date_args(year, month, day) < 0) {
return NULL;[](#l3.38)
- }
- if (check_time_args(hour, minute, second, usecond, fold) < 0) {
return NULL;[](#l3.41)
- }
- if (check_tzinfo_subclass(tzinfo) < 0) {
return NULL;[](#l3.44)
- }
+ self = (PyDateTime_DateTime *) (type->tp_alloc(type, aware)); if (self != NULL) { self->hastzinfo = aware; @@ -726,6 +741,13 @@ new_time_ex2(int hour, int minute, int s PyDateTime_Time *self; char aware = tzinfo != Py_None;
- if (check_time_args(hour, minute, second, usecond, fold) < 0) {
return NULL;[](#l3.55)
- }
- if (check_tzinfo_subclass(tzinfo) < 0) {
return NULL;[](#l3.58)
- }
+ self = (PyDateTime_Time *) (type->tp_alloc(type, aware)); if (self != NULL) { self->hastzinfo = aware; @@ -2500,8 +2522,6 @@ date_new(PyTypeObject *type, PyObject *a if (PyArg_ParseTupleAndKeywords(args, kw, "iii", date_kws, &year, &month, &day)) {
if (check_date_args(year, month, day) < 0)[](#l3.68)
} return self; @@ -3586,10 +3606,6 @@ time_new(PyTypeObject *type, PyObject *a if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO$i", time_kws, &hour, &minute, &second, &usecond, &tzinfo, &fold)) {return NULL;[](#l3.69) self = new_date_ex(year, month, day, type);[](#l3.70)
if (check_time_args(hour, minute, second, usecond, fold) < 0)[](#l3.77)
return NULL;[](#l3.78)
if (check_tzinfo_subclass(tzinfo) < 0)[](#l3.79)
} @@ -4176,12 +4192,6 @@ datetime_new(PyTypeObject *type, PyObjec if (PyArg_ParseTupleAndKeywords(args, kw, "iii|iiiiO$i", datetime_kws, &year, &month, &day, &hour, &minute, &second, &usecond, &tzinfo, &fold)) {return NULL;[](#l3.80) self = new_time_ex2(hour, minute, second, usecond, tzinfo, fold,[](#l3.81) type);[](#l3.82)
if (check_date_args(year, month, day) < 0)[](#l3.88)
return NULL;[](#l3.89)
if (check_time_args(hour, minute, second, usecond, fold) < 0)[](#l3.90)
return NULL;[](#l3.91)
if (check_tzinfo_subclass(tzinfo) < 0)[](#l3.92)
return NULL;[](#l3.93) self = new_datetime_ex2(year, month, day,[](#l3.94) hour, minute, second, usecond,[](#l3.95) tzinfo, fold, type);[](#l3.96)
@@ -4203,7 +4213,15 @@ static long long utc_to_seconds(int year, int month, int day, int hour, int minute, int second) {
- /* ymd_to_ord() doesn't support year <= 0 */
- if (year < MINYEAR || year > MAXYEAR) {
PyErr_Format(PyExc_ValueError, "year %i is out of range", year);[](#l3.106)
return -1;[](#l3.107)
- }
- ordinal = ymd_to_ord(year, month, day); return ((ordinal * 24 + hour) * 60 + minute) * 60 + second; }
@@ -4219,7 +4237,6 @@ local(long long u) "timestamp out of range for platform time_t"); return -1; }
- /* XXX: add bounds checking */ if (_PyTime_localtime(t, &local_time) != 0) return -1; return utc_to_seconds(local_time.tm_year + 1900, @@ -4257,6 +4274,7 @@ datetime_from_timet_and_us(PyObject *cls */ second = Py_MIN(59, tm.tm_sec);
- /* local timezone requires to compute fold */ if (tzinfo == Py_None && f == _PyTime_localtime) { long long probe_seconds, result_seconds, transition;
@@ -4516,12 +4534,13 @@ add_datetime_timedelta(PyDateTime_DateTi assert(factor == 1 || factor == -1); if (normalize_datetime(&year, &month, &day,
&hour, &minute, &second, µsecond) < 0)[](#l3.134)
&hour, &minute, &second, µsecond) < 0) {[](#l3.135) return NULL;[](#l3.136)
- else
return new_datetime(year, month, day,[](#l3.138)
hour, minute, second, microsecond,[](#l3.139)
HASTZINFO(date) ? date->tzinfo : Py_None, 0);[](#l3.140)