diff options
-rw-r--r-- | Lib/datetime.py | 9 | ||||
-rw-r--r-- | Lib/test/datetimetester.py | 7 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Windows/2018-05-16-11-31-17.bpo-29097.9mqEuI.rst | 3 | ||||
-rw-r--r-- | Modules/_datetimemodule.c | 17 |
4 files changed, 34 insertions, 2 deletions
diff --git a/Lib/datetime.py b/Lib/datetime.py index 5e922c8..cff9203 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -6,6 +6,7 @@ time zone and DST data sources. import time as _time import math as _math +import sys def _cmp(x, y): return 0 if x == y else 1 if x > y else -1 @@ -1572,6 +1573,14 @@ class datetime(date): # 23 hours at 1969-09-30 13:00:00 in Kwajalein. # Let's probe 24 hours in the past to detect a transition: max_fold_seconds = 24 * 3600 + + # On Windows localtime_s throws an OSError for negative values, + # thus we can't perform fold detection for values of time less + # than the max time fold. See comments in _datetimemodule's + # version of this method for more details. + if t < max_fold_seconds and sys.platform.startswith("win"): + return result + y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6] probe1 = cls(y, m, d, hh, mm, ss, us, tz) trans = result - probe1 - timedelta(0, max_fold_seconds) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 7d4cdac..f647a23 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -70,7 +70,7 @@ class TestModule(unittest.TestCase): if not name.startswith('__') and not name.endswith('__')) allowed = set(['MAXYEAR', 'MINYEAR', 'date', 'datetime', 'datetime_CAPI', 'time', 'timedelta', 'timezone', - 'tzinfo']) + 'tzinfo', 'sys']) self.assertEqual(names - allowed, set([])) def test_divide_and_round(self): @@ -4955,6 +4955,11 @@ class TestLocalTimeDisambiguation(unittest.TestCase): self.assertEqual(t0.fold, 0) self.assertEqual(t1.fold, 1) + def test_fromtimestamp_low_fold_detection(self): + # Ensure that fold detection doesn't cause an + # OSError for really low values, see bpo-29097 + self.assertEqual(datetime.fromtimestamp(0).fold, 0) + @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') def test_timestamp(self): dt0 = datetime(2014, 11, 2, 1, 30) diff --git a/Misc/NEWS.d/next/Windows/2018-05-16-11-31-17.bpo-29097.9mqEuI.rst b/Misc/NEWS.d/next/Windows/2018-05-16-11-31-17.bpo-29097.9mqEuI.rst new file mode 100644 index 0000000..a59efc7 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2018-05-16-11-31-17.bpo-29097.9mqEuI.rst @@ -0,0 +1,3 @@ +Fix bug where :meth:`datetime.fromtimestamp` erronously throws an +:exc:`OSError` on Windows for values between 0 and 86400. +Patch by Ammar Askar. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 31aa88d..076912d 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -4625,7 +4625,22 @@ datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us, second = Py_MIN(59, tm.tm_sec); /* local timezone requires to compute fold */ - if (tzinfo == Py_None && f == _PyTime_localtime) { + if (tzinfo == Py_None && f == _PyTime_localtime + /* On Windows, passing a negative value to local results + * in an OSError because localtime_s on Windows does + * not support negative timestamps. Unfortunately this + * means that fold detection for time values between + * 0 and max_fold_seconds will result in an identical + * error since we subtract max_fold_seconds to detect a + * fold. However, since we know there haven't been any + * folds in the interval [0, max_fold_seconds) in any + * timezone, we can hackily just forego fold detection + * for this time range. + */ +#ifdef MS_WINDOWS + && (timet - max_fold_seconds > 0) +#endif + ) { long long probe_seconds, result_seconds, transition; result_seconds = utc_to_seconds(year, month, day, |