Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(5)

Delta Between Two Patch Sets: Lib/datetime.py

Issue 23517: datetime.utcfromtimestamp rounds results incorrectly
Left Patch Set: Created 4 years, 7 months ago
Right Patch Set: Created 4 years, 6 months ago
 Left: Base Patch Set 1: None Patch Set 2: None Patch Set 3: None Right: Patch Set 1: None Patch Set 2: None Patch Set 3: None
« no previous file with change/comment | « no previous file | Lib/test/datetimetester.py » ('j') | Lib/test/datetimetester.py » ('J')
LEFTRIGHT
1 """Concrete date/time and related types. 1 """Concrete date/time and related types.
2 2
4 time zone and DST data sources. 4 time zone and DST data sources.
5 """ 5 """
6 6
7 import time as _time 7 import time as _time
8 import math as _math 8 import math as _math
9 9
10 def _cmp(x, y): 10 def _cmp(x, y):
(...skipping 281 matching lines...)
292 # round up if either r / b > 0.5, or r / b == 0.5 and q is odd. 292 # round up if either r / b > 0.5, or r / b == 0.5 and q is odd.
293 # The expression r / b > 0.5 is equivalent to 2 * r > b if b is 293 # The expression r / b > 0.5 is equivalent to 2 * r > b if b is
294 # positive, 2 * r < b if b negative. 294 # positive, 2 * r < b if b negative.
295 r *= 2 295 r *= 2
296 greater_than_half = r > b if b > 0 else r < b 296 greater_than_half = r > b if b > 0 else r < b
297 if greater_than_half or r == b and q % 2 == 1: 297 if greater_than_half or r == b and q % 2 == 1:
298 q += 1 298 q += 1
299 299
300 return q 300 return q
301 301
302 def _round_half_up(x):
303 """Round to nearest with ties going away from zero."""
304 if x >= 0.0:
305 return _math.floor(x + 0.5)
306 else:
307 return _math.ceil(x - 0.5)
308
309
310 class timedelta: 302 class timedelta:
311 """Represent the difference between two datetime objects. 303 """Represent the difference between two datetime objects.
312 304
313 Supported operators: 305 Supported operators:
314 306
316 - unary plus, minus, abs 308 - unary plus, minus, abs
317 - compare to timedelta 309 - compare to timedelta
318 - multiply, divide by int 310 - multiply, divide by int
319 311
(...skipping 63 matching lines...)
383 s += int(seconds) # can't overflow 375 s += int(seconds) # can't overflow
384 assert isinstance(s, int) 376 assert isinstance(s, int)
385 assert abs(s) <= 2 * 24 * 3600 377 assert abs(s) <= 2 * 24 * 3600
386 # seconds isn't referenced again before redefinition 378 # seconds isn't referenced again before redefinition
387 379
388 usdouble = secondsfrac * 1e6 380 usdouble = secondsfrac * 1e6
389 assert abs(usdouble) < 2.1e6 # exact value not critical 381 assert abs(usdouble) < 2.1e6 # exact value not critical
390 # secondsfrac isn't referenced again 382 # secondsfrac isn't referenced again
391 383
392 if isinstance(microseconds, float): 384 if isinstance(microseconds, float):
393 microseconds = _round_half_up(microseconds + usdouble) 385 microseconds += usdouble
386 microseconds = round(microseconds, 0)
387 seconds, microseconds = divmod(microseconds, 1e6)
388 assert microseconds == int(microseconds)
389 assert seconds == int(seconds)
390 days, seconds = divmod(seconds, 24.*3600.)
391 assert days == int(days)
392 assert seconds == int(seconds)
393 d += int(days)
394 s += int(seconds) # can't overflow
395 assert isinstance(s, int)
396 assert abs(s) <= 3 * 24 * 3600
397 else:
394 seconds, microseconds = divmod(microseconds, 1000000) 398 seconds, microseconds = divmod(microseconds, 1000000)
395 days, seconds = divmod(seconds, 24*3600) 399 days, seconds = divmod(seconds, 24*3600)
396 d += days 400 d += days
397 s += seconds 401 s += int(seconds) # can't overflow
398 else: 402 assert isinstance(s, int)
399 microseconds = int(microseconds) 403 assert abs(s) <= 3 * 24 * 3600
400 seconds, microseconds = divmod(microseconds, 1000000) 404 microseconds = float(microseconds)
401 days, seconds = divmod(seconds, 24*3600) 405 microseconds += usdouble
402 d += days 406 microseconds = round(microseconds, 0)
403 s += seconds
404 microseconds = _round_half_up(microseconds + usdouble)
405 assert isinstance(s, int)
406 assert isinstance(microseconds, int)
407 assert abs(s) <= 3 * 24 * 3600 407 assert abs(s) <= 3 * 24 * 3600
408 assert abs(microseconds) < 3.1e6 408 assert abs(microseconds) < 3.1e6
409 409
410 # Just a little bit of carrying possible for microseconds and seconds. 410 # Just a little bit of carrying possible for microseconds and seconds.
411 seconds, us = divmod(microseconds, 1000000) 411 assert isinstance(microseconds, float)
412 s += seconds 412 assert int(microseconds) == microseconds
413 us = int(microseconds)
414 seconds, us = divmod(us, 1000000)
415 s += seconds # cant't overflow
416 assert isinstance(s, int)
413 days, s = divmod(s, 24*3600) 417 days, s = divmod(s, 24*3600)
414 d += days 418 d += days
415 419
416 assert isinstance(d, int) 420 assert isinstance(d, int)
417 assert isinstance(s, int) and 0 <= s < 24*3600 421 assert isinstance(s, int) and 0 <= s < 24*3600
418 assert isinstance(us, int) and 0 <= us < 1000000 422 assert isinstance(us, int) and 0 <= us < 1000000
419 423
420 if abs(d) > 999999999:
421 raise OverflowError("timedelta # of days is too large: %d" % d)
422
423 self = object.__new__(cls) 424 self = object.__new__(cls)
425
424 self._days = d 426 self._days = d
425 self._seconds = s 427 self._seconds = s
426 self._microseconds = us 428 self._microseconds = us
429 if abs(d) > 999999999:
430 raise OverflowError("timedelta # of days is too large: %d" % d)
431
427 return self 432 return self
428 433
429 def __repr__(self): 434 def __repr__(self):
430 if self._microseconds: 435 if self._microseconds:
431 return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__, 436 return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__,
432 self._days, 437 self._days,
433 self._seconds, 438 self._seconds,
434 self._microseconds) 439 self._microseconds)
435 if self._seconds: 440 if self._seconds:
436 return "%s(%d, %d)" % ('datetime.' + self.__class__.__name__, 441 return "%s(%d, %d)" % ('datetime.' + self.__class__.__name__,
(...skipping 913 matching lines...)
1350 def microsecond(self): 1355 def microsecond(self):
1351 """microsecond (0-999999)""" 1356 """microsecond (0-999999)"""
1352 return self._microsecond 1357 return self._microsecond
1353 1358
1354 @property 1359 @property
1355 def tzinfo(self): 1360 def tzinfo(self):
1356 """timezone info object""" 1361 """timezone info object"""
1357 return self._tzinfo 1362 return self._tzinfo
1358 1363
1359 @classmethod 1364 @classmethod
1365 def _fromtimestamp(cls, t, utc, tz):
1366 """Construct a datetime from a POSIX timestamp (like time.time()).
1367
1368 A timezone info object may be passed in as well.
1369 """
1370 frac, t = _math.modf(t)
sasha 2015/09/10 21:14:58 You may find it easier to use t, frac = divmod(t,
1371 us = round(frac * 1e6)
1372 if us >= 1000000:
1373 t += 1
1374 us -= 1000000
1375 elif us < 0:
1376 t -= 1
1377 us += 1000000
1378
1379 converter = _time.gmtime if utc else _time.localtime
1380 y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
1381 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1382 return cls(y, m, d, hh, mm, ss, us, tz)
1383
1384 @classmethod
1360 def fromtimestamp(cls, t, tz=None): 1385 def fromtimestamp(cls, t, tz=None):
1361 """Construct a datetime from a POSIX timestamp (like time.time()). 1386 """Construct a datetime from a POSIX timestamp (like time.time()).
1362 1387
1363 A timezone info object may be passed in as well. 1388 A timezone info object may be passed in as well.
1364 """ 1389 """
1365
1366 _check_tzinfo_arg(tz) 1390 _check_tzinfo_arg(tz)
1367 1391
1368 converter = _time.localtime if tz is None else _time.gmtime 1392 result = cls._fromtimestamp(t, tz is not None, tz)
1369
1370 t, frac = divmod(t, 1.0)
1371 us = int(frac * 1e6)
1372
1373 # If timestamp is less than one microsecond smaller than a
1374 # full second, us can be rounded up to 1000000. In this case,
1375 # roll over to seconds, otherwise, ValueError is raised
1376 # by the constructor.
1377 if us == 1000000:
1378 t += 1
1379 us = 0
1380 y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
1381 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1382 result = cls(y, m, d, hh, mm, ss, us, tz)
1383 if tz is not None: 1393 if tz is not None:
1384 result = tz.fromutc(result) 1394 result = tz.fromutc(result)
1385 return result 1395 return result
1386 1396
1387 @classmethod 1397 @classmethod
1388 def utcfromtimestamp(cls, t): 1398 def utcfromtimestamp(cls, t):
1389 "Construct a UTC datetime from a POSIX timestamp (like time.time())." 1399 """Construct a naive UTC datetime from a POSIX timestamp."""
1390 t, frac = divmod(t, 1.0) 1400 return cls._fromtimestamp(t, True, None)
1391 us = int(frac * 1e6)
1392
1393 # If timestamp is less than one microsecond smaller than a
1394 # full second, us can be rounded up to 1000000. In this case,
1395 # roll over to seconds, otherwise, ValueError is raised
1396 # by the constructor.
1397 if us == 1000000:
1398 t += 1
1399 us = 0
1400 y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t)
1401 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1402 return cls(y, m, d, hh, mm, ss, us)
1403 1401
1404 # XXX This is supposed to do better than we *can* do by using time.time(), 1402 # XXX This is supposed to do better than we *can* do by using time.time(),
1405 # XXX if the platform supports a more accurate way. The C implementation 1403 # XXX if the platform supports a more accurate way. The C implementation
1406 # XXX uses gettimeofday on platforms that have it, but that isn't 1404 # XXX uses gettimeofday on platforms that have it, but that isn't
1407 # XXX available from Python. So now() may return different results 1405 # XXX available from Python. So now() may return different results
1408 # XXX across the implementations. 1406 # XXX across the implementations.
1409 @classmethod 1407 @classmethod
1410 def now(cls, tz=None): 1408 def now(cls, tz=None):
1411 "Construct a datetime from time.time() and optional time zone info." 1409 "Construct a datetime from time.time() and optional time zone info."
1412 t = _time.time() 1410 t = _time.time()
(...skipping 730 matching lines...)
2143 _check_time_fields, _check_tzinfo_arg, _check_tzname, 2141 _check_time_fields, _check_tzinfo_arg, _check_tzname,
2144 _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month, 2142 _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month,
2145 _days_before_year, _days_in_month, _format_time, _is_leap, 2143 _days_before_year, _days_in_month, _format_time, _is_leap,
2146 _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class, 2144 _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class,
2147 _wrap_strftime, _ymd2ord) 2145 _wrap_strftime, _ymd2ord)
2148 # XXX Since import * above excludes names that start with _, 2146 # XXX Since import * above excludes names that start with _,
2149 # docstring does not get overwritten. In the future, it may be 2147 # docstring does not get overwritten. In the future, it may be
2150 # appropriate to maintain a single module level docstring and 2148 # appropriate to maintain a single module level docstring and
2151 # remove the following line. 2149 # remove the following line.
2152 from _datetime import __doc__ 2150 from _datetime import __doc__
LEFTRIGHT

This is Rietveld 894c83f36cb7+