diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 27cacb8e01f..34ccb2da13d 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -204,17 +204,6 @@ def _format_offset(off, sep=':'): s += '.%06d' % ss.microseconds return s -_normalize_century = None -def _need_normalize_century(): - global _normalize_century - if _normalize_century is None: - try: - _normalize_century = ( - _time.strftime("%Y", (99, 1, 1, 0, 0, 0, 0, 1, 0)) != "0099") - except ValueError: - _normalize_century = True - return _normalize_century - # Correctly substitute for %z and %Z escapes in strftime formats. def _wrap_strftime(object, format, timetuple): # Don't call utcoffset() or tzname() unless actually needed. @@ -272,14 +261,6 @@ def _wrap_strftime(object, format, timetuple): # strftime is going to have at this: escape % Zreplace = s.replace('%', '%%') newformat.append(Zreplace) - elif ch in 'YG' and object.year < 1000 and _need_normalize_century(): - # Note that datetime(1000, 1, 1).strftime('%G') == '1000' so - # year 1000 for %G can go on the fast path. - if ch == 'G': - year = int(_time.strftime("%G", timetuple)) - else: - year = object.year - push('{:04}'.format(year)) else: push('%') push(ch) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index ca804fe28b3..d0ee974dbcd 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1697,26 +1697,18 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase): self.assertTrue(self.theclass.max) def test_strftime_y2k(self): - # Test that years less than 1000 are 0-padded; note that the beginning - # of an ISO 8601 year may fall in an ISO week of the year before, and - # therefore needs an offset of -1 when formatting with '%G'. - dataset = ( - (1, 0), - (49, -1), - (70, 0), - (99, 0), - (100, -1), - (999, 0), - (1000, 0), - (1970, 0), - ) - for year, offset in dataset: - for specifier in 'YG': - with self.subTest(year=year, specifier=specifier): - d = self.theclass(year, 1, 1) - if specifier == 'G': - year += offset - self.assertEqual(d.strftime(f"%{specifier}"), f"{year:04d}") + for y in (1, 49, 70, 99, 100, 999, 1000, 1970): + d = self.theclass(y, 1, 1) + # Issue 13305: For years < 1000, the value is not always + # padded to 4 digits across platforms. The C standard + # assumes year >= 1900, so it does not specify the number + # of digits. + if d.strftime("%Y") != '%04d' % y: + # Year 42 returns '42', not padded + self.assertEqual(d.strftime("%Y"), '%d' % y) + # '0042' is obtained anyway + if support.has_strftime_extensions: + self.assertEqual(d.strftime("%4Y"), '%04d' % y) def test_replace(self): cls = self.theclass diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 18631195a29..4706a93bd15 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1848,11 +1848,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, const char *ptoappend; /* ptr to string to append to output buffer */ Py_ssize_t ntoappend; /* # of bytes to append to output buffer */ -#ifdef Py_NORMALIZE_CENTURY - /* Buffer of maximum size of formatted year permitted by long. */ - char buf[SIZEOF_LONG*5/2+2]; -#endif - assert(object && format && timetuple); assert(PyUnicode_Check(format)); /* Convert the input format to a C string and size */ @@ -1860,11 +1855,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, if (!pin) return NULL; - PyObject *strftime = _PyImport_GetModuleAttrString("time", "strftime"); - if (strftime == NULL) { - goto Done; - } - /* Scan the input format, looking for %z/%Z/%f escapes, building * a new format. Since computing the replacements for those codes * is expensive, don't unless they're actually used. @@ -1946,47 +1936,8 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, ptoappend = PyBytes_AS_STRING(freplacement); ntoappend = PyBytes_GET_SIZE(freplacement); } -#ifdef Py_NORMALIZE_CENTURY - else if (ch == 'Y' || ch == 'G') { - /* 0-pad year with century as necessary */ - PyObject *item = PyTuple_GET_ITEM(timetuple, 0); - long year_long = PyLong_AsLong(item); - - if (year_long == -1 && PyErr_Occurred()) { - goto Done; - } - /* Note that datetime(1000, 1, 1).strftime('%G') == '1000' so year - 1000 for %G can go on the fast path. */ - if (year_long >= 1000) { - goto PassThrough; - } - if (ch == 'G') { - PyObject *year_str = PyObject_CallFunction(strftime, "sO", - "%G", timetuple); - if (year_str == NULL) { - goto Done; - } - PyObject *year = PyNumber_Long(year_str); - Py_DECREF(year_str); - if (year == NULL) { - goto Done; - } - year_long = PyLong_AsLong(year); - Py_DECREF(year); - if (year_long == -1 && PyErr_Occurred()) { - goto Done; - } - } - - ntoappend = PyOS_snprintf(buf, sizeof(buf), "%04ld", year_long); - ptoappend = buf; - } -#endif else { /* percent followed by something else */ -#ifdef Py_NORMALIZE_CENTURY - PassThrough: -#endif ptoappend = pin - 2; ntoappend = 2; } @@ -2018,13 +1969,17 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, goto Done; { PyObject *format; + PyObject *strftime = _PyImport_GetModuleAttrString("time", "strftime"); + if (strftime == NULL) + goto Done; format = PyUnicode_FromString(PyBytes_AS_STRING(newfmt)); if (format != NULL) { result = PyObject_CallFunctionObjArgs(strftime, format, timetuple, NULL); Py_DECREF(format); } + Py_DECREF(strftime); } Done: Py_XDECREF(freplacement); @@ -2032,7 +1987,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, Py_XDECREF(colonzreplacement); Py_XDECREF(Zreplacement); Py_XDECREF(newfmt); - Py_XDECREF(strftime); return result; } diff --git a/configure b/configure index dcca9612f26..beffc1fd76c 100755 --- a/configure +++ b/configure @@ -25925,58 +25925,6 @@ printf "%s\n" "#define HAVE_STAT_TV_NSEC2 1" >>confdefs.h fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether year with century should be normalized for strftime" >&5 -printf %s "checking whether year with century should be normalized for strftime... " >&6; } -if test ${ac_cv_normalize_century+y} -then : - printf %s "(cached) " >&6 -else $as_nop - -if test "$cross_compiling" = yes -then : - ac_cv_normalize_century=yes -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include -#include - -int main(void) -{ - char year[5]; - struct tm date = { - .tm_year = -1801, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { - return 1; - } - return 0; -} - -_ACEOF -if ac_fn_c_try_run "$LINENO" -then : - ac_cv_normalize_century=yes -else $as_nop - ac_cv_normalize_century=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_normalize_century" >&5 -printf "%s\n" "$ac_cv_normalize_century" >&6; } -if test "$ac_cv_normalize_century" = yes -then - -printf "%s\n" "#define Py_NORMALIZE_CENTURY 1" >>confdefs.h - -fi - have_curses=no have_panel=no diff --git a/configure.ac b/configure.ac index 1459a3ce62c..5842bd24c45 100644 --- a/configure.ac +++ b/configure.ac @@ -6612,34 +6612,6 @@ then [Define if you have struct stat.st_mtimensec]) fi -AC_CACHE_CHECK([whether year with century should be normalized for strftime], [ac_cv_normalize_century], [ -AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include -#include - -int main(void) -{ - char year[5]; - struct tm date = { - .tm_year = -1801, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { - return 1; - } - return 0; -} -]])], -[ac_cv_normalize_century=yes], -[ac_cv_normalize_century=no], -[ac_cv_normalize_century=yes])]) -if test "$ac_cv_normalize_century" = yes -then - AC_DEFINE([Py_NORMALIZE_CENTURY], [1], - [Define if year with century should be normalized for strftime.]) -fi - dnl check for ncursesw/ncurses and panelw/panel dnl NOTE: old curses is not detected. dnl have_curses=[no, yes] diff --git a/pyconfig.h.in b/pyconfig.h.in index 10980c92176..4d8b1d4f254 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1686,9 +1686,6 @@ SipHash13: 3, externally defined: 0 */ #undef Py_HASH_ALGORITHM -/* Define if year with century should be normalized for strftime. */ -#undef Py_NORMALIZE_CENTURY - /* Define if rl_startup_hook takes arguments */ #undef Py_RL_STARTUP_HOOK_TAKES_ARGS