[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