https://github.com/python/cpython/commit/e6878fc3c871dcc3ae1854735ec264f6a9c8dd42
commit: e6878fc3c871dcc3ae1854735ec264f6a9c8dd42
branch: 3.12
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2024-07-29T21:55:12+03:00
summary:

Revert "[3.12] gh-120713: Normalize year with century for datetime.strftime 
(GH-120820) (GH-121145)" (GH-122409)

This reverts commit 027902b56f2176c571e5e244c3c23dc7247b67dc.

files:
D Misc/NEWS.d/next/Library/2024-06-21-06-37-46.gh-issue-120713.WBbQx4.rst
M Lib/_pydatetime.py
M Lib/test/datetimetester.py
M Modules/_datetimemodule.c
M configure
M configure.ac
M pyconfig.h.in

diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py
index d5d4210a35eb8c..ad6292e1e412c4 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 ac6a07b2509ad8..0528e0701fa982 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -1687,26 +1687,18 @@ def test_bool(self):
         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/Misc/NEWS.d/next/Library/2024-06-21-06-37-46.gh-issue-120713.WBbQx4.rst 
b/Misc/NEWS.d/next/Library/2024-06-21-06-37-46.gh-issue-120713.WBbQx4.rst
deleted file mode 100644
index 18386a43eddc6f..00000000000000
--- a/Misc/NEWS.d/next/Library/2024-06-21-06-37-46.gh-issue-120713.WBbQx4.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-:meth:`datetime.datetime.strftime` now 0-pads years with less than four digits 
for the format specifiers ``%Y`` and ``%G`` on Linux.
-Patch by Ben Hsing
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 632baa06f26950..5a062b9c8c0e2c 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -1603,11 +1603,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 */
@@ -1615,11 +1610,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.
@@ -1701,47 +1691,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;
         }
@@ -1773,13 +1724,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);
@@ -1787,7 +1742,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 d93a4e91f485d7..4dfaeecfc0b269 100755
--- a/configure
+++ b/configure
@@ -25687,58 +25687,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 <time.h>
-#include <string.h>
-
-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 b46098ae61663c..0d6df8e24e4233 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6415,34 +6415,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 <time.h>
-#include <string.h>
-
-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 ncurses/ncursesw and panel/panelw
 dnl NOTE: old curses is not detected.
 dnl have_curses=[no, ncursesw, ncurses]
diff --git a/pyconfig.h.in b/pyconfig.h.in
index b8e3c830fbecf5..6d370f6664c10c 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -1618,9 +1618,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 you want to enable internal statistics gathering. */
 #undef Py_STATS
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to