]> granicus.if.org Git - python/commitdiff
Issue #8013: time.asctime and time.ctime no longer call system asctime
authorAlexander Belopolsky <alexander.belopolsky@gmail.com>
Tue, 4 Jan 2011 16:34:30 +0000 (16:34 +0000)
committerAlexander Belopolsky <alexander.belopolsky@gmail.com>
Tue, 4 Jan 2011 16:34:30 +0000 (16:34 +0000)
and ctime functions.  The year range for time.asctime is now 1900
through maxint.  The range for time.ctime is the same as for
time.localtime.  The string produced by these functions is longer than
24 characters when year is greater than 9999.

Doc/library/time.rst
Lib/test/test_time.py
Misc/NEWS
Modules/timemodule.c

index bd8df0ed7857d79fd5339e51fefba26fd359aa5a..b0d9e07ba89d06f0223d2cba94469ded753b9bdb 100644 (file)
@@ -125,7 +125,7 @@ The module defines the following functions and data items:
 .. function:: asctime([t])
 
    Convert a tuple or :class:`struct_time` representing a time as returned by
-   :func:`gmtime` or :func:`localtime` to a 24-character string of the following
+   :func:`gmtime` or :func:`localtime` to a string of the following
    form: ``'Sun Jun 20 23:21:05 1993'``.  If *t* is not provided, the current time
    as returned by :func:`localtime` is used. Locale information is not used by
    :func:`asctime`.
index 6451b27a434927f4e813965d9d7e6b4a650e26b2..0674ede64ca1ce59005e4693cba9a3f669ecff8c 100644 (file)
@@ -2,6 +2,7 @@ from test import support
 import time
 import unittest
 import locale
+import sysconfig
 
 class TimeTestCase(unittest.TestCase):
 
@@ -121,34 +122,32 @@ class TimeTestCase(unittest.TestCase):
 
     def test_asctime(self):
         time.asctime(time.gmtime(self.t))
+
+        # Max year is only limited by the size of C int.
+        sizeof_int = sysconfig.get_config_vars('SIZEOF_INT')[0]
+        bigyear = (1 << 8 * sizeof_int - 1) - 1
+        asc = time.asctime((bigyear, 6, 1) + (0,)*6)
+        self.assertEqual(asc[-len(str(bigyear)):], str(bigyear))
+        self.assertRaises(OverflowError, time.asctime, (bigyear + 1,) + (0,)*8)
         self.assertRaises(TypeError, time.asctime, 0)
         self.assertRaises(TypeError, time.asctime, ())
-        # XXX: POSIX-compliant asctime should refuse to convert year > 9999,
-        # but glibc implementation does not.  For now, just check it doesn't
-        # segfault as it did before, and the result contains no newline.
-        try:
-            result = time.asctime((12345, 1, 0, 0, 0, 0, 0, 0, 0))
-        except ValueError:
-            # for POSIX-compliant runtimes
-            pass
-        else:
-            self.assertNotIn('\n', result)
 
     def test_asctime_bounding_check(self):
         self._bounds_checking(time.asctime)
 
     def test_ctime(self):
-        # XXX: POSIX-compliant ctime should refuse to convert year > 9999,
-        # but glibc implementation does not.  For now, just check it doesn't
-        # segfault as it did before, and the result contains no newline.
+        t = time.mktime((1973, 9, 16, 1, 3, 52, 0, 0, -1))
+        self.assertEqual(time.ctime(t), 'Sun Sep 16 01:03:52 1973')
+        t = time.mktime((2000, 1, 1, 0, 0, 0, 0, 0, -1))
+        self.assertEqual(time.ctime(t), 'Sat Jan  1 00:00:00 2000')
         try:
-            result = time.ctime(1e12)
+            bigval = time.mktime((10000, 1, 10) + (0,)*6)
         except ValueError:
-            # for POSIX-compliant runtimes (or 32-bit systems, where time_t
-            # cannot hold timestamps with a five-digit year)
+            # If mktime fails, ctime will fail too.  This may happen
+            # on some platforms.
             pass
         else:
-            self.assertNotIn('\n', result)
+            self.assertEquals(time.ctime(bigval)[-5:], '10000')
 
     @unittest.skipIf(not hasattr(time, "tzset"),
         "time module has no attribute tzset")
index 2c5df9e0ab73aa861f5ec1c756ddbd3cc8d33937..5dd389ce855380540c5df7cd36ebf6bb0a15ce45 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -1422,6 +1422,12 @@ Core and Builtins
 Extension Modules
 -----------------
 
