Skip to content

Commit 2ec5bd6

Browse files
committed
Issue #23517: fromtimestamp() and utcfromtimestamp() methods of
datetime.datetime now round microseconds to nearest with ties going away from zero (ROUND_HALF_UP), as Python 2 and Python older than 3.3, instead of rounding towards -Infinity (ROUND_FLOOR).
1 parent 0fa5ef7 commit 2ec5bd6

4 files changed

Lines changed: 14 additions & 8 deletions

File tree

Lib/datetime.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,7 +1384,7 @@ def fromtimestamp(cls, t, tz=None):
13841384
converter = _time.localtime if tz is None else _time.gmtime
13851385

13861386
t, frac = divmod(t, 1.0)
1387-
us = int(frac * 1e6)
1387+
us = _round_half_up(frac * 1e6)
13881388

13891389
# If timestamp is less than one microsecond smaller than a
13901390
# full second, us can be rounded up to 1000000. In this case,
@@ -1404,7 +1404,7 @@ def fromtimestamp(cls, t, tz=None):
14041404
def utcfromtimestamp(cls, t):
14051405
"""Construct a naive UTC datetime from a POSIX timestamp."""
14061406
t, frac = divmod(t, 1.0)
1407-
us = int(frac * 1e6)
1407+
us = _round_half_up(frac * 1e6)
14081408

14091409
# If timestamp is less than one microsecond smaller than a
14101410
# full second, us can be rounded up to 1000000. In this case,

Lib/test/datetimetester.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1847,6 +1847,7 @@ def test_microsecond_rounding(self):
18471847
zero = fts(0)
18481848
self.assertEqual(zero.second, 0)
18491849
self.assertEqual(zero.microsecond, 0)
1850+
one = fts(1e-6)
18501851
try:
18511852
minus_one = fts(-1e-6)
18521853
except OSError:
@@ -1857,22 +1858,22 @@ def test_microsecond_rounding(self):
18571858
self.assertEqual(minus_one.microsecond, 999999)
18581859

18591860
t = fts(-1e-8)
1860-
self.assertEqual(t, minus_one)
1861+
self.assertEqual(t, zero)
18611862
t = fts(-9e-7)
18621863
self.assertEqual(t, minus_one)
18631864
t = fts(-1e-7)
1864-
self.assertEqual(t, minus_one)
1865+
self.assertEqual(t, zero)
18651866

18661867
t = fts(1e-7)
18671868
self.assertEqual(t, zero)
18681869
t = fts(9e-7)
1869-
self.assertEqual(t, zero)
1870+
self.assertEqual(t, one)
18701871
t = fts(0.99999949)
18711872
self.assertEqual(t.second, 0)
18721873
self.assertEqual(t.microsecond, 999999)
18731874
t = fts(0.9999999)
1874-
self.assertEqual(t.second, 0)
1875-
self.assertEqual(t.microsecond, 999999)
1875+
self.assertEqual(t.second, 1)
1876+
self.assertEqual(t.microsecond, 0)
18761877

18771878
def test_insane_fromtimestamp(self):
18781879
# It's possible that some platform maps time_t to double,

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ Core and Builtins
1717
Library
1818
-------
1919

20+
- Issue #23517: fromtimestamp() and utcfromtimestamp() methods of
21+
datetime.datetime now round microseconds to nearest with ties going away from
22+
zero (ROUND_HALF_UP), as Python 2 and Python older than 3.3, instead of
23+
rounding towards -Infinity (ROUND_FLOOR).
24+
2025
- Issue #23517: datetime.timedelta constructor now rounds microseconds to
2126
nearest with ties going away from zero (ROUND_HALF_UP), as Python 2 and
2227
Python older than 3.3, instead of rounding to nearest with ties going to

Modules/_datetimemodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4078,7 +4078,7 @@ datetime_from_timestamp(PyObject *cls, TM_FUNC f, PyObject *timestamp,
40784078
long us;
40794079

40804080
if (_PyTime_ObjectToTimeval(timestamp,
4081-
&timet, &us, _PyTime_ROUND_FLOOR) == -1)
4081+
&timet, &us, _PyTime_ROUND_HALF_UP) == -1)
40824082
return NULL;
40834083

40844084
return datetime_from_timet_and_us(cls, f, timet, (int)us, tzinfo);

0 commit comments

Comments
 (0)