https://github.com/python/cpython/commit/f884dc6f70fde3550a175ff7ddce79ccc38b3ec6
commit: f884dc6f70fde3550a175ff7ddce79ccc38b3ec6
branch: main
author: Jason Yalim, PhD <[email protected]>
committer: encukou <[email protected]>
date: 2026-03-13T14:00:39+01:00
summary:

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


Co-authored-by: Stan Ulbrych <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-02-17-03-43-07.gh-issue-140715.twmcM_.rst
M Doc/library/datetime.rst
M Lib/_strptime.py
M Lib/test/datetimetester.py
M Lib/test/test_strptime.py
M Lib/test/test_time.py

diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
index 73217136f14472..8993049a720b1c 100644
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -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
diff --git a/Lib/_strptime.py b/Lib/_strptime.py
index 0d81ff6765e1ed..3367ac485a590c 100644
--- a/Lib/_strptime.py
+++ b/Lib/_strptime.py
@@ -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
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 97eec618932aa5..e264433ca590bf 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -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
diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py
index fd8525feb88d53..dfc8ef6d2c5b7e 100644
--- a/Lib/test/test_strptime.py
+++ b/Lib/test/test_strptime.py
@@ -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)"""
 
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
index da5fd16b8b6291..be8f6b057654c2 100644
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -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':
diff --git 
a/Misc/NEWS.d/next/Library/2026-02-17-03-43-07.gh-issue-140715.twmcM_.rst 
b/Misc/NEWS.d/next/Library/2026-02-17-03-43-07.gh-issue-140715.twmcM_.rst
new file mode 100644
index 00000000000000..3bebc6660df825
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-02-17-03-43-07.gh-issue-140715.twmcM_.rst
@@ -0,0 +1 @@
+Add ``%n`` and ``%t`` support to :meth:`~datetime.datetime.strptime`.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to