bpo-29097: Forego fold detection on windows for low timestamp values (GH-2385) (GH-8466)
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.
(cherry picked from commit 96d1e69a12
)
Co-authored-by: Ammar Askar <ammar_askar@hotmail.com>
This commit is contained in:
parent
ec02c58f5a
commit
973649342c
|
@ -6,6 +6,7 @@ time zone and DST data sources.
|
||||||
|
|
||||||
import time as _time
|
import time as _time
|
||||||
import math as _math
|
import math as _math
|
||||||
|
import sys
|
||||||
|
|
||||||
def _cmp(x, y):
|
def _cmp(x, y):
|
||||||
return 0 if x == y else 1 if x > y else -1
|
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.
|
# 23 hours at 1969-09-30 13:00:00 in Kwajalein.
|
||||||
# Let's probe 24 hours in the past to detect a transition:
|
# Let's probe 24 hours in the past to detect a transition:
|
||||||
max_fold_seconds = 24 * 3600
|
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]
|
y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]
|
||||||
probe1 = cls(y, m, d, hh, mm, ss, us, tz)
|
probe1 = cls(y, m, d, hh, mm, ss, us, tz)
|
||||||
trans = result - probe1 - timedelta(0, max_fold_seconds)
|
trans = result - probe1 - timedelta(0, max_fold_seconds)
|
||||||
|
|
|
@ -70,7 +70,7 @@ class TestModule(unittest.TestCase):
|
||||||
if not name.startswith('__') and not name.endswith('__'))
|
if not name.startswith('__') and not name.endswith('__'))
|
||||||
allowed = set(['MAXYEAR', 'MINYEAR', 'date', 'datetime',
|
allowed = set(['MAXYEAR', 'MINYEAR', 'date', 'datetime',
|
||||||
'datetime_CAPI', 'time', 'timedelta', 'timezone',
|
'datetime_CAPI', 'time', 'timedelta', 'timezone',
|
||||||
'tzinfo'])
|
'tzinfo', 'sys'])
|
||||||
self.assertEqual(names - allowed, set([]))
|
self.assertEqual(names - allowed, set([]))
|
||||||
|
|
||||||
def test_divide_and_round(self):
|
def test_divide_and_round(self):
|
||||||
|
@ -4955,6 +4955,11 @@ class TestLocalTimeDisambiguation(unittest.TestCase):
|
||||||
self.assertEqual(t0.fold, 0)
|
self.assertEqual(t0.fold, 0)
|
||||||
self.assertEqual(t1.fold, 1)
|
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')
|
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
|
||||||
def test_timestamp(self):
|
def test_timestamp(self):
|
||||||
dt0 = datetime(2014, 11, 2, 1, 30)
|
dt0 = datetime(2014, 11, 2, 1, 30)
|
||||||
|
|
|
@ -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.
|
|
@ -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);
|
second = Py_MIN(59, tm.tm_sec);
|
||||||
|
|
||||||
/* local timezone requires to compute fold */
|
/* 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;
|
long long probe_seconds, result_seconds, transition;
|
||||||
|
|
||||||
result_seconds = utc_to_seconds(year, month, day,
|
result_seconds = utc_to_seconds(year, month, day,
|
||||||
|
|
Loading…
Reference in New Issue