Merge 3.4 (datetime rounding)

This commit is contained in:
Victor Stinner 2015-09-18 14:50:18 +02:00
commit 84ff4abd79
4 changed files with 110 additions and 45 deletions

View file

@ -1365,6 +1365,26 @@ class datetime(date):
"""timezone info object"""
return self._tzinfo
@classmethod
def _fromtimestamp(cls, t, utc, tz):
"""Construct a datetime from a POSIX timestamp (like time.time()).
A timezone info object may be passed in as well.
"""
frac, t = _math.modf(t)
us = round(frac * 1e6)
if us >= 1000000:
t += 1
us -= 1000000
elif us < 0:
t -= 1
us += 1000000
converter = _time.gmtime if utc else _time.localtime
y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
ss = min(ss, 59) # clamp out leap seconds if the platform has them
return cls(y, m, d, hh, mm, ss, us, tz)
@classmethod
def fromtimestamp(cls, t, tz=None):
"""Construct a datetime from a POSIX timestamp (like time.time()).
@ -1373,21 +1393,7 @@ class datetime(date):
"""
_check_tzinfo_arg(tz)
converter = _time.localtime if tz is None else _time.gmtime
t, frac = divmod(t, 1.0)
us = int(frac * 1e6)
# If timestamp is less than one microsecond smaller than a
# full second, us can be rounded up to 1000000. In this case,
# roll over to seconds, otherwise, ValueError is raised
# by the constructor.
if us == 1000000:
t += 1
us = 0
y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
ss = min(ss, 59) # clamp out leap seconds if the platform has them
result = cls(y, m, d, hh, mm, ss, us, tz)
result = cls._fromtimestamp(t, tz is not None, tz)
if tz is not None:
result = tz.fromutc(result)
return result
@ -1395,19 +1401,7 @@ class datetime(date):
@classmethod
def utcfromtimestamp(cls, t):
"""Construct a naive UTC datetime from a POSIX timestamp."""
t, frac = divmod(t, 1.0)
us = int(frac * 1e6)
# If timestamp is less than one microsecond smaller than a
# full second, us can be rounded up to 1000000. In this case,
# roll over to seconds, otherwise, ValueError is raised
# by the constructor.
if us == 1000000:
t += 1
us = 0
y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t)
ss = min(ss, 59) # clamp out leap seconds if the platform has them
return cls(y, m, d, hh, mm, ss, us)
return cls._fromtimestamp(t, True, None)
@classmethod
def now(cls, tz=None):

View file

@ -663,11 +663,15 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
eq(td(milliseconds=0.5/1000), td(microseconds=0))
eq(td(milliseconds=-0.5/1000), td(microseconds=0))
eq(td(milliseconds=-0.5/1000), td(microseconds=-0))
eq(td(milliseconds=0.6/1000), td(microseconds=1))
eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
eq(td(milliseconds=1.5/1000), td(microseconds=2))
eq(td(milliseconds=-1.5/1000), td(microseconds=-2))
eq(td(seconds=0.5/10**6), td(microseconds=0))
eq(td(seconds=-0.5/10**6), td(microseconds=0))
eq(td(seconds=-0.5/10**6), td(microseconds=-0))
eq(td(seconds=1/2**7), td(microseconds=7812))
eq(td(seconds=-1/2**7), td(microseconds=-7812))
# Rounding due to contributions from more than one field.
us_per_hour = 3600e6
@ -1851,6 +1855,7 @@ class TestDateTime(TestDate):
zero = fts(0)
self.assertEqual(zero.second, 0)
self.assertEqual(zero.microsecond, 0)
one = fts(1e-6)
try:
minus_one = fts(-1e-6)
except OSError:
@ -1861,22 +1866,28 @@ class TestDateTime(TestDate):
self.assertEqual(minus_one.microsecond, 999999)
t = fts(-1e-8)
self.assertEqual(t, minus_one)
self.assertEqual(t, zero)
t = fts(-9e-7)
self.assertEqual(t, minus_one)
t = fts(-1e-7)
self.assertEqual(t, minus_one)
self.assertEqual(t, zero)
t = fts(-1/2**7)
self.assertEqual(t.second, 59)
self.assertEqual(t.microsecond, 992188)
t = fts(1e-7)
self.assertEqual(t, zero)
t = fts(9e-7)
self.assertEqual(t, zero)
self.assertEqual(t, one)
t = fts(0.99999949)
self.assertEqual(t.second, 0)
self.assertEqual(t.microsecond, 999999)
t = fts(0.9999999)
self.assertEqual(t.second, 1)
self.assertEqual(t.microsecond, 0)
t = fts(1/2**7)
self.assertEqual(t.second, 0)
self.assertEqual(t.microsecond, 999999)
self.assertEqual(t.microsecond, 7812)
def test_insane_fromtimestamp(self):
# It's possible that some platform maps time_t to double,