gh-140715: Add %t and %n format codes support to strptime() (GH-144896)

Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
This commit is contained in:
Jason Yalim, PhD 2026-03-13 06:00:39 -07:00 committed by GitHub
parent 9162238511
commit f884dc6f70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 46 additions and 7 deletions

View file

@ -2611,8 +2611,10 @@ requires, and these work on all supported platforms.
| ``%M`` | Minute as a zero-padded | 00, 01, ..., 59 | \(9) |
| | decimal number. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%n`` | The newline character | ``\n`` | \(0) |
| | (``'\n'``). | | |
| ``%n`` | The newline character | ``\n`` | |
| | (``'\n'``). For | | |
| | :meth:`!strptime`, zero or | | |
| | more whitespace. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%p`` | Locale's equivalent of either || AM, PM (en_US); | \(1), |
| | AM or PM. || am, pm (de_DE) | \(3) |
@ -2625,8 +2627,9 @@ requires, and these work on all supported platforms.
| ``%S`` | Second as a zero-padded | 00, 01, ..., 59 | \(4), |
| | decimal number. | | \(9) |
+-----------+--------------------------------+------------------------+-------+
| ``%t`` | The tab character | ``\t`` | \(0) |
| | (``'\t'``). | | |
| ``%t`` | The tab character (``'\t'``). | ``\t`` | |
| | For :meth:`!strptime`, | | |
| | zero or more whitespace. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%T`` | ISO 8601 time format, | 10:01:59 | |
| | equivalent to ``%H:%M:%S``. | | |
@ -2717,7 +2720,8 @@ differences between platforms in handling of unsupported format specifiers.
``%:z`` was added for :meth:`~.datetime.strftime`.
.. versionadded:: 3.15
``%:z``, ``%F``, and ``%D`` were added for :meth:`~.datetime.strptime`.
``%D``, ``%F``, ``%n``, ``%t``, and ``%:z`` were added for
:meth:`~.datetime.strptime`.
Technical detail

View file

@ -382,7 +382,10 @@ def __init__(self, locale_time=None):
'Z': self.__seqToRE((tz for tz_names in self.locale_time.timezone
for tz in tz_names),
'Z'),
'%': '%'}
'n': r'\s*',
't': r'\s*',
'%': '%',
}
if self.locale_time.LC_alt_digits is None:
for d in 'dmyCHIMS':
mapping['O' + d] = r'(?P<%s>\d\d|\d| \d)' % d

View file

@ -2207,6 +2207,20 @@ def test_strptime_D_format(self):
self.theclass.strptime(test_date, "%m/%d/%y")
)
def test_strptime_n_and_t_format(self):
format_directives = ('%n', '%t', '%n%t', '%t%n')
whitespaces = ('', ' ', '\t', '\r', '\v', '\n', '\f')
for fd in format_directives:
for ws in (*whitespaces, ''.join(whitespaces)):
with self.subTest(format_directive=fd, whitespace=ws):
self.assertEqual(
self.theclass.strptime(
f"2026{ws}02{ws}03",
f"%Y{fd}%m{fd}%d",
),
self.theclass(2026, 2, 3),
)
#############################################################################
# datetime tests

View file

@ -670,6 +670,23 @@ def test_strptime_D_format(self):
time.strptime(test_date, "%m/%d/%y")
)
def test_strptime_n_and_t_format(self):
format_directives = ('%n', '%t', '%n%t', '%t%n')
whitespaces = ('', ' ', '\t', '\r', '\v', '\n', '\f')
for fd in format_directives:
for ws in (*whitespaces, ''.join(whitespaces)):
with self.subTest(format_directive=fd, whitespace=ws):
self.assertEqual(
time.strptime(
f"2026{ws}02{ws}03",
f"%Y{fd}%m{fd}%d",
),
time.strptime(
f'2026-02-03',
"%Y-%m-%d",
),
)
class Strptime12AMPMTests(unittest.TestCase):
"""Test a _strptime regression in '%I %p' at 12 noon (12 PM)"""

View file

@ -359,7 +359,7 @@ def test_strptime(self):
# raising an exception.
tt = time.gmtime(self.t)
for directive in ('a', 'A', 'b', 'B', 'c', 'd', 'D', 'F', 'H', 'I',
'j', 'm', 'M', 'p', 'S', 'T',
'j', 'm', 'M', 'n', 'p', 'S', 't', 'T',
'U', 'w', 'W', 'x', 'X', 'y', 'Y', 'Z', '%'):
format = '%' + directive
if directive == 'd':

View file

@ -0,0 +1 @@
Add ``%n`` and ``%t`` support to :meth:`~datetime.datetime.strptime`.