Skip to content

Commit 61c2c61

Browse files
committed
pythongh-122272: guarantee specifiers %F and %C for datetime.strftime to be 0-padded
1 parent ac8da34 commit 61c2c61

File tree

3 files changed

+44
-13
lines changed

3 files changed

+44
-13
lines changed

Lib/_pydatetime.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,14 +272,19 @@ def _wrap_strftime(object, format, timetuple):
272272
# strftime is going to have at this: escape %
273273
Zreplace = s.replace('%', '%%')
274274
newformat.append(Zreplace)
275-
elif ch in 'YG' and object.year < 1000 and _need_normalize_century():
276-
# Note that datetime(1000, 1, 1).strftime('%G') == '1000' so
277-
# year 1000 for %G can go on the fast path.
275+
# Note that datetime(1000, 1, 1).strftime('%G') == '1000' so
276+
# year 1000 for %G can go on the fast path.
277+
elif ch in 'YGFC' and object.year < 1000 and _need_normalize_century():
278278
if ch == 'G':
279279
year = int(_time.strftime("%G", timetuple))
280280
else:
281281
year = object.year
282-
push('{:04}'.format(year))
282+
if ch == 'C':
283+
push('{:02}'.format(year // 100))
284+
else:
285+
push('{:04}'.format(year))
286+
if ch == 'F':
287+
push('-{:02}-{:02}'.format(*timetuple[1:3]))
283288
else:
284289
push('%')
285290
push(ch)

Lib/test/datetimetester.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,13 +1710,19 @@ def test_strftime_y2k(self):
17101710
(1000, 0),
17111711
(1970, 0),
17121712
)
1713-
for year, offset in dataset:
1714-
for specifier in 'YG':
1713+
for year, g_offset in dataset:
1714+
for specifier in 'YGFC':
17151715
with self.subTest(year=year, specifier=specifier):
17161716
d = self.theclass(year, 1, 1)
17171717
if specifier == 'G':
1718-
year += offset
1719-
self.assertEqual(d.strftime(f"%{specifier}"), f"{year:04d}")
1718+
year += g_offset
1719+
if specifier == 'C':
1720+
expected = f"{year // 100:02d}"
1721+
else:
1722+
expected = f"{year:04d}"
1723+
if specifier == 'F':
1724+
expected += f"-01-01"
1725+
self.assertEqual(d.strftime(f"%{specifier}"), expected)
17201726

17211727
def test_replace(self):
17221728
cls = self.theclass

Modules/_datetimemodule.c

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,8 +1852,10 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
18521852
Py_ssize_t ntoappend; /* # of bytes to append to output buffer */
18531853

18541854
#ifdef Py_NORMALIZE_CENTURY
1855-
/* Buffer of maximum size of formatted year permitted by long. */
1856-
char buf[SIZEOF_LONG*5/2+2];
1855+
/* Buffer of maximum size of formatted year permitted by long.
1856+
* Adding 6 just to accomodate %F with dashes, 2-digit month and day.
1857+
*/
1858+
char buf[SIZEOF_LONG*5/2+2+6];
18571859
#endif
18581860

18591861
assert(object && format && timetuple);
@@ -1950,7 +1952,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
19501952
ntoappend = PyBytes_GET_SIZE(freplacement);
19511953
}
19521954
#ifdef Py_NORMALIZE_CENTURY
1953-
else if (ch == 'Y' || ch == 'G') {
1955+
else if (ch == 'Y' || ch == 'G' || ch == 'F' || ch == 'C') {
19541956
/* 0-pad year with century as necessary */
19551957
PyObject *item = PyTuple_GET_ITEM(timetuple, 0);
19561958
long year_long = PyLong_AsLong(item);
@@ -1980,8 +1982,26 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
19801982
goto Done;
19811983
}
19821984
}
1983-
1984-
ntoappend = PyOS_snprintf(buf, sizeof(buf), "%04ld", year_long);
1985+
if (ch == 'F') {
1986+
item = PyTuple_GET_ITEM(timetuple, 1);
1987+
long month = PyLong_AsLong(item);
1988+
if (month == -1 && PyErr_Occurred()) {
1989+
goto Done;
1990+
}
1991+
item = PyTuple_GET_ITEM(timetuple, 2);
1992+
long day = PyLong_AsLong(item);
1993+
if (day == -1 && PyErr_Occurred()) {
1994+
goto Done;
1995+
}
1996+
ntoappend = PyOS_snprintf(buf, sizeof(buf), "%04ld-%02ld-%02ld",
1997+
year_long, month, day);
1998+
}
1999+
else {
2000+
ntoappend = PyOS_snprintf(buf, sizeof(buf), "%04ld", year_long);
2001+
if (ch == 'C') {
2002+
ntoappend -= 2;
2003+
}
2004+
}
19852005
ptoappend = buf;
19862006
}
19872007
#endif

0 commit comments

Comments
 (0)