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.
This commit is contained in:
parent
2427ab9d6f
commit
b9588b528a
|
@ -125,7 +125,7 @@ The module defines the following functions and data items:
|
||||||
.. function:: asctime([t])
|
.. function:: asctime([t])
|
||||||
|
|
||||||
Convert a tuple or :class:`struct_time` representing a time as returned by
|
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
|
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
|
as returned by :func:`localtime` is used. Locale information is not used by
|
||||||
:func:`asctime`.
|
:func:`asctime`.
|
||||||
|
|
|
@ -2,6 +2,7 @@ from test import support
|
||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
import locale
|
import locale
|
||||||
|
import sysconfig
|
||||||
|
|
||||||
class TimeTestCase(unittest.TestCase):
|
class TimeTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -121,34 +122,32 @@ class TimeTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def test_asctime(self):
|
def test_asctime(self):
|
||||||
time.asctime(time.gmtime(self.t))
|
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, 0)
|
||||||
self.assertRaises(TypeError, time.asctime, ())
|
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):
|
def test_asctime_bounding_check(self):
|
||||||
self._bounds_checking(time.asctime)
|
self._bounds_checking(time.asctime)
|
||||||
|
|
||||||
def test_ctime(self):
|
def test_ctime(self):
|
||||||
# XXX: POSIX-compliant ctime should refuse to convert year > 9999,
|
t = time.mktime((1973, 9, 16, 1, 3, 52, 0, 0, -1))
|
||||||
# but glibc implementation does not. For now, just check it doesn't
|
self.assertEqual(time.ctime(t), 'Sun Sep 16 01:03:52 1973')
|
||||||
# segfault as it did before, and the result contains no newline.
|
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:
|
try:
|
||||||
result = time.ctime(1e12)
|
bigval = time.mktime((10000, 1, 10) + (0,)*6)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# for POSIX-compliant runtimes (or 32-bit systems, where time_t
|
# If mktime fails, ctime will fail too. This may happen
|
||||||
# cannot hold timestamps with a five-digit year)
|
# on some platforms.
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.assertNotIn('\n', result)
|
self.assertEquals(time.ctime(bigval)[-5:], '10000')
|
||||||
|
|
||||||
@unittest.skipIf(not hasattr(time, "tzset"),
|
@unittest.skipIf(not hasattr(time, "tzset"),
|
||||||
"time module has no attribute tzset")
|
"time module has no attribute tzset")
|
||||||
|
|
|
@ -1422,6 +1422,12 @@ Core and Builtins
|
||||||
Extension Modules
|
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
|
- Issue #6608: time.asctime is now checking struct tm fields its input
|
||||||
before passing it to the system asctime. Patch by MunSic Jeong.
|
before passing it to the system asctime. Patch by MunSic Jeong.
|
||||||
|
|
||||||
|
|
|
@ -599,19 +599,51 @@ time_strptime(PyObject *self, PyObject *args)
|
||||||
return strptime_result;
|
return strptime_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(strptime_doc,
|
PyDoc_STRVAR(strptime_doc,
|
||||||
"strptime(string, format) -> struct_time\n\
|
"strptime(string, format) -> struct_time\n\
|
||||||
\n\
|
\n\
|
||||||
Parse a string to a time tuple according to a format specification.\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()).");
|
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 *
|
static PyObject *
|
||||||
time_asctime(PyObject *self, PyObject *args)
|
time_asctime(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PyObject *tup = NULL;
|
PyObject *tup = NULL;
|
||||||
struct tm buf;
|
struct tm buf;
|
||||||
char *p, *q;
|
|
||||||
if (!PyArg_UnpackTuple(args, "asctime", 0, 1, &tup))
|
if (!PyArg_UnpackTuple(args, "asctime", 0, 1, &tup))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (tup == NULL) {
|
if (tup == NULL) {
|
||||||
|
@ -619,17 +651,7 @@ time_asctime(PyObject *self, PyObject *args)
|
||||||
buf = *localtime(&tt);
|
buf = *localtime(&tt);
|
||||||
} else if (!gettmarg(tup, &buf) || !checktm(&buf))
|
} else if (!gettmarg(tup, &buf) || !checktm(&buf))
|
||||||
return NULL;
|
return NULL;
|
||||||
p = asctime(&buf);
|
return _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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(asctime_doc,
|
PyDoc_STRVAR(asctime_doc,
|
||||||
|
@ -644,7 +666,7 @@ time_ctime(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PyObject *ot = NULL;
|
PyObject *ot = NULL;
|
||||||
time_t tt;
|
time_t tt;
|
||||||
char *p, *q;
|
struct tm *timeptr;
|
||||||
|
|
||||||
if (!PyArg_UnpackTuple(args, "ctime", 0, 1, &ot))
|
if (!PyArg_UnpackTuple(args, "ctime", 0, 1, &ot))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -658,17 +680,12 @@ time_ctime(PyObject *self, PyObject *args)
|
||||||
if (tt == (time_t)-1 && PyErr_Occurred())
|
if (tt == (time_t)-1 && PyErr_Occurred())
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
p = ctime(&tt);
|
timeptr = localtime(&tt);
|
||||||
if (p == NULL) {
|
if (timeptr == NULL) {
|
||||||
PyErr_SetString(PyExc_ValueError, "unconvertible time");
|
PyErr_SetString(PyExc_ValueError, "unconvertible time");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* Replace a terminating newline by a null byte, normally at position 24.
|
return _asctime(timeptr);
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(ctime_doc,
|
PyDoc_STRVAR(ctime_doc,
|
||||||
|
|
Loading…
Reference in New Issue