[Python-checkins] r82128 - in python/branches/py3k: Doc/library/datetime.rst Lib/test/test_datetime.py Misc/NEWS Modules/datetimemodule.c

alexander.belopolsky python-checkins at python.org
Mon Jun 21 17:21:14 CEST 2010


Author: alexander.belopolsky
Date: Mon Jun 21 17:21:14 2010
New Revision: 82128

Log:
Issue #9005: Prevent utctimetuple() from producing year 0 or year 10,000.


Modified:
   python/branches/py3k/Doc/library/datetime.rst
   python/branches/py3k/Lib/test/test_datetime.py
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/Modules/datetimemodule.c

Modified: python/branches/py3k/Doc/library/datetime.rst
==============================================================================
--- python/branches/py3k/Doc/library/datetime.rst	(original)
+++ python/branches/py3k/Doc/library/datetime.rst	Mon Jun 21 17:21:14 2010
@@ -974,10 +974,10 @@
    ``d.dst()`` returns.  DST is never in effect for a UTC time.
 
    If *d* is aware, *d* is normalized to UTC time, by subtracting
-   ``d.utcoffset()``, and a :class:`time.struct_time` for the normalized time is
-   returned.  :attr:`tm_isdst` is forced to 0. Note that the result's
-   :attr:`tm_year` member may be :const:`MINYEAR`\ -1 or :const:`MAXYEAR`\ +1, if
-   *d*.year was ``MINYEAR`` or ``MAXYEAR`` and UTC adjustment spills over a year
+   ``d.utcoffset()``, and a :class:`time.struct_time` for the
+   normalized time is returned.  :attr:`tm_isdst` is forced to 0. Note
+   that an :exc:`OverflowError` may be raised if *d*.year was
+   ``MINYEAR`` or ``MAXYEAR`` and UTC adjustment spills over a year
    boundary.
 
 

Modified: python/branches/py3k/Lib/test/test_datetime.py
==============================================================================
--- python/branches/py3k/Lib/test/test_datetime.py	(original)
+++ python/branches/py3k/Lib/test/test_datetime.py	Mon Jun 21 17:21:14 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")

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Mon Jun 21 17:21:14 2010
@@ -1330,6 +1330,11 @@
 Extension Modules
 -----------------
 
+- Issue #9005: Prevent utctimetuple() from producing year 0 or year
+  10,000.  Prior to this change, timezone adjustment in utctimetuple()
+  could produce tm_year value of 0 or 10,000.  Now an OverflowError is
+  raised in these edge cases.
+
 - Issue #6641: The ``datetime.strptime`` method now supports the
   ``%z`` directive.  When the ``%z`` directive is present in the
   format string, an aware ``datetime`` object is returned with

Modified: python/branches/py3k/Modules/datetimemodule.c
==============================================================================
--- python/branches/py3k/Modules/datetimemodule.c	(original)
+++ python/branches/py3k/Modules/datetimemodule.c	Mon Jun 21 17:21:14 2010
@@ -4932,15 +4932,9 @@
 
         mm -= offset;
         stat = normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us);
-        if (stat < 0) {
-            /* At the edges, it's possible we overflowed
-             * beyond MINYEAR or MAXYEAR.
-             */
-            if (PyErr_ExceptionMatches(PyExc_OverflowError))
-                PyErr_Clear();
-            else
-                return NULL;
-        }
+        /* OverflowError may be raised in the edge cases. */
+        if (stat < 0)
+            return NULL;
     }
     return build_struct_time(y, m, d, hh, mm, ss, 0);
 }


More information about the Python-checkins mailing list