+- Issue #8013: time.asctime and time.ctime no longer call system
+  asctime and ctime functions.  The year range for time.asctime is now
+  1900 through maxint.  The range for time.ctime is the same as for
+  time.localtime.  The string produced by these functions is longer
+  than 24 characters when year is greater than 9999.
+
 - Issue #6608: time.asctime is now checking struct tm fields its input
   before passing it to the system asctime.  Patch by MunSic Jeong.
 
index 4a0bacb2bbec4ddc14629661ae29ede443ae34b7..29a2f2dc998c29d7e6e20a09db9c0135a6778a27 100644 (file)
@@ -599,19 +599,51 @@ time_strptime(PyObject *self, PyObject *args)
     return strptime_result;
 }
 
+
 PyDoc_STRVAR(strptime_doc,
 "strptime(string, format) -> struct_time\n\
 \n\
 Parse a string to a time tuple according to a format specification.\n\
 See the library reference manual for formatting codes (same as strftime()).");
 
+static PyObject *
+_asctime(struct tm *timeptr)
+{
+    /* Inspired by Open Group reference implementation available at
+     * http://pubs.opengroup.org/onlinepubs/009695399/functions/asctime.html */
+    static char wday_name[7][3] = {
+        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+    };
+    static char mon_name[12][3] = {
+        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+    };
+    char buf[20]; /* 'Sun Sep 16 01:03:52\0' */
+    int n;
+
+    n = snprintf(buf, sizeof(buf), "%.3s %.3s%3d %.2d:%.2d:%.2d",
+                 wday_name[timeptr->tm_wday],
+                 mon_name[timeptr->tm_mon],
+                 timeptr->tm_mday, timeptr->tm_hour,
+                 timeptr->tm_min, timeptr->tm_sec);
+    /* XXX: since the fields used by snprintf above are validated in checktm,
+     * the following condition should never trigger. We keep the check because
+     * historically fixed size buffer used in asctime was the source of
+     * crashes. */
+    if (n + 1 != sizeof(buf)) {
+        PyErr_SetString(PyExc_ValueError, "unconvertible time");
+        return NULL;
+    }
+
+    return PyUnicode_FromFormat("%s %d", buf, 1900 + timeptr->tm_year);
+}
 
 static PyObject *
 time_asctime(PyObject *self, PyObject *args)
 {
     PyObject *tup = NULL;
     struct tm buf;
-    char *p, *q;
+
     if (!PyArg_UnpackTuple(args, "asctime", 0, 1, &tup))
         return NULL;
     if (tup == NULL) {
@@ -619,17 +651,7 @@ time_asctime(PyObject *self, PyObject *args)
         buf = *localtime(&tt);
     } else if (!gettmarg(tup, &buf) || !checktm(&buf))
         return NULL;
-    p = asctime(&buf);
-    if (p == NULL) {
-        PyErr_SetString(PyExc_ValueError, "unconvertible time");
-        return NULL;
-    }
-    /* Replace a terminating newline by a null byte, normally at position 24.
-     * It can occur later if the year has more than four digits. */
-    for (q = p+24; *q != '\0'; q++)
-        if (*q == '\n')
-            *q = '\0';
-    return PyUnicode_FromString(p);
+    return _asctime(&buf);
 }
 
 PyDoc_STRVAR(asctime_doc,
@@ -644,7 +666,7 @@ time_ctime(PyObject *self, PyObject *args)
 {
     PyObject *ot = NULL;
     time_t tt;
-    char *p, *q;
+    struct tm *timeptr;
 
     if (!PyArg_UnpackTuple(args, "ctime", 0, 1, &ot))
         return NULL;
@@ -658,17 +680,12 @@ time_ctime(PyObject *self, PyObject *args)
         if (tt == (time_t)-1 && PyErr_Occurred())
             return NULL;
     }
-    p = ctime(&tt);
-    if (p == NULL) {
+    timeptr = localtime(&tt);
+    if (timeptr == NULL) {
         PyErr_SetString(PyExc_ValueError, "unconvertible time");
-        return NULL;
+        return NULL;        
     }
-    /* Replace a terminating newline by a null byte, normally at position 24.
-     * It can occur later if the year has more than four digits. */
-    for (q = p+24; *q != '\0'; q++)
-        if (*q == '\n')
-            *q = '\0';
-    return PyUnicode_FromString(p);
+    return _asctime(timeptr);
 }
 
 PyDoc_STRVAR(ctime_doc,