Skip to content

Commit dcc80e0

Browse files
committed
Support negative datetime.datetime.timestamp values from naive datetimes on Windows
1 parent 4eacf38 commit dcc80e0

File tree

4 files changed

+26
-2
lines changed

4 files changed

+26
-2
lines changed

Lib/_pydatetime.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import time as _time
1010
import math as _math
11-
import sys
11+
import os
1212
from operator import index as _index
1313

1414
def _cmp(x, y):
@@ -1877,7 +1877,7 @@ def _fromtimestamp(cls, t, utc, tz):
18771877
# thus we can't perform fold detection for values of time less
18781878
# than the max time fold. See comments in _datetimemodule's
18791879
# version of this method for more details.
1880-
if t < max_fold_seconds and sys.platform.startswith("win"):
1880+
if t < max_fold_seconds and os.name == 'nt':
18811881
return result
18821882

18831883
y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]
@@ -2050,6 +2050,9 @@ def local(u):
20502050
def timestamp(self):
20512051
"Return POSIX timestamp as float"
20522052
if self._tzinfo is None:
2053+
if self < _NAIVE_EPOCH and os.name == 'nt':
2054+
# Windows converters throw an OSError for negative values.
2055+
return (self - _NAIVE_EPOCH).total_seconds()
20532056
s = self._mktime()
20542057
return s + self.microsecond / 1e6
20552058
else:
@@ -2561,6 +2564,7 @@ def _name_from_offset(delta):
25612564
timezone.min = timezone._create(-timedelta(hours=23, minutes=59))
25622565
timezone.max = timezone._create(timedelta(hours=23, minutes=59))
25632566
_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
2567+
_NAIVE_EPOCH = datetime(1970, 1, 1)
25642568

25652569
# Some time zone algebra. For a datetime x, let
25662570
# x.n = x stripped of its timezone -- its naive time.

Lib/test/datetimetester.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2671,6 +2671,10 @@ def test_timestamp_aware(self):
26712671
self.assertEqual(t.timestamp(),
26722672
18000 + 3600 + 2*60 + 3 + 4*1e-6)
26732673

2674+
def test_naive_timestamp_before_epoch(self):
2675+
# Before #81708 this raised OSError on Windows.
2676+
self.assertLess(self.theclass(1969,1,1).timestamp(), 0)
2677+
26742678
@support.run_with_tz('MSK-03') # Something east of Greenwich
26752679
def test_microsecond_rounding(self):
26762680
def utcfromtimestamp(*args, **kwargs):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Support negative :meth:`datetime.datetime.timestamp` values from naive
2+
datetimes on Windows by subtracting a naive epoch. Patch by John Keith Hohm.

Modules/_datetimemodule.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6869,6 +6869,20 @@ datetime_timestamp(PyObject *op, PyObject *Py_UNUSED(dummy))
68696869
result = delta_total_seconds(delta, NULL);
68706870
Py_DECREF(delta);
68716871
}
6872+
#ifdef MS_WINDOWS
6873+
else if (GET_YEAR(self) < 1970) {
6874+
PyObject *naive_epoch = new_datetime(1970, 1, 1, 0, 0, 0, 0, Py_None, 0);
6875+
if (naive_epoch == NULL) {
6876+
return NULL;
6877+
}
6878+
PyObject *delta = datetime_subtract(op, naive_epoch);
6879+
Py_DECREF(naive_epoch);
6880+
if (delta == NULL)
6881+
return NULL;
6882+
result = delta_total_seconds(delta, NULL);
6883+
Py_DECREF(delta);
6884+
}
6885+
#endif
68726886
else {
68736887
long long seconds;
68746888
seconds = local_to_seconds(GET_YEAR(self),

0 commit comments

Comments
 (0)