]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Revert "[3.12] gh-120713: Normalize year with century for datetime.strftime (GH-12082...
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 29 Jul 2024 18:55:12 +0000 (21:55 +0300)
committerGitHub <noreply@github.com>
Mon, 29 Jul 2024 18:55:12 +0000 (21:55 +0300)
This reverts commit 027902b56f2176c571e5e244c3c23dc7247b67dc.

Lib/_pydatetime.py
Lib/test/datetimetester.py
Misc/NEWS.d/next/Library/2024-06-21-06-37-46.gh-issue-120713.WBbQx4.rst [deleted file]
Modules/_datetimemodule.c
configure
configure.ac
pyconfig.h.in

index d5d4210a35eb8c2ed9701a35bf59b5a58b561d19..ad6292e1e412c44b47bbc32219960073fac47181 100644 (file)
@@ -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)
index ac6a07b2509ad8c2f7c0b5abdb8f6a0b73f9c9b1..0528e0701fa982ce4a07ff4d2b10f4a975f8d1a1 100644 (file)
@@ -1687,26 +1687,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/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 (file)
index 18386a4..0000000
+++ /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
index 632baa06f269501c3c81518b486d547435db9d5f..5a062b9c8c0e2ceb1548931bb357d8be82c031e5 100644 (file)
@@ -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;
 }
 
index d93a4e91f485d7ee5ffdcb52cd9d821e4b492b2b..4dfaeecfc0b2693f61f491a1cfa56913470d8996 100755 (executable)
--- 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
 
index b46098ae61663cbee0789913e2c01901651ff67a..0d6df8e24e4233a792512aeec09065ea840b5893 100644 (file)
@@ -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]
index b8e3c830fbecf51b25835034d28bfd7b248379ea..6d370f6664c10c0b7c28c3ab7781e38c5e9f7bff 100644 (file)
    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