]> granicus.if.org Git - python/commitdiff
bpo-29097: Forego fold detection on windows for low timestamp values (GH-2385)
authorAmmar Askar <ammar_askar@hotmail.com>
Wed, 25 Jul 2018 16:54:58 +0000 (09:54 -0700)
committerAlexander Belopolsky <abalkin@users.noreply.github.com>
Wed, 25 Jul 2018 16:54:58 +0000 (12:54 -0400)
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 timestamps between 0 and max_fold_seconds will result in this OSError since we subtract max_fold_seconds from the timestamp 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 on Windows.

Lib/datetime.py
Lib/test/datetimetester.py
Misc/NEWS.d/next/Windows/2018-05-16-11-31-17.bpo-29097.9mqEuI.rst [new file with mode: 0644]
Modules/_datetimemodule.c

index 5e922c80b017ffb2f36873a56ca42037c8425be6..cff92033c496a7936956960ab080e52b497e2ace 100644 (file)
@@ -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)
index 7d4cdac9f41a1e03d15ff892b511c568595c9666..f647a232f40442d4539a4bc21bf0a561e1ae3e56 100644 (file)
@@ -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 (file)
index 0000000..a59efc7
--- /dev/null
@@ -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.
index 31aa88d4a26a8634c1f1a143c2b55ebcb85087fd..076912d58f4af82a13b70498e89673f392b52396 100644 (file)
@@ -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,