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

Delta Between Two Patch Sets: Lib/datetime.py

Issue 23517: datetime.utcfromtimestamp rounds results incorrectly
Left Patch Set: Created 4 years, 5 months ago
Right Patch Set: Created 4 years, 5 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | Lib/test/datetimetester.py » ('j') | Lib/test/datetimetester.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 """Concrete date/time and related types. 1 """Concrete date/time and related types.
2 2
3 See http://www.iana.org/time-zones/repository/tz-link.html for 3 See http://www.iana.org/time-zones/repository/tz-link.html for
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...) Expand 10 before | Expand all | Expand 10 after
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
315 - add, subtract timedelta 307 - add, subtract timedelta
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...) Expand 10 before | Expand all | Expand 10 after
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 918 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
1360 def _fromtimestamp(cls, t, utc, tz): 1365 def _fromtimestamp(cls, t, utc, tz):
1361 """Construct a datetime from a POSIX timestamp (like time.time()). 1366 """Construct a datetime from a POSIX timestamp (like time.time()).
1362 1367
1363 A timezone info object may be passed in as well. 1368 A timezone info object may be passed in as well.
1364 """ 1369 """
1365 frac, t = _math.modf(t) 1370 frac, t = _math.modf(t)
sasha 2015/09/10 21:14:58 You may find it easier to use t, frac = divmod(t,
storchaka 2015/09/10 22:30:27 See also issue23607. divmod(t, 1) allowed to suppo
1366 us = _round_half_up(frac * 1e6) 1371 us = round(frac * 1e6)
1367 if us >= 1000000: 1372 if us >= 1000000:
1368 t += 1 1373 t += 1
1369 us -= 1000000 1374 us -= 1000000
1370 elif us < 0: 1375 elif us < 0:
1371 t -= 1 1376 t -= 1
1372 us += 1000000 1377 us += 1000000
1373 1378
1374 converter = _time.gmtime if utc else _time.localtime 1379 converter = _time.gmtime if utc else _time.localtime
1375 y, m, d, hh, mm, ss, weekday, jday, dst = converter(t) 1380 y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
1376 ss = min(ss, 59) # clamp out leap seconds if the platform has them 1381 ss = min(ss, 59) # clamp out leap seconds if the platform has them
(...skipping 759 matching lines...) Expand 10 before | Expand all | Expand 10 after
2136 _check_time_fields, _check_tzinfo_arg, _check_tzname, 2141 _check_time_fields, _check_tzinfo_arg, _check_tzname,
2137 _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month, 2142 _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month,
2138 _days_before_year, _days_in_month, _format_time, _is_leap, 2143 _days_before_year, _days_in_month, _format_time, _is_leap,
2139 _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class, 2144 _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class,
2140 _wrap_strftime, _ymd2ord) 2145 _wrap_strftime, _ymd2ord)
2141 # XXX Since import * above excludes names that start with _, 2146 # XXX Since import * above excludes names that start with _,
2142 # docstring does not get overwritten. In the future, it may be 2147 # docstring does not get overwritten. In the future, it may be
2143 # appropriate to maintain a single module level docstring and 2148 # appropriate to maintain a single module level docstring and
2144 # remove the following line. 2149 # remove the following line.
2145 from _datetime import __doc__ 2150 from _datetime import __doc__
LEFTRIGHT

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+