Skip to content

gh-83461: Don't allow datetime parsing to accept non-ASCII digits #131008

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add docs and tests for time.strptime
  • Loading branch information
StanFromIreland committed Mar 9, 2025
commit a0b0f07b2a2e20a1252da4513c52a5792d94e55d
5 changes: 3 additions & 2 deletions Doc/library/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2432,7 +2432,8 @@ Class attributes:

:class:`date`, :class:`.datetime`, and :class:`.time` objects all support a
``strftime(format)`` method, to create a string representing the time under the
control of an explicit format string. The methods only accept ASCII digits.
control of an explicit format string. A :exc:`ValueError` will be raised if digits
are not ASCII.

Conversely, the :meth:`date.strptime`, :meth:`datetime.strptime` and
:meth:`time.strptime` class methods create an object from a string
Expand Down Expand Up @@ -2612,7 +2613,7 @@ differences between platforms in handling of unsupported format specifiers.
``%:z`` was added.

.. versionchanged:: next
Digits must be ASCII
Non-ASCII digits are now rejected by ``strptime`` for numerical directives.

Technical Detail
^^^^^^^^^^^^^^^^
Expand Down
10 changes: 7 additions & 3 deletions Doc/library/time.rst
Original file line number Diff line number Diff line change
Expand Up @@ -594,9 +594,10 @@ Functions
:func:`strftime`; it defaults to ``"%a %b %d %H:%M:%S %Y"`` which matches the
formatting returned by :func:`ctime`. If *string* cannot be parsed according
to *format*, or if it has excess data after parsing, :exc:`ValueError` is
raised. The default values used to fill in any missing data when more
accurate values cannot be inferred are ``(1900, 1, 1, 0, 0, 0, 0, 1, -1)``.
Both *string* and *format* must be strings.
raised. :exc:`ValueError` is raised if digits are not ASCII. The default
values used to fill in any missing data when more accurate values cannot be
inferred are ``(1900, 1, 1, 0, 0, 0, 0, 1, -1)``. Both *string* and *format*
must be strings.

For example:

Expand All @@ -616,6 +617,9 @@ Functions
and thus does not necessarily support all directives available that are not
documented as supported.

.. versionchanged:: next
Non-ASCII digits are now rejected by ``strptime``.


.. class:: struct_time

Expand Down
17 changes: 13 additions & 4 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -504,10 +504,10 @@ datetime
* Add :meth:`datetime.time.strptime` and :meth:`datetime.date.strptime`.
(Contributed by Wannes Boeykens in :gh:`41431`.)

* The ``strptime(format)`` methods: :meth:`datetime.date.strptime`,
:meth:`datetime.datetime.strptime` and :meth:`datetime.time.strptime`
only accept ASCII digits.
(Contributed by Stan Ulbrych in :gh:`131008`
* The :meth:`datetime.date.strptime`, :meth:`datetime.datetime.strptime` and
:meth:`datetime.time.strptime` methods now only accept ASCII digits, will and
raise a :exc:`ValueError` if non-ASCII digits are specified.
(Contributed by Stan Ulbrych in :gh:`131008`.)

decimal
-------
Expand Down Expand Up @@ -883,6 +883,15 @@ sys.monitoring
* Two new events are added: :monitoring-event:`BRANCH_LEFT` and
:monitoring-event:`BRANCH_RIGHT`. The ``BRANCH`` event is deprecated.


time
----

* The :meth:`time.strptime`, now only accept ASCII digits, and will raise a
:exc:`ValueError` if non-ASCII digits are specified.
(Contributed by Stan Ulbrych in :gh:`131008`.)


threading
---------

Expand Down
8 changes: 4 additions & 4 deletions Lib/_strptime.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import calendar
from re import compile as re_compile
from re import sub as re_sub
from re import ASCII, IGNORECASE
from re import IGNORECASE
from re import escape as re_escape
from datetime import (date as datetime_date,
timedelta as datetime_timedelta,
Expand Down Expand Up @@ -290,7 +290,7 @@ def __init__(self, locale_time=None):
'f': r"(?P<f>[0-9]{1,6})",
'H': r"(?P<H>2[0-3]|[0-1][0-9]|[0-9])",
'I': r"(?P<I>1[0-2]|0[1-9]|[1-9]| [1-9])",
'G': r"(?P<G>[0-9][0-9][0-9][0-9])",
'G': r"(?P<G>[0-9]{4})",
'j': r"(?P<j>36[0-6]|3[0-5][0-9]|[1-2][0-9][0-9]|0[1-9][0-9]|00[1-9]|[1-9][0-9]|0[1-9]|[1-9])",
'm': r"(?P<m>1[0-2]|0[1-9]|[1-9])",
'M': r"(?P<M>[0-5][0-9]|[0-9])",
Expand All @@ -300,8 +300,8 @@ def __init__(self, locale_time=None):
'u': r"(?P<u>[1-7])",
'V': r"(?P<V>5[0-3]|0[1-9]|[1-4][0-9]|[0-9])",
# W is set below by using 'U'
'y': r"(?P<y>[0-9][0-9])",
'Y': r"(?P<Y>[0-9][0-9][0-9][0-9])",
'y': r"(?P<y>[0-9]{2})",
'Y': r"(?P<Y>[0-9]{4})",
'z': r"(?P<z>[+-][0-9][0-9]:?[0-5][0-9](:?[0-5][0-9](\.[0-9]{1,6})?)?|(?-i:Z))",
'A': self.__seqToRE(self.locale_time.f_weekday, 'A'),
'a': self.__seqToRE(self.locale_time.a_weekday, 'a'),
Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ def test_strptime_leap_year(self):
r'.*day of month without a year.*'):
time.strptime('02-07 18:28', '%m-%d %H:%M')

def test_strptime_non_ascii(self):
with self.assertRaises(ValueError):
time.strptime('٢025', '%Y')

def test_asctime(self):
time.asctime(time.gmtime(self.t))

Expand Down
Loading