Skip to content

Commit 6ea8a3a

Browse files
ammaraskarabalkin
authored andcommitted
[3.6] bpo-29097: Forego fold detection on windows for low timestamp values (GH-2385) (GH-8498)
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 96d1e69) Co-authored-by: Ammar Askar <[email protected]>
1 parent 777cdd9 commit 6ea8a3a

File tree

4 files changed

+33
-2
lines changed

4 files changed

+33
-2
lines changed

Lib/datetime.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import time as _time
88
import math as _math
9+
import sys
910

1011
def _cmp(x, y):
1112
return 0 if x == y else 1 if x > y else -1
@@ -1444,6 +1445,14 @@ def _fromtimestamp(cls, t, utc, tz):
14441445
# 23 hours at 1969-09-30 13:00:00 in Kwajalein.
14451446
# Let's probe 24 hours in the past to detect a transition:
14461447
max_fold_seconds = 24 * 3600
1448+
1449+
# On Windows localtime_s throws an OSError for negative values,
1450+
# thus we can't perform fold detection for values of time less
1451+
# than the max time fold. See comments in _datetimemodule's
1452+
# version of this method for more details.
1453+
if t < max_fold_seconds and sys.platform.startswith("win"):
1454+
return result
1455+
14471456
y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]
14481457
probe1 = cls(y, m, d, hh, mm, ss, us, tz)
14491458
trans = result - probe1 - timedelta(0, max_fold_seconds)

Lib/test/datetimetester.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def test_name_cleanup(self):
6969
if not name.startswith('__') and not name.endswith('__'))
7070
allowed = set(['MAXYEAR', 'MINYEAR', 'date', 'datetime',
7171
'datetime_CAPI', 'time', 'timedelta', 'timezone',
72-
'tzinfo'])
72+
'tzinfo', 'sys'])
7373
self.assertEqual(names - allowed, set([]))
7474

7575
def test_divide_and_round(self):
@@ -4423,6 +4423,10 @@ def test_fromtimestamp_lord_howe(self):
44234423
self.assertEqual(t0.fold, 0)
44244424
self.assertEqual(t1.fold, 1)
44254425

4426+
def test_fromtimestamp_low_fold_detection(self):
4427+
# Ensure that fold detection doesn't cause an
4428+
# OSError for really low values, see bpo-29097
4429+
self.assertEqual(datetime.fromtimestamp(0).fold, 0)
44264430

44274431
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
44284432
def test_timestamp(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix bug where :meth:`datetime.fromtimestamp` erronously throws an
2+
:exc:`OSError` on Windows for values between 0 and 86400.
3+
Patch by Ammar Askar.

Modules/_datetimemodule.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4309,7 +4309,22 @@ datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us,
43094309
second = Py_MIN(59, tm.tm_sec);
43104310

43114311
/* local timezone requires to compute fold */
4312-
if (tzinfo == Py_None && f == _PyTime_localtime) {
4312+
if (tzinfo == Py_None && f == _PyTime_localtime
4313+
/* On Windows, passing a negative value to local results
4314+
* in an OSError because localtime_s on Windows does
4315+
* not support negative timestamps. Unfortunately this
4316+
* means that fold detection for time values between
4317+
* 0 and max_fold_seconds will result in an identical
4318+
* error since we subtract max_fold_seconds to detect a
4319+
* fold. However, since we know there haven't been any
4320+
* folds in the interval [0, max_fold_seconds) in any
4321+
* timezone, we can hackily just forego fold detection
4322+
* for this time range.
4323+
*/
4324+
#ifdef MS_WINDOWS
4325+
&& (timet - max_fold_seconds > 0)
4326+
#endif
4327+
) {
43134328
long long probe_seconds, result_seconds, transition;
43144329

43154330
result_seconds = utc_to_seconds(year, month, day,

0 commit comments

Comments
 (0)