[pypy-commit] pypy default: merge heads
arigo
noreply at buildbot.pypy.org
Tue Nov 24 16:50:06 EST 2015
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r80915:30a4accaa4d9
Date: 2015-11-24 22:49 +0100
http://bitbucket.org/pypy/pypy/changeset/30a4accaa4d9/
Log: merge heads
diff --git a/lib_pypy/datetime.py b/lib_pypy/datetime.py
--- a/lib_pypy/datetime.py
+++ b/lib_pypy/datetime.py
@@ -17,11 +17,12 @@
"""
from __future__ import division
-import numbers as _numbers
import time as _time
import math as _math
import struct as _struct
+_SENTINEL = object()
+
def _cmp(x, y):
return 0 if x == y else 1 if x > y else -1
@@ -32,6 +33,8 @@
MAXYEAR = 9999
_MINYEARFMT = 1900
+_MAX_DELTA_DAYS = 999999999
+
# Utility functions, adapted from Python's Demo/classes/Dates.py, which
# also assumes the current Gregorian calendar indefinitely extended in
# both directions. Difference: Dates.py calls January 1 of year 0 day
@@ -96,6 +99,15 @@
# pasting together 25 4-year cycles.
assert _DI100Y == 25 * _DI4Y - 1
+_US_PER_US = 1
+_US_PER_MS = 1000
+_US_PER_SECOND = 1000000
+_US_PER_MINUTE = 60000000
+_SECONDS_PER_DAY = 24 * 3600
+_US_PER_HOUR = 3600000000
+_US_PER_DAY = 86400000000
+_US_PER_WEEK = 604800000000
+
def _ord2ymd(n):
"ordinal -> (year, month, day), considering 01-Jan-0001 as day 1."
@@ -417,6 +429,24 @@
self.hour, self.minute, self.second = hour, minute, second
self.microsecond = microsecond
+def _accum(tag, sofar, num, factor, leftover):
+ if isinstance(num, (int, long)):
+ prod = num * factor
+ rsum = sofar + prod
+ return rsum, leftover
+ if isinstance(num, float):
+ fracpart, intpart = _math.modf(num)
+ prod = int(intpart) * factor
+ rsum = sofar + prod
+ if fracpart == 0.0:
+ return rsum, leftover
+ assert isinstance(factor, (int, long))
+ fracpart, intpart = _math.modf(factor * fracpart)
+ rsum += int(intpart)
+ return rsum, leftover + fracpart
+ raise TypeError("unsupported type for timedelta %s component: %s" %
+ (tag, type(num)))
+
class timedelta(object):
"""Represent the difference between two datetime objects.
@@ -436,100 +466,35 @@
"""
__slots__ = '_days', '_seconds', '_microseconds', '_hashcode'
- def __new__(cls, days=0, seconds=0, microseconds=0,
- milliseconds=0, minutes=0, hours=0, weeks=0):
- # Doing this efficiently and accurately in C is going to be difficult
- # and error-prone, due to ubiquitous overflow possibilities, and that
- # C double doesn't have enough bits of precision to represent
- # microseconds over 10K years faithfully. The code here tries to make
- # explicit where go-fast assumptions can be relied on, in order to
- # guide the C implementation; it's way more convoluted than speed-
- # ignoring auto-overflow-to-long idiomatic Python could be.
+ def __new__(cls, days=_SENTINEL, seconds=_SENTINEL, microseconds=_SENTINEL,
+ milliseconds=_SENTINEL, minutes=_SENTINEL, hours=_SENTINEL, weeks=_SENTINEL):
+ x = 0
+ leftover = 0.0
+ if microseconds is not _SENTINEL:
+ x, leftover = _accum("microseconds", x, microseconds, _US_PER_US, leftover)
+ if milliseconds is not _SENTINEL:
+ x, leftover = _accum("milliseconds", x, milliseconds, _US_PER_MS, leftover)
+ if seconds is not _SENTINEL:
+ x, leftover = _accum("seconds", x, seconds, _US_PER_SECOND, leftover)
+ if minutes is not _SENTINEL:
+ x, leftover = _accum("minutes", x, minutes, _US_PER_MINUTE, leftover)
+ if hours is not _SENTINEL:
+ x, leftover = _accum("hours", x, hours, _US_PER_HOUR, leftover)
+ if days is not _SENTINEL:
+ x, leftover = _accum("days", x, days, _US_PER_DAY, leftover)
+ if weeks is not _SENTINEL:
+ x, leftover = _accum("weeks", x, weeks, _US_PER_WEEK, leftover)
+ if leftover != 0.0:
+ x += _round(leftover)
+ return cls._from_microseconds(x)
- # XXX Check that all inputs are ints, longs or floats.
+ @classmethod
+ def _from_microseconds(cls, us):
+ s, us = divmod(us, _US_PER_SECOND)
+ d, s = divmod(s, _SECONDS_PER_DAY)
- # Final values, all integer.
- # s and us fit in 32-bit signed ints; d isn't bounded.
- d = s = us = 0
-
- # Normalize everything to days, seconds, microseconds.
- days += weeks*7
- seconds += minutes*60 + hours*3600
- microseconds += milliseconds*1000
-
- # Get rid of all fractions, and normalize s and us.
- # Take a deep breath <wink>.
- if isinstance(days, float):
- dayfrac, days = _math.modf(days)
- daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))
- assert daysecondswhole == int(daysecondswhole) # can't overflow
- s = int(daysecondswhole)
- assert days == int(days)
- d = int(days)
- else:
- daysecondsfrac = 0.0
- d = days
- assert isinstance(daysecondsfrac, float)
- assert abs(daysecondsfrac) <= 1.0
- assert isinstance(d, _numbers.Integral)
- assert abs(s) <= 24 * 3600
- # days isn't referenced again before redefinition
-
- if isinstance(seconds, float):
- secondsfrac, seconds = _math.modf(seconds)
- assert seconds == int(seconds)
- seconds = int(seconds)
- secondsfrac += daysecondsfrac
- assert abs(secondsfrac) <= 2.0
- else:
- secondsfrac = daysecondsfrac
- # daysecondsfrac isn't referenced again
- assert isinstance(secondsfrac, float)
- assert abs(secondsfrac) <= 2.0
-
- assert isinstance(seconds, _numbers.Integral)
- days, seconds = divmod(seconds, 24*3600)
- d += days
- s += int(seconds) # can't overflow
- assert isinstance(s, _numbers.Integral)
- assert abs(s) <= 2 * 24 * 3600
- # seconds isn't referenced again before redefinition
-
- usdouble = secondsfrac * 1e6
- assert abs(usdouble) < 2.1e6 # exact value not critical
- # secondsfrac isn't referenced again
-
- if isinstance(microseconds, float):
- microseconds = _round(microseconds + usdouble)
- seconds, microseconds = divmod(microseconds, 1000000)
- days, seconds = divmod(seconds, 24*3600)
- d += days
- s += int(seconds)
- microseconds = int(microseconds)
- else:
- microseconds = int(microseconds)
- seconds, microseconds = divmod(microseconds, 1000000)
- days, seconds = divmod(seconds, 24*3600)
- d += days
- s += int(seconds)
- microseconds = _round(microseconds + usdouble)
- assert isinstance(s, _numbers.Integral)
- assert isinstance(microseconds, _numbers.Integral)
- assert abs(s) <= 3 * 24 * 3600
- assert abs(microseconds) < 3.1e6
-
- # Just a little bit of carrying possible for microseconds and seconds.
- seconds, us = divmod(microseconds, 1000000)
- s += seconds
- days, s = divmod(s, 24*3600)
- d += days
-
- assert isinstance(d, _numbers.Integral)
- assert isinstance(s, _numbers.Integral) and 0 <= s < 24*3600
- assert isinstance(us, _numbers.Integral) and 0 <= us < 1000000
-
- if abs(d) > 999999999:
- raise OverflowError("timedelta # of days is too large: %d" % d)
+ if not -_MAX_DELTA_DAYS <= d <= _MAX_DELTA_DAYS:
+ raise OverflowError("days=%d; must have magnitude <= %d" % (d, _MAX_DELTA_DAYS))
self = object.__new__(cls)
self._days = d
@@ -538,6 +503,10 @@
self._hashcode = -1
return self
+ def _to_microseconds(self):
+ return ((self._days * _SECONDS_PER_DAY + self._seconds) * _US_PER_SECOND +
+ self._microseconds)
+
def __repr__(self):
module = "datetime." if self.__class__ is timedelta else ""
if self._microseconds:
@@ -565,8 +534,7 @@
def total_seconds(self):
"""Total seconds in the duration."""
- return ((self.days * 86400 + self.seconds) * 10**6 +
- self.microseconds) / 10**6
+ return self._to_microseconds() / 10**6
# Read-only field accessors
@property
@@ -626,25 +594,18 @@
return self
def __mul__(self, other):
- if isinstance(other, (int, long)):
- # for CPython compatibility, we cannot use
- # our __class__ here, but need a real timedelta
- return timedelta(self._days * other,
- self._seconds * other,
- self._microseconds * other)
- return NotImplemented
+ if not isinstance(other, (int, long)):
+ return NotImplemented
+ usec = self._to_microseconds()
+ return timedelta._from_microseconds(usec * other)
__rmul__ = __mul__
- def _to_microseconds(self):
- return ((self._days * (24*3600) + self._seconds) * 1000000 +
- self._microseconds)
-
def __div__(self, other):
if not isinstance(other, (int, long)):
return NotImplemented
usec = self._to_microseconds()
- return timedelta(0, 0, usec // other)
+ return timedelta._from_microseconds(usec // other)
__floordiv__ = __div__
@@ -708,9 +669,8 @@
def __reduce__(self):
return (self.__class__, self._getstate())
-timedelta.min = timedelta(-999999999)
-timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59,
- microseconds=999999)
+timedelta.min = timedelta(-_MAX_DELTA_DAYS)
+timedelta.max = timedelta(_MAX_DELTA_DAYS, 24*3600-1, 1000000-1)
timedelta.resolution = timedelta(microseconds=1)
class date(object):
@@ -1508,18 +1468,24 @@
A timezone info object may be passed in as well.
"""
+ _check_tzinfo_arg(tz)
+ converter = _time.localtime if tz is None else _time.gmtime
+ self = cls._from_timestamp(converter, timestamp, tz)
+ if tz is not None:
+ self = tz.fromutc(self)
+ return self
- _check_tzinfo_arg(tz)
+ @classmethod
+ def utcfromtimestamp(cls, t):
+ "Construct a UTC datetime from a POSIX timestamp (like time.time())."
+ return cls._from_timestamp(_time.gmtime, t, None)
- converter = _time.localtime if tz is None else _time.gmtime
-
- if isinstance(timestamp, _numbers.Integral):
- us = 0
- else:
- t_full = timestamp
- timestamp = int(_math.floor(timestamp))
- frac = t_full - timestamp
- us = _round(frac * 1e6)
+ @classmethod
+ def _from_timestamp(cls, converter, timestamp, tzinfo):
+ t_full = timestamp
+ timestamp = int(_math.floor(timestamp))
+ frac = t_full - timestamp
+ us = _round(frac * 1e6)
# If timestamp is less than one microsecond smaller than a
# full second, us can be rounded up to 1000000. In this case,
@@ -1530,32 +1496,7 @@
us = 0
y, m, d, hh, mm, ss, weekday, jday, dst = converter(timestamp)
ss = min(ss, 59) # clamp out leap seconds if the platform has them
- result = cls(y, m, d, hh, mm, ss, us, 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())."
- if isinstance(t, _numbers.Integral):
- us = 0
- else:
- t_full = t
- t = int(_math.floor(t))
- frac = t_full - t
- us = _round(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(y, m, d, hh, mm, ss, us, tzinfo)
@classmethod
def now(cls, tz=None):
diff --git a/pypy/module/test_lib_pypy/test_datetime.py b/pypy/module/test_lib_pypy/test_datetime.py
--- a/pypy/module/test_lib_pypy/test_datetime.py
+++ b/pypy/module/test_lib_pypy/test_datetime.py
@@ -309,6 +309,10 @@
assert td_div_int_newint == td_div_newint_newint
assert td_div_newint_int == td_div_newint_newint
+ def test_return_types(self):
+ td = datetime.timedelta(5)
+ assert type(td.total_seconds()) is float
+
class TestDatetimeHost(BaseTestDatetime):
def setup_class(cls):
More information about the pypy-commit
mailing list