diff -r 0ad37807dd4c Lib/datetime.py --- a/Lib/datetime.py Sat Sep 05 00:13:47 2015 +0200 +++ b/Lib/datetime.py Sat Sep 05 00:20:54 2015 +0200 @@ -1357,49 +1357,42 @@ class datetime(date): 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_half_up(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()). A timezone info object may be passed in as well. """ - _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 @classmethod def utcfromtimestamp(cls, t): - "Construct a UTC datetime from a POSIX timestamp (like time.time())." - 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) + """Construct a naive UTC datetime from a POSIX timestamp.""" + return cls._fromtimestamp(t, True, None) # XXX This is supposed to do better than we *can* do by using time.time(), # XXX if the platform supports a more accurate way. The C implementation diff -r 0ad37807dd4c Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py Sat Sep 05 00:13:47 2015 +0200 +++ b/Lib/test/datetimetester.py Sat Sep 05 00:20:54 2015 +0200 @@ -656,6 +656,8 @@ class TestTimeDelta(HarmlessMixedCompari eq(td(milliseconds=-0.6/1000), td(microseconds=-1)) eq(td(seconds=0.5/10**6), td(microseconds=1)) eq(td(seconds=-0.5/10**6), td(microseconds=-1)) + eq(td(seconds=1/2**7), td(microseconds=7813)) + eq(td(seconds=-1/2**7), td(microseconds=-7813)) # Rounding due to contributions from more than one field. us_per_hour = 3600e6 @@ -1830,6 +1832,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: @@ -1840,22 +1843,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(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, 7813) + t = fts(-1/2**7) + self.assertEqual(t.second, 59) + self.assertEqual(t.microsecond, 992187) def test_insane_fromtimestamp(self): # It's possible that some platform maps time_t to double, diff -r 0ad37807dd4c Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Sat Sep 05 00:13:47 2015 +0200 +++ b/Modules/_datetimemodule.c Sat Sep 05 00:20:54 2015 +0200 @@ -4107,7 +4107,8 @@ datetime_from_timestamp(PyObject *cls, T time_t timet; long us; - if (_PyTime_ObjectToTimeval(timestamp, &timet, &us, _PyTime_ROUND_DOWN) == -1) + if (_PyTime_ObjectToTimeval(timestamp, + &timet, &us, _PyTime_ROUND_HALF_UP) == -1) return NULL; return datetime_from_timet_and_us(cls, f, timet, (int)us, tzinfo); }