Skip to content

Commit

Permalink
[3.6] bpo-29097: Forego fold detection on windows for low timestamp v…
Browse files Browse the repository at this point in the history
…alues (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 <ammar_askar@hotmail.com>
  • Loading branch information
ammaraskar authored and abalkin committed Jul 27, 2018
1 parent 777cdd9 commit 6ea8a3a
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 2 deletions.
9 changes: 9 additions & 0 deletions Lib/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

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
Expand Down Expand Up @@ -1444,6 +1445,14 @@ def _fromtimestamp(cls, t, utc, tz):
# 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)
Expand Down
6 changes: 5 additions & 1 deletion Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def test_name_cleanup(self):
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):
Expand Down Expand Up @@ -4423,6 +4423,10 @@ def test_fromtimestamp_lord_howe(self):
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):
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
17 changes: 16 additions & 1 deletion Modules/_datetimemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -4309,7 +4309,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,
Expand Down

0 comments on commit 6ea8a3a

Please sign in to comment.