Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions Doc/deprecations/pending-removal-in-3.18.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Pending removal in Python 3.18
------------------------------

* :mod:`datetime`:

* :meth:`~datetime.datetime.strptime` calls using a format string containing
a day of month without a year. This has been deprecated since Python 3.13.

* :mod:`decimal`:

* The non-standard and undocumented :class:`~decimal.Decimal` format
Expand Down
14 changes: 7 additions & 7 deletions Doc/library/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1168,7 +1168,7 @@ Other constructors, all class methods:
:exc:`DeprecationWarning` is now emitted. This is to avoid a quadrennial
leap year bug in code seeking to parse only a month and day as the
default year used in absence of one in the format is not a leap year.
Such *format* values may raise an error as of Python 3.15. The
Such *format* values may raise an error as of Python 3.18. The
workaround is to always include a year in your *format*. If parsing
*date_string* values that do not have a year, explicitly add a year that
is a leap year before parsing:
Expand Down Expand Up @@ -2531,13 +2531,13 @@ requires, and these work on all supported platforms.
| | truncated to an integer as a | | |
| | zero-padded decimal number. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%d`` | Day of the month as a | 01, 02, ..., 31 | \(9) |
| | zero-padded decimal number. | | |
| ``%d`` | Day of the month as a | 01, 02, ..., 31 | \(9), |
| | zero-padded decimal number. | | \(10) |
+-----------+--------------------------------+------------------------+-------+
| ``%D`` | Equivalent to ``%m/%d/%y``. | 11/10/2025 | \(9), |
| | | | \(0) |
+-----------+--------------------------------+------------------------+-------+
| ``%e`` | The day of the month as a | ␣1, ␣2, ..., 31 | |
| ``%e`` | The day of the month as a | ␣1, ␣2, ..., 31 | \(10) |
| | space-padded decimal number. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%F`` | Equivalent to ``%Y-%m-%d``, | 2025-10-11, | \(0) |
Expand Down Expand Up @@ -2868,11 +2868,11 @@ Notes:
>>> datetime.strptime(f"{month_day};1984", "%m/%d;%Y") # No leap year bug.
datetime.datetime(1984, 2, 29, 0, 0)

.. deprecated-removed:: 3.13 3.15
.. deprecated-removed:: 3.13 3.18
:meth:`~.datetime.strptime` calls using a format string containing
a day of month without a year now emit a
: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`.
:exc:`DeprecationWarning`. In 3.18 we will change this into
an error or change the default year to a leap year.

.. rubric:: Footnotes

Expand Down
9 changes: 4 additions & 5 deletions Lib/_strptime.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ def repl(m):
case 'Y' | 'y' | 'G':
nonlocal year_in_format
year_in_format = True
case 'd':
case 'd' | 'e':
nonlocal day_of_month_in_format
day_of_month_in_format = True
return self[directive]
Expand All @@ -475,10 +475,9 @@ def repl(m):
import warnings
warnings.warn("""\
Parsing dates involving a day of month without a year specified is ambiguous
and fails to parse leap day. The default behavior will change in Python 3.15
to either always raise an exception or to use a different default year (TBD).
To avoid trouble, add a specific year to the input & format.
See https://github.com/python/cpython/issues/70647.""",
and fails to parse leap day. The default behavior will change in Python 3.18
to either always raise an exception or to use a different default year.
To avoid trouble, add a specific year to the input and format.""",
DeprecationWarning,
skip_file_prefixes=(os.path.dirname(__file__),))
return format
Expand Down
8 changes: 8 additions & 0 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -1208,9 +1208,12 @@ def test_strptime_leap_year(self):
with self.assertRaises(ValueError):
# The existing behavior that GH-70647 seeks to change.
date.strptime('02-29', '%m-%d')
with self.assertRaises(ValueError):
date.strptime('02-29', '%m-%e')
with self._assertNotWarns(DeprecationWarning):
date.strptime('20-03-14', '%y-%m-%d')
date.strptime('02-29,2024', '%m-%d,%Y')
date.strptime('02-29,2024', '%m-%e,%Y')

class SubclassDate(date):
sub_var = 1
Expand Down Expand Up @@ -3096,10 +3099,15 @@ def test_strptime_leap_year(self):
with self.assertWarnsRegex(DeprecationWarning,
r'.*day of month without a year.*'):
self.theclass.strptime('03-14.159265', '%m-%d.%f')
with self.assertWarnsRegex(DeprecationWarning,
r'.*day of month without a year.*'):
self.theclass.strptime('03-14.159265', '%m-%e.%f')
with self._assertNotWarns(DeprecationWarning):
self.theclass.strptime('20-03-14.159265', '%y-%m-%d.%f')
with self._assertNotWarns(DeprecationWarning):
self.theclass.strptime('02-29,2024', '%m-%d,%Y')
with self._assertNotWarns(DeprecationWarning):
self.theclass.strptime('02-29,2024', '%m-%e,%Y')

def test_strptime_z_empty(self):
for directive in ('z', ':z'):
Expand Down
7 changes: 5 additions & 2 deletions Lib/test/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,11 +358,11 @@ def test_strptime(self):
# Should be able to go round-trip from strftime to strptime without
# raising an exception.
tt = time.gmtime(self.t)
for directive in ('a', 'A', 'b', 'B', 'c', 'd', 'H', 'I',
for directive in ('a', 'A', 'b', 'B', 'c', 'd', 'e', 'H', 'I',
'j', 'm', 'M', 'p', 'S',
'U', 'w', 'W', 'x', 'X', 'y', 'Y', 'Z', '%'):
format = '%' + directive
if directive == 'd':
if directive in ('d', 'e'):
format += ',%Y' # Avoid GH-70647.
strf_output = time.strftime(format, tt)
try:
Expand Down Expand Up @@ -391,6 +391,9 @@ def test_strptime_leap_year(self):
with self.assertWarnsRegex(DeprecationWarning,
r'.*day of month without a year.*'):
time.strptime('02-07 18:28', '%m-%d %H:%M')
with self.assertWarnsRegex(DeprecationWarning,
r'.*day of month without a year.*'):
time.strptime('02- 7 18:28', '%m-%e %H:%M')

def test_asctime(self):
time.asctime(time.gmtime(self.t))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Include the ``%e`` format code in the :meth:`~datetime.datetime.strptime`
deprecation for day-of-month format codes used without a year.
Loading