From 43c8345b1bd59d289da15f8e3a1af1f4d39fe223 Mon Sep 17 00:00:00 2001 From: Jason Yalim Date: Mon, 16 Feb 2026 20:32:44 -0700 Subject: [PATCH 01/10] add `%t` C99 token to strptime --- Lib/_strptime.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/_strptime.py b/Lib/_strptime.py index fe34808d88769a..b734954ab46bda 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -382,7 +382,9 @@ def __init__(self, locale_time=None): 'Z': self.__seqToRE((tz for tz_names in self.locale_time.timezone for tz in tz_names), 'Z'), - '%': '%'} + 't': '\t', + '%': '%' + } if self.locale_time.LC_alt_digits is None: for d in 'dmyCHIMS': mapping['O' + d] = r'(?P<%s>\d\d|\d| \d)' % d From d8382362f67ee43f56a8802d398ca748152facd7 Mon Sep 17 00:00:00 2001 From: Jason Yalim Date: Mon, 16 Feb 2026 20:33:26 -0700 Subject: [PATCH 02/10] init passing tests --- Lib/test/datetimetester.py | 7 +++++++ Lib/test/test_strptime.py | 7 +++++++ Lib/test/test_time.py | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 97eec618932aa5..ddb82da01e0200 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2207,6 +2207,13 @@ def test_strptime_D_format(self): self.theclass.strptime(test_date, "%m/%d/%y") ) + def test_strptime_t_format(self): + test_date = "2026\t02\t16" + self.assertEqual( + self.theclass.strptime(test_date, "%Y%t%m%t%d"), + self.theclass.strptime(test_date, "%Y\t%m\t%d") + ) + ############################################################################# # datetime tests diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index fd8525feb88d53..cad3bd928b7ad9 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -670,6 +670,13 @@ def test_strptime_D_format(self): time.strptime(test_date, "%m/%d/%y") ) + def test_strptime_t_format(self): + test_date = "2026\t02\t16" + self.assertEqual( + time.strptime(test_date, "%Y%t%m%t%d"), + time.strptime(test_date, "%Y\t%m\t%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..a96808ea182f8c 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', 'p', 'S', 't', 'T', 'U', 'w', 'W', 'x', 'X', 'y', 'Y', 'Z', '%'): format = '%' + directive if directive == 'd': From a31ad125254ae81f13014cc3811f643a3c617365 Mon Sep 17 00:00:00 2001 From: Jason Yalim Date: Mon, 16 Feb 2026 20:34:47 -0700 Subject: [PATCH 03/10] drop `(0)` footnote from `%t` docs, reflecting support; add support note --- Doc/library/datetime.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 39a7a1530a95cc..a73419d801ff83 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -2584,7 +2584,7 @@ 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`` | The tab character | ``\t`` | | | | (``'\t'``). | | | +-----------+--------------------------------+------------------------+-------+ | ``%T`` | ISO 8601 time format, | 10:01:59 | | From 669870a6c7b8a37d4afc953c7760115be7f63186 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 03:43:09 +0000 Subject: [PATCH 04/10] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2026-02-17-03-43-07.gh-issue-140715.twmcM_.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2026-02-17-03-43-07.gh-issue-140715.twmcM_.rst 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..f4ffd5187431a8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-17-03-43-07.gh-issue-140715.twmcM_.rst @@ -0,0 +1 @@ +Add ``'%t'`` support to :meth:`~datetime.datetime.strptime`. From d77ebf761b5947eadb08ab4312c0e7e4d1dda37b Mon Sep 17 00:00:00 2001 From: Jason Yalim Date: Mon, 16 Feb 2026 20:45:28 -0700 Subject: [PATCH 05/10] add support note --- Doc/library/datetime.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index a73419d801ff83..db836032b05114 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -2676,7 +2676,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`. + ``%:z``, ``%F``, ``%D``, and ``%t`` were added for + :meth:`~.datetime.strptime`. Technical Detail ^^^^^^^^^^^^^^^^ From fcc03cf95b4219712fac65a0b7e1806e1f4de73f Mon Sep 17 00:00:00 2001 From: Jason Yalim Date: Fri, 20 Feb 2026 22:32:32 -0700 Subject: [PATCH 06/10] modify %t -> more general r'\s+' from '\t' to meet C99 --- Lib/_strptime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_strptime.py b/Lib/_strptime.py index b734954ab46bda..f41177213f7666 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -382,7 +382,7 @@ def __init__(self, locale_time=None): 'Z': self.__seqToRE((tz for tz_names in self.locale_time.timezone for tz in tz_names), 'Z'), - 't': '\t', + 't': r'\s+', '%': '%' } if self.locale_time.LC_alt_digits is None: From 519315721587dac1adc482785d0ea992061a19d3 Mon Sep 17 00:00:00 2001 From: Jason Yalim Date: Fri, 20 Feb 2026 23:00:35 -0700 Subject: [PATCH 07/10] use theclass() and test '\s+' functionality, as requested --- Lib/test/datetimetester.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index ddb82da01e0200..8d8b971191d19a 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2208,10 +2208,13 @@ def test_strptime_D_format(self): ) def test_strptime_t_format(self): - test_date = "2026\t02\t16" + test_year,test_month,test_day = 2026,2,20 self.assertEqual( - self.theclass.strptime(test_date, "%Y%t%m%t%d"), - self.theclass.strptime(test_date, "%Y\t%m\t%d") + self.theclass.strptime( + f'{test_year} \r {test_month}\t \n{test_day}', + "%Y%t%m%t%d" + ), + self.theclass(test_year,test_month,test_day) ) From edfed51a3d07899f7e01e2a27d238887f23e52c7 Mon Sep 17 00:00:00 2001 From: Jason Yalim Date: Fri, 20 Feb 2026 23:38:13 -0700 Subject: [PATCH 08/10] test '\s+' functionality, as requested --- Lib/test/test_strptime.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index cad3bd928b7ad9..b8c33cb5c388de 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -671,10 +671,16 @@ def test_strptime_D_format(self): ) def test_strptime_t_format(self): - test_date = "2026\t02\t16" + test_year,test_month,test_day = 2026,2,20 self.assertEqual( - time.strptime(test_date, "%Y%t%m%t%d"), - time.strptime(test_date, "%Y\t%m\t%d") + time.strptime( + f'{test_year} \r {test_month}\t \n{test_day}', + "%Y%t%m%t%d" + ), + time.strptime( + f'{test_year}-{test_month}-{test_day}', + "%Y-%m-%d" + ) ) class Strptime12AMPMTests(unittest.TestCase): From 16195db0634753cf4edce518c49409d03aa96b15 Mon Sep 17 00:00:00 2001 From: Jason Yalim Date: Fri, 20 Feb 2026 23:39:59 -0700 Subject: [PATCH 09/10] alphabetize 3.15 patch notes --- Doc/library/datetime.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index db836032b05114..f15d7afa32d30d 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -2676,7 +2676,7 @@ differences between platforms in handling of unsupported format specifiers. ``%:z`` was added for :meth:`~.datetime.strftime`. .. versionadded:: 3.15 - ``%:z``, ``%F``, ``%D``, and ``%t`` were added for + ``%D``, ``%F``, ``%t``, and ``:%z`` were added for :meth:`~.datetime.strptime`. Technical Detail From 73019625213c39fb6a868ce72a81db4119742cde Mon Sep 17 00:00:00 2001 From: Jason Yalim Date: Sat, 21 Feb 2026 00:32:18 -0700 Subject: [PATCH 10/10] update docs to reflect powerful %t strptime functionality --- Doc/library/datetime.rst | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index f15d7afa32d30d..45f5f00f7978f4 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -2584,8 +2584,11 @@ 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`` | | -| | (``'\t'``). | | | +| ``%t`` | The tab character, ``'\t'``, | ``\t`` | \(11) | +| | for ``strftime``, else | | | +| | for ``strptime``, any | | | +| | blank-space character(s), | | | +| | i.e., ``'\s+'``. | | | +-----------+--------------------------------+------------------------+-------+ | ``%T`` | ISO 8601 time format, | 10:01:59 | | | | equivalent to ``%H:%M:%S``. | | | @@ -2881,6 +2884,12 @@ Notes: :exc:`DeprecationWarning`. In 3.15 or later we may change this into an error or change the default year to a leap year. See :gh:`70647`. +(11) + See `The Open Group Base Specifications Issue 8 IEEE Std 1003.1-2024 + documentation for strptime + `_ + for details on the :meth: `~.datetime.strptime` invocation. + .. rubric:: Footnotes .. [#] If, that is, we ignore the effects of Relativity