Skip to content
Prev Previous commit
Next Next commit
CPython needs C99
  • Loading branch information
encukou committed Jun 27, 2025
commit 8231ace06c0b160fa6ec62ac092350bced352572
38 changes: 22 additions & 16 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -1807,7 +1807,7 @@ def test_bool(self):
self.assertTrue(self.theclass.min)
self.assertTrue(self.theclass.max)

def test_strftime_y2k(self):
def check_strftime_y2k(self, specifier):
# 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'.
Expand All @@ -1821,22 +1821,28 @@ def test_strftime_y2k(self):
(1000, 0),
(1970, 0),
)
specifiers = 'YG'
if _time.strftime('%F', (1900, 1, 1, 0, 0, 0, 0, 1, 0)) == '1900-01-01':
specifiers += 'FC'
for year, g_offset in dataset:
for specifier in specifiers:
with self.subTest(year=year, specifier=specifier):
d = self.theclass(year, 1, 1)
if specifier == 'G':
year += g_offset
if specifier == 'C':
expected = f"{year // 100:02d}"
else:
expected = f"{year:04d}"
if specifier == 'F':
expected += f"-01-01"
self.assertEqual(d.strftime(f"%{specifier}"), expected)
with self.subTest(year=year, specifier=specifier):
d = self.theclass(year, 1, 1)
if specifier == 'G':
year += g_offset
if specifier == 'C':
expected = f"{year // 100:02d}"
else:
expected = f"{year:04d}"
if specifier == 'F':
expected += f"-01-01"
self.assertEqual(d.strftime(f"%{specifier}"), expected)

def test_strftime_y2k(self):
self.check_strftime_y2k('Y')
self.check_strftime_y2k('G')

def test_strftime_y2k_c99(self):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not it be decorated with cpython_only?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! The meaning of cpython_only is not very clear.

This test might be usable for other implementations as well, so I think it's best for other implementations to skip the test if it doesn't work for them. IMO, cpython_only is best for things like bytecode details or sys.getsizeof.
But, that's just my opinion of course.

# CPython requires C11; specifiers new in C99 must work.
# (Other implementations may want to disable this test.)
self.check_strftime_y2k('F')
self.check_strftime_y2k('C')

def test_replace(self):
cls = self.theclass
Expand Down
22 changes: 2 additions & 20 deletions Modules/_datetimemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1781,24 +1781,6 @@ normalize_century(void)
return cache;
}

/* Check whether C99-specific strftime specifiers are supported. */
inline static int
strftime_c99_support(void)
{
static int cache = -1;
if (cache < 0) {
char full_date[11];
struct tm date = {
.tm_year = 0,
.tm_mon = 0,
.tm_mday = 1
};
cache = (strftime(full_date, sizeof(full_date), "%F", &date) &&
strcmp(full_date, "1900-01-01") == 0);
}
return cache;
}

static PyObject *
make_somezreplacement(PyObject *object, char *sep, PyObject *tzinfoarg)
{
Expand Down Expand Up @@ -1970,8 +1952,8 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
}
replacement = freplacement;
}
else if (normalize_century() && (ch == 'Y' || ch == 'G' ||
(strftime_c99_support() && (ch == 'F' || ch == 'C'))))
else if (normalize_century()
&& (ch == 'Y' || ch == 'G' || ch == 'F' || ch == 'C'))
{
/* 0-pad year with century as necessary */
PyObject *item = PySequence_GetItem(timetuple, 0);
Expand Down
1 change: 0 additions & 1 deletion Tools/c-analyzer/cpython/ignored.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,6 @@ Modules/_datetimemodule.c parse_hh_mm_ss_ff correction -
Modules/_datetimemodule.c time_isoformat specs -
Modules/_datetimemodule.c - capi_types -
Modules/_datetimemodule.c normalize_century cache -
Modules/_datetimemodule.c strftime_c99_support cache -
Modules/_decimal/_decimal.c - cond_map_template -
Modules/_decimal/_decimal.c - dec_signal_string -
Modules/_decimal/_decimal.c - dflt_ctx -
Expand Down
Loading