mirror of https://github.com/python/cpython
Revert "[3.13] gh-120713: Normalize year with century for datetime.strftime (GH-120820) (GH-121144)" (GH-122408)
This reverts commit 009618f112
.
This commit is contained in:
parent
10cf7d6d00
commit
9f6f8790ef
|
@ -204,17 +204,6 @@ def _format_offset(off, sep=':'):
|
||||||
s += '.%06d' % ss.microseconds
|
s += '.%06d' % ss.microseconds
|
||||||
return s
|
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.
|
# Correctly substitute for %z and %Z escapes in strftime formats.
|
||||||
def _wrap_strftime(object, format, timetuple):
|
def _wrap_strftime(object, format, timetuple):
|
||||||
# Don't call utcoffset() or tzname() unless actually needed.
|
# 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 %
|
# strftime is going to have at this: escape %
|
||||||
Zreplace = s.replace('%', '%%')
|
Zreplace = s.replace('%', '%%')
|
||||||
newformat.append(Zreplace)
|
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:
|
else:
|
||||||
push('%')
|
push('%')
|
||||||
push(ch)
|
push(ch)
|
||||||
|
|
|
@ -1697,26 +1697,18 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
|
||||||
self.assertTrue(self.theclass.max)
|
self.assertTrue(self.theclass.max)
|
||||||
|
|
||||||
def test_strftime_y2k(self):
|
def test_strftime_y2k(self):
|
||||||
# Test that years less than 1000 are 0-padded; note that the beginning
|
for y in (1, 49, 70, 99, 100, 999, 1000, 1970):
|
||||||
# of an ISO 8601 year may fall in an ISO week of the year before, and
|
d = self.theclass(y, 1, 1)
|
||||||
# therefore needs an offset of -1 when formatting with '%G'.
|
# Issue 13305: For years < 1000, the value is not always
|
||||||
dataset = (
|
# padded to 4 digits across platforms. The C standard
|
||||||
(1, 0),
|
# assumes year >= 1900, so it does not specify the number
|
||||||
(49, -1),
|
# of digits.
|
||||||
(70, 0),
|
if d.strftime("%Y") != '%04d' % y:
|
||||||
(99, 0),
|
# Year 42 returns '42', not padded
|
||||||
(100, -1),
|
self.assertEqual(d.strftime("%Y"), '%d' % y)
|
||||||
(999, 0),
|
# '0042' is obtained anyway
|
||||||
(1000, 0),
|
if support.has_strftime_extensions:
|
||||||
(1970, 0),
|
self.assertEqual(d.strftime("%4Y"), '%04d' % y)
|
||||||
)
|
|
||||||
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}")
|
|
||||||
|
|
||||||
def test_replace(self):
|
def test_replace(self):
|
||||||
cls = self.theclass
|
cls = self.theclass
|
||||||
|
|
|
@ -1848,11 +1848,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
|
||||||
const char *ptoappend; /* ptr to string to append to output buffer */
|
const char *ptoappend; /* ptr to string to append to output buffer */
|
||||||
Py_ssize_t ntoappend; /* # of bytes 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(object && format && timetuple);
|
||||||
assert(PyUnicode_Check(format));
|
assert(PyUnicode_Check(format));
|
||||||
/* Convert the input format to a C string and size */
|
/* Convert the input format to a C string and size */
|
||||||
|
@ -1860,11 +1855,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
|
||||||
if (!pin)
|
if (!pin)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
PyObject *strftime = _PyImport_GetModuleAttrString("time", "strftime");
|
|
||||||
if (strftime == NULL) {
|
|
||||||
goto Done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan the input format, looking for %z/%Z/%f escapes, building
|
/* Scan the input format, looking for %z/%Z/%f escapes, building
|
||||||
* a new format. Since computing the replacements for those codes
|
* a new format. Since computing the replacements for those codes
|
||||||
* is expensive, don't unless they're actually used.
|
* 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);
|
ptoappend = PyBytes_AS_STRING(freplacement);
|
||||||
ntoappend = PyBytes_GET_SIZE(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 {
|
else {
|
||||||
/* percent followed by something else */
|
/* percent followed by something else */
|
||||||
#ifdef Py_NORMALIZE_CENTURY
|
|
||||||
PassThrough:
|
|
||||||
#endif
|
|
||||||
ptoappend = pin - 2;
|
ptoappend = pin - 2;
|
||||||
ntoappend = 2;
|
ntoappend = 2;
|
||||||
}
|
}
|
||||||
|
@ -2018,13 +1969,17 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
|
||||||
goto Done;
|
goto Done;
|
||||||
{
|
{
|
||||||
PyObject *format;
|
PyObject *format;
|
||||||
|
PyObject *strftime = _PyImport_GetModuleAttrString("time", "strftime");
|
||||||
|
|
||||||
|
if (strftime == NULL)
|
||||||
|
goto Done;
|
||||||
format = PyUnicode_FromString(PyBytes_AS_STRING(newfmt));
|
format = PyUnicode_FromString(PyBytes_AS_STRING(newfmt));
|
||||||
if (format != NULL) {
|
if (format != NULL) {
|
||||||
result = PyObject_CallFunctionObjArgs(strftime,
|
result = PyObject_CallFunctionObjArgs(strftime,
|
||||||
format, timetuple, NULL);
|
format, timetuple, NULL);
|
||||||
Py_DECREF(format);
|
Py_DECREF(format);
|
||||||
}
|
}
|
||||||
|
Py_DECREF(strftime);
|
||||||
}
|
}
|
||||||
Done:
|
Done:
|
||||||
Py_XDECREF(freplacement);
|
Py_XDECREF(freplacement);
|
||||||
|
@ -2032,7 +1987,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
|
||||||
Py_XDECREF(colonzreplacement);
|
Py_XDECREF(colonzreplacement);
|
||||||
Py_XDECREF(Zreplacement);
|
Py_XDECREF(Zreplacement);
|
||||||
Py_XDECREF(newfmt);
|
Py_XDECREF(newfmt);
|
||||||
Py_XDECREF(strftime);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25925,58 +25925,6 @@ printf "%s\n" "#define HAVE_STAT_TV_NSEC2 1" >>confdefs.h
|
||||||
|
|
||||||
fi
|
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_curses=no
|
||||||
have_panel=no
|
have_panel=no
|
||||||
|
|
||||||
|
|
28
configure.ac
28
configure.ac
|
@ -6612,34 +6612,6 @@ then
|
||||||
[Define if you have struct stat.st_mtimensec])
|
[Define if you have struct stat.st_mtimensec])
|
||||||
fi
|
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 ncursesw/ncurses and panelw/panel
|
dnl check for ncursesw/ncurses and panelw/panel
|
||||||
dnl NOTE: old curses is not detected.
|
dnl NOTE: old curses is not detected.
|
||||||
dnl have_curses=[no, yes]
|
dnl have_curses=[no, yes]
|
||||||
|
|
|
@ -1686,9 +1686,6 @@
|
||||||
SipHash13: 3, externally defined: 0 */
|
SipHash13: 3, externally defined: 0 */
|
||||||
#undef Py_HASH_ALGORITHM
|
#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 */
|
/* Define if rl_startup_hook takes arguments */
|
||||||
#undef Py_RL_STARTUP_HOOK_TAKES_ARGS
|
#undef Py_RL_STARTUP_HOOK_TAKES_ARGS
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue