[Python-checkins] r82134 - in sandbox/branches/py3k-datetime: datetime.py test_datetime.py
alexander.belopolsky
python-checkins at python.org
Mon Jun 21 17:46:24 CEST 2010
Author: alexander.belopolsky
Date: Mon Jun 21 17:46:24 2010
New Revision: 82134
Log:
removed tmxxx, resynced tests with py3k
Modified:
sandbox/branches/py3k-datetime/datetime.py
sandbox/branches/py3k-datetime/test_datetime.py
Modified: sandbox/branches/py3k-datetime/datetime.py
==============================================================================
--- sandbox/branches/py3k-datetime/datetime.py (original)
+++ sandbox/branches/py3k-datetime/datetime.py Mon Jun 21 17:46:24 2010
@@ -24,6 +24,7 @@
MINYEAR = 1
MAXYEAR = 9999
+_MAXORDINAL = 3652059 # date.max.toordinal()
# Utility functions, adapted from Python's Demo/classes/Dates.py, which
# also assumes the current Gregorian calendar indefinitely extended in
@@ -325,104 +326,6 @@
raise TypeError("can't compare '%s' to '%s'" % (
type(x).__name__, type(y).__name__))
-# This is a start at a struct tm workalike. Goals:
-#
-# + Works the same way across platforms.
-# + Handles all the fields datetime needs handled, without 1970-2038 glitches.
-#
-# Note: I suspect it's best if this flavor of tm does *not* try to
-# second-guess timezones or DST. Instead fold whatever adjustments you want
-# into the minutes argument (and the constructor will normalize).
-
-_ORD1970 = _ymd2ord(1970, 1, 1) # base ordinal for UNIX epoch
-
-class tmxxx:
-
- ordinal = None
-
- def __init__(self, year, month, day, hour=0, minute=0, second=0,
- microsecond=0):
- # Normalize all the inputs, and store the normalized values.
- if not 0 <= microsecond <= 999999:
- carry, microsecond = divmod(microsecond, 1000000)
- second += carry
- if not 0 <= second <= 59:
- carry, second = divmod(second, 60)
- minute += carry
- if not 0 <= minute <= 59:
- carry, minute = divmod(minute, 60)
- hour += carry
- if not 0 <= hour <= 23:
- carry, hour = divmod(hour, 24)
- day += carry
-
- # That was easy. Now it gets muddy: the proper range for day
- # can't be determined without knowing the correct month and year,
- # but if day is, e.g., plus or minus a million, the current month
- # and year values make no sense (and may also be out of bounds
- # themselves).
- # Saying 12 months == 1 year should be non-controversial.
- if not 1 <= month <= 12:
- carry, month = divmod(month-1, 12)
- year += carry
- month += 1
- assert 1 <= month <= 12
-
- # Now only day can be out of bounds (year may also be out of bounds
- # for a datetime object, but we don't care about that here).
- # If day is out of bounds, what to do is arguable, but at least the
- # method here is principled and explainable.
- dim = _days_in_month(year, month)
- if not 1 <= day <= dim:
- # Move day-1 days from the first of the month. First try to
- # get off cheap if we're only one day out of range (adjustments
- # for timezone alone can't be worse than that).
- if day == 0: # move back a day
- month -= 1
- if month > 0:
- day = _days_in_month(year, month)
- else:
- year, month, day = year-1, 12, 31
- elif day == dim + 1: # move forward a day
- month += 1
- day = 1
- if month > 12:
- month = 1
- year += 1
- else:
- self.ordinal = _ymd2ord(year, month, 1) + (day - 1)
- year, month, day = _ord2ymd(self.ordinal)
-
- self.year, self.month, self.day = year, month, day
- self.hour, self.minute, self.second = hour, minute, second
- self.microsecond = microsecond
-
- def toordinal(self):
- """Return proleptic Gregorian ordinal for the year, month and day.
-
- January 1 of year 1 is day 1. Only the year, month and day values
- contribute to the result.
- """
- if self.ordinal is None:
- self.ordinal = _ymd2ord(self.year, self.month, self.day)
- return self.ordinal
-
- def time(self):
- "Return Unixish timestamp, as a float (assuming UTC)."
- days = self.toordinal() - _ORD1970 # convert to UNIX epoch
- seconds = ((days * 24. + self.hour)*60. + self.minute)*60.
- return seconds + self.second + self.microsecond / 1e6
-
- def ctime(self):
- "Return ctime() style string."
- weekday = self.toordinal() % 7 or 7
- return "%s %s %2d %02d:%02d:%02d %04d" % (
- _DAYNAMES[weekday],
- _MONTHNAMES[self.month],
- self.day,
- self.hour, self.minute, self.second,
- self.year)
-
class timedelta(object):
"""Represent the difference between two datetime objects.
@@ -826,9 +729,14 @@
# easily done without using strftime() -- that's better too because
# strftime("%c", ...) is locale specific.
+
def ctime(self):
- "Format a la ctime()."
- return tmxxx(self.__year, self.__month, self.__day).ctime()
+ "Return ctime() style string."
+ weekday = self.toordinal() % 7 or 7
+ return "%s %s %2d 00:00:00 %04d" % (
+ _DAYNAMES[weekday],
+ _MONTHNAMES[self.__month],
+ self.__day, self.__year)
def strftime(self, fmt):
"Format using strftime()."
@@ -945,12 +853,10 @@
def __add__(self, other):
"Add a date to a timedelta."
if isinstance(other, timedelta):
- t = tmxxx(self.__year,
- self.__month,
- self.__day + other.days)
- self._checkOverflow(t.year)
- result = date(t.year, t.month, t.day)
- return result
+ o = self.toordinal() + other.days
+ if 0 < o <= _MAXORDINAL:
+ return date.fromordinal(o)
+ raise OverflowError("result out of range")
raise TypeError
# XXX Should be 'return NotImplemented', but there's a bug in 2.2...
@@ -1508,13 +1414,11 @@
def utctimetuple(self):
"Return UTC time tuple compatible with time.gmtime()."
+ offset = self.utcoffset()
+ if offset:
+ self -= offset
y, m, d = self.year, self.month, self.day
hh, mm, ss = self.hour, self.minute, self.second
- offset = self._utcoffset()
- if offset: # neither None nor 0
- tm = tmxxx(y, m, d, hh, mm - offset)
- y, m, d = tm.year, tm.month, tm.day
- hh, mm = tm.hour, tm.minute
return _build_struct_time(y, m, d, hh, mm, ss, 0)
def date(self):
@@ -1578,10 +1482,14 @@
# Ways to produce a string.
def ctime(self):
- "Format a la ctime()."
- t = tmxxx(self.__year, self.__month, self.__day, self.__hour,
- self.__minute, self.__second)
- return t.ctime()
+ "Return ctime() style string."
+ weekday = self.toordinal() % 7 or 7
+ return "%s %s %2d %02d:%02d:%02d %04d" % (
+ _DAYNAMES[weekday],
+ _MONTHNAMES[self.__month],
+ self.__day,
+ self.__hour, self.__minute, self.__second,
+ self.__year)
def isoformat(self, sep='T'):
"""Return the time formatted according to ISO.
@@ -1767,18 +1675,20 @@
"Add a datetime and a timedelta."
if not isinstance(other, timedelta):
return NotImplemented
- t = tmxxx(self.__year,
- self.__month,
- self.__day + other.days,
- self.__hour,
- self.__minute,
- self.__second + other.seconds,
- self.__microsecond + other.microseconds)
- self._checkOverflow(t.year)
- result = datetime(t.year, t.month, t.day,
- t.hour, t.minute, t.second,
- t.microsecond, tzinfo=self._tzinfo)
- return result
+ delta = timedelta(self.toordinal(),
+ hours=self.__hour,
+ minutes=self.__minute,
+ seconds=self.__second,
+ microseconds=self.__microsecond)
+ delta += other
+ hour, rem = divmod(delta.seconds, 3600)
+ minute, second = divmod(rem, 60)
+ if 0 < delta.days <= _MAXORDINAL:
+ return datetime.combine(date.fromordinal(delta.days),
+ time(hour, minute, second,
+ delta.microseconds,
+ tzinfo=self._tzinfo))
+ raise OverflowError("result out of range")
__radd__ = __add__
Modified: sandbox/branches/py3k-datetime/test_datetime.py
==============================================================================
--- sandbox/branches/py3k-datetime/test_datetime.py (original)
+++ sandbox/branches/py3k-datetime/test_datetime.py Mon Jun 21 17:46:24 2010
@@ -2997,8 +2997,6 @@
def utcoffset(self, dt):
return self.uofs
- # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
- # in effect for a UTC time.
for dstvalue in -33, 33, 0, None:
d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
t = d.utctimetuple()
@@ -3011,34 +3009,32 @@
self.assertEqual(d.weekday(), t.tm_wday)
self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
t.tm_yday)
+ # Ensure tm_isdst is 0 regardless of what dst() says: DST
+ # is never in effect for a UTC time.
self.assertEqual(0, t.tm_isdst)
- # At the edges, UTC adjustment can normalize into years out-of-range
- # for a datetime object. Ensure that a correct timetuple is
- # created anyway.
+ # Check that utctimetuple() is the same as
+ # astimezone(utc).timetuple()
+ d = cls(2010, 11, 13, 14, 15, 16, 171819)
+ for tz in [timezone.min, timezone.utc, timezone.max]:
+ dtz = d.replace(tzinfo=tz)
+ self.assertEqual(dtz.utctimetuple()[:-1],
+ dtz.astimezone(timezone.utc).timetuple()[:-1])
+ # At the edges, UTC adjustment can produce years out-of-range
+ # for a datetime object. Ensure that an OverflowError is
+ # raised.
tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
# That goes back 1 minute less than a full day.
- t = tiny.utctimetuple()
- self.assertEqual(t.tm_year, MINYEAR-1)
- self.assertEqual(t.tm_mon, 12)
- self.assertEqual(t.tm_mday, 31)
- self.assertEqual(t.tm_hour, 0)
- self.assertEqual(t.tm_min, 1)
- self.assertEqual(t.tm_sec, 37)
- self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
- self.assertEqual(t.tm_isdst, 0)
+ self.assertRaises(OverflowError, tiny.utctimetuple)
huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
# That goes forward 1 minute less than a full day.
- t = huge.utctimetuple()
- self.assertEqual(t.tm_year, MAXYEAR+1)
- self.assertEqual(t.tm_mon, 1)
- self.assertEqual(t.tm_mday, 1)
- self.assertEqual(t.tm_hour, 23)
- self.assertEqual(t.tm_min, 58)
- self.assertEqual(t.tm_sec, 37)
- self.assertEqual(t.tm_yday, 1)
- self.assertEqual(t.tm_isdst, 0)
+ self.assertRaises(OverflowError, huge.utctimetuple)
+ # More overflow cases
+ tiny = cls.min.replace(tzinfo=timezone(MINUTE))
+ self.assertRaises(OverflowError, tiny.utctimetuple)
+ huge = cls.max.replace(tzinfo=timezone(-MINUTE))
+ self.assertRaises(OverflowError, huge.utctimetuple)
def test_tzinfo_isoformat(self):
zero = FixedOffset(0, "+00:00")
More information about the Python-checkins
mailing list