[Python-checkins] cpython (3.6): Fix datetime.fromtimestamp(): check bounds

victor.stinner python-checkins at python.org
Fri Feb 10 04:35:00 EST 2017


https://hg.python.org/cpython/rev/383c0238b5b0
changeset: 106488:383c0238b5b0
branch: 3.6
parent: 106486:c5267272e66a
user: Victor Stinner <victor.stinner at gmail.com>
date: Fri Feb 10 10:34:02 2017 +0100
summary:
 Fix datetime.fromtimestamp(): check bounds
Issue #29100: Fix datetime.fromtimestamp() regression introduced in Python
3.6.0: check minimum and maximum years.
files:
 Lib/test/datetimetester.py | 36 +++++++++++++++
 Misc/NEWS | 3 +
 Modules/_datetimemodule.c | 61 +++++++++++++++++--------
 3 files changed, 79 insertions(+), 21 deletions(-)
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -1989,6 +1989,42 @@
 self.assertEqual(t.second, 0)
 self.assertEqual(t.microsecond, 7812)
 
+ def test_timestamp_limits(self):
+ # minimum timestamp
+ min_dt = self.theclass.min.replace(tzinfo=timezone.utc)
+ min_ts = min_dt.timestamp()
+ # date 0001年01月01日 00:00:00+00:00: timestamp=-62135596800
+ self.assertEqual(self.theclass.fromtimestamp(min_ts, tz=timezone.utc),
+ min_dt)
+
+ # maximum timestamp: set seconds to zero to avoid rounding issues
+ max_dt = self.theclass.max.replace(tzinfo=timezone.utc,
+ second=0, microsecond=0)
+ max_ts = max_dt.timestamp()
+ # date 9999年12月31日 23:59:00+00:00: timestamp 253402300740
+ self.assertEqual(self.theclass.fromtimestamp(max_ts, tz=timezone.utc),
+ max_dt)
+
+ # number of seconds greater than 1 year: make sure that the new date
+ # is not valid in datetime.datetime limits
+ delta = 3600 * 24 * 400
+
+ # too small
+ ts = min_ts - delta
+ # converting a Python int to C time_t can raise a OverflowError,
+ # especially on 32-bit platforms.
+ with self.assertRaises((ValueError, OverflowError)):
+ self.theclass.fromtimestamp(ts)
+ with self.assertRaises((ValueError, OverflowError)):
+ self.theclass.utcfromtimestamp(ts)
+
+ # too big
+ ts = max_dt.timestamp() + delta
+ with self.assertRaises((ValueError, OverflowError)):
+ self.theclass.fromtimestamp(ts)
+ with self.assertRaises((ValueError, OverflowError)):
+ self.theclass.utcfromtimestamp(ts)
+
 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
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -57,6 +57,9 @@
 Library
 -------
 
+- Issue #29100: Fix datetime.fromtimestamp() regression introduced in Python
+ 3.6.0: check minimum and maximum years.
+
 - Issue #29519: Fix weakref spewing exceptions during interpreter shutdown
 when used with a rare combination of multiprocessing and custom codecs.
 
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -120,6 +120,8 @@
 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 @@
 {
 
 if (year < MINYEAR || year > MAXYEAR) {
- PyErr_SetString(PyExc_ValueError,
- "year is out of range");
+ PyErr_Format(PyExc_ValueError, "year %i is out of range", year);
 return -1;
 }
 if (month < 1 || month > 12) {
@@ -672,6 +673,10 @@
 {
 PyDateTime_Date *self;
 
+ if (check_date_args(year, month, day) < 0) {
+ return NULL;
+ }
+
 self = (PyDateTime_Date *) (type->tp_alloc(type, 0));
 if (self != NULL)
 set_date_fields(self, year, month, day);
@@ -689,6 +694,16 @@
 PyDateTime_DateTime *self;
 char aware = tzinfo != Py_None;
 
+ if (check_date_args(year, month, day) < 0) {
+ return NULL;
+ }
+ if (check_time_args(hour, minute, second, usecond, fold) < 0) {
+ return NULL;
+ }
+ if (check_tzinfo_subclass(tzinfo) < 0) {
+ return NULL;
+ }
+
 self = (PyDateTime_DateTime *) (type->tp_alloc(type, aware));
 if (self != NULL) {
 self->hastzinfo = aware;
@@ -726,6 +741,13 @@
 PyDateTime_Time *self;
 char aware = tzinfo != Py_None;
 
+ if (check_time_args(hour, minute, second, usecond, fold) < 0) {
+ return NULL;
+ }
+ if (check_tzinfo_subclass(tzinfo) < 0) {
+ return NULL;
+ }
+
 self = (PyDateTime_Time *) (type->tp_alloc(type, aware));
 if (self != NULL) {
 self->hastzinfo = aware;
@@ -2500,8 +2522,6 @@
 
 if (PyArg_ParseTupleAndKeywords(args, kw, "iii", date_kws,
 &year, &month, &day)) {
- if (check_date_args(year, month, day) < 0)
- return NULL;
 self = new_date_ex(year, month, day, type);
 }
 return self;
@@ -3586,10 +3606,6 @@
 if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO$i", time_kws,
 &hour, &minute, &second, &usecond,
 &tzinfo, &fold)) {
- if (check_time_args(hour, minute, second, usecond, fold) < 0)
- return NULL;
- if (check_tzinfo_subclass(tzinfo) < 0)
- return NULL;
 self = new_time_ex2(hour, minute, second, usecond, tzinfo, fold,
 type);
 }
@@ -4176,12 +4192,6 @@
 if (PyArg_ParseTupleAndKeywords(args, kw, "iii|iiiiO$i", datetime_kws,
 &year, &month, &day, &hour, &minute,
 &second, &usecond, &tzinfo, &fold)) {
- if (check_date_args(year, month, day) < 0)
- return NULL;
- if (check_time_args(hour, minute, second, usecond, fold) < 0)
- return NULL;
- if (check_tzinfo_subclass(tzinfo) < 0)
- return NULL;
 self = new_datetime_ex2(year, month, day,
 hour, minute, second, usecond,
 tzinfo, fold, type);
@@ -4203,7 +4213,15 @@
 utc_to_seconds(int year, int month, int day,
 int hour, int minute, int second)
 {
- long long ordinal = ymd_to_ord(year, month, day);
+ long long ordinal;
+
+ /* ymd_to_ord() doesn't support year <= 0 */
+ if (year < MINYEAR || year > MAXYEAR) {
+ PyErr_Format(PyExc_ValueError, "year %i is out of range", year);
+ return -1;
+ }
+
+ ordinal = ymd_to_ord(year, month, day);
 return ((ordinal * 24 + hour) * 60 + minute) * 60 + second;
 }
 
@@ -4219,7 +4237,6 @@
 "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 @@
 */
 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 @@
 
 assert(factor == 1 || factor == -1);
 if (normalize_datetime(&year, &month, &day,
- &hour, &minute, &second, &microsecond) < 0)
+ &hour, &minute, &second, &microsecond) < 0) {
 return NULL;
- else
- return new_datetime(year, month, day,
- hour, minute, second, microsecond,
- HASTZINFO(date) ? date->tzinfo : Py_None, 0);
+ }
+
+ return new_datetime(year, month, day,
+ hour, minute, second, microsecond,
+ HASTZINFO(date) ? date->tzinfo : Py_None, 0);
 }
 
 static PyObject *
-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /