]> granicus.if.org Git - python/commitdiff
Fix bug #1520914. Starting in 2.4, time.strftime() began to check the bounds
authorBrett Cannon <bcannon@gmail.com>
Tue, 18 Jul 2006 04:41:36 +0000 (04:41 +0000)
committerBrett Cannon <bcannon@gmail.com>
Tue, 18 Jul 2006 04:41:36 +0000 (04:41 +0000)
of values in the time tuple passed in.  Unfortunately people came to rely on
undocumented behaviour of setting unneeded values to 0, regardless of if it was
within the valid range.  Now those values force the value internally to the
minimum value when 0 is passed in.

Doc/lib/libtime.tex
Lib/test/test_time.py
Misc/NEWS
Modules/timemodule.c

index b39b65063c61941f1b1d84ea1cf12cee8179e472..0e83400a2b6b31999de412eddbed62b214be7670 100644 (file)
@@ -226,6 +226,8 @@ if any field in \var{t} is outside of the allowed range.
 \versionchanged[Allowed \var{t} to be omitted]{2.1}
 \versionchanged[\exception{ValueError} raised if a field in \var{t} is
 out of range]{2.4}
+\versionchanged[0 is now a legal argument for any position in the time tuple;
+if it is normally illegal the value is forced to a correct one.]{2.5}
 
 
 The following directives can be embedded in the \var{format} string.
index 94bbcca30920a57d1da3825481a90027c0ffb391..f4be7590f1c18929ebe66cea8edcfa8fd9c43620 100644 (file)
@@ -39,9 +39,9 @@ class TimeTestCase(unittest.TestCase):
 
     def test_strftime_bounds_checking(self):
         # Make sure that strftime() checks the bounds of the various parts
-        #of the time tuple.
+        #of the time tuple (0 is valid for *all* values).
 
-        # Check year
+        # Check year [1900, max(int)]
         self.assertRaises(ValueError, time.strftime, '',
                             (1899, 1, 1, 0, 0, 0, 0, 1, -1))
         if time.accept2dyear:
@@ -49,27 +49,27 @@ class TimeTestCase(unittest.TestCase):
                                 (-1, 1, 1, 0, 0, 0, 0, 1, -1))
             self.assertRaises(ValueError, time.strftime, '',
                                 (100, 1, 1, 0, 0, 0, 0, 1, -1))
-        # Check month
+        # Check month [1, 12] + zero support
         self.assertRaises(ValueError, time.strftime, '',
-                            (1900, 0, 1, 0, 0, 0, 0, 1, -1))
+                            (1900, -1, 1, 0, 0, 0, 0, 1, -1))
         self.assertRaises(ValueError, time.strftime, '',
                             (1900, 13, 1, 0, 0, 0, 0, 1, -1))
-        # Check day of month
+        # Check day of month [1, 31] + zero support
         self.assertRaises(ValueError, time.strftime, '',
-                            (1900, 1, 0, 0, 0, 0, 0, 1, -1))
+                            (1900, 1, -1, 0, 0, 0, 0, 1, -1))
         self.assertRaises(ValueError, time.strftime, '',
                             (1900, 1, 32, 0, 0, 0, 0, 1, -1))
-        # Check hour
+        # Check hour [0, 23]
         self.assertRaises(ValueError, time.strftime, '',
                             (1900, 1, 1, -1, 0, 0, 0, 1, -1))
         self.assertRaises(ValueError, time.strftime, '',
                             (1900, 1, 1, 24, 0, 0, 0, 1, -1))
-        # Check minute
+        # Check minute [0, 59]
         self.assertRaises(ValueError, time.strftime, '',
                             (1900, 1, 1, 0, -1, 0, 0, 1, -1))
         self.assertRaises(ValueError, time.strftime, '',
                             (1900, 1, 1, 0, 60, 0, 0, 1, -1))
-        # Check second
+        # Check second [0, 61]
         self.assertRaises(ValueError, time.strftime, '',
                             (1900, 1, 1, 0, 0, -1, 0, 1, -1))
         # C99 only requires allowing for one leap second, but Python's docs say
@@ -82,17 +82,25 @@ class TimeTestCase(unittest.TestCase):
         #  modulo.
         self.assertRaises(ValueError, time.strftime, '',
                             (1900, 1, 1, 0, 0, 0, -2, 1, -1))
-        # Check day of the year
+        # Check day of the year [1, 366] + zero support
         self.assertRaises(ValueError, time.strftime, '',
-                            (1900, 1, 1, 0, 0, 0, 0, 0, -1))
+                            (1900, 1, 1, 0, 0, 0, 0, -1, -1))
         self.assertRaises(ValueError, time.strftime, '',
                             (1900, 1, 1, 0, 0, 0, 0, 367, -1))
-        # Check daylight savings flag
+        # Check daylight savings flag [-1, 1]
         self.assertRaises(ValueError, time.strftime, '',
                             (1900, 1, 1, 0, 0, 0, 0, 1, -2))
         self.assertRaises(ValueError, time.strftime, '',
                             (1900, 1, 1, 0, 0, 0, 0, 1, 2))
 
+    def test_default_values_for_zero(self):
+        # Make sure that using all zeros uses the proper default values.
+        # No test for daylight savings since strftime() does not change output
+        # based on its value.
+        expected = "2000 01 01 00 00 00 1 001"
+        result = time.strftime("%Y %m %d %H %M %S %w %j", (0,)*9)
+        self.assertEquals(expected, result)
+
     def test_strptime(self):
         tt = time.gmtime(self.t)
         for directive in ('a', 'A', 'b', 'B', 'c', 'd', 'H', 'I',
index 4c761e0ee3016c0340afb80a56b2fa7fd9242c4f..53f3dc8349861cc1cedf7e8baee7896843f05be7 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -27,6 +27,12 @@ Core and builtins
 Library
 -------
 
+- Bug #1520914: Change time.strftime() to accept a zero for any position in its
+  argument tuple.  For arguments where zero is illegal, the value is forced to
+  the minimum value that is correct.  This is to support an undocumented but
+  common way people used  to fill in inconsequential information in the time
+  tuple pre-2.4.
+
 - Patch #1220874: Update the binhex module for Mach-O.
 
 Extension Modules
index 402e349b42045baaffd903dd0c7cf7f055a2e1e8..eb279fc9cdbb8c40320a9dd1eea3d2318991100b 100644 (file)
@@ -406,13 +406,35 @@ time_strftime(PyObject *self, PyObject *args)
             indexing blindly into some array for a textual representation
             by some bad index (fixes bug #897625).
 
-            No check for year since handled in gettmarg().
+           Also support values of zero from Python code for arguments in which
+           that is out of range by forcing that value to the lowest value that
+           is valid (fixed bug #XXX).
+
+           Valid ranges based on what is allowed in struct tm:
+
+           - tm_year: [0, max(int)] (1)
+           - tm_mon: [0, 11] (2)
+           - tm_mday: [1, 31]
+           - tm_hour: [0, 23]
+           - tm_min: [0, 59]
+           - tm_sec: [0, 60]
+           - tm_wday: [0, 6] (1)
+           - tm_yday: [0, 365] (2)
+           - tm_isdst: [-max(int), max(int)]
+
+           (1) gettmarg() handles bounds-checking.
+           (2) Python's acceptable range is one greater than the range in C,
+               thus need to check against automatic decrement by gettmarg().
         */
-        if (buf.tm_mon < 0 || buf.tm_mon > 11) {
+       if (buf.tm_mon == -1)
+           buf.tm_mon = 0;
+       else if (buf.tm_mon < 0 || buf.tm_mon > 11) {
             PyErr_SetString(PyExc_ValueError, "month out of range");
                         return NULL;
         }
-        if (buf.tm_mday < 1 || buf.tm_mday > 31) {
+       if (buf.tm_mday == 0)
+           buf.tm_mday = 1;
+       else if (buf.tm_mday < 0 || buf.tm_mday > 31) {
             PyErr_SetString(PyExc_ValueError, "day of month out of range");
                         return NULL;
         }
@@ -434,7 +456,9 @@ time_strftime(PyObject *self, PyObject *args)
             PyErr_SetString(PyExc_ValueError, "day of week out of range");
             return NULL;
         }
-        if (buf.tm_yday < 0 || buf.tm_yday > 365) {
+       if (buf.tm_yday == -1)
+           buf.tm_yday = 0;
+       else if (buf.tm_yday < 0 || buf.tm_yday > 365) {
             PyErr_SetString(PyExc_ValueError, "day of year out of range");
             return NULL;
         }