Skip to content

gh-70647: Better promote how to safely parse yearless dates in datetime. #116179

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 7 commits into
base: main
Choose a base branch
from
Open
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
27 changes: 23 additions & 4 deletions Doc/library/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2640,7 +2640,28 @@ Broadly speaking, ``d.strftime(fmt)`` acts like the :mod:`time` module's

For the :meth:`.datetime.strptime` class method, the default value is
``1900-01-01T00:00:00.000``: any components not specified in the format string
will be pulled from the default value. [#]_
will be pulled from the default value.

.. note::
When used to parse partial dates lacking a year, :meth:`~.datetime.strptime`
will raise when encountering February 29 because its default year of 1900 is
*not* a leap year. Always add a default leap year to partial date strings
before parsing.

.. doctest::

>>> from datetime import datetime
>>> import warnings
>>> value = "2/29"
>>> with warnings.catch_warnings():
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs folks: Do we have a way to do this ignoring of the DeprecationWarning in the doctest code without polluting the example code with something I don't want anyone to ever write in their code?

doctest seems to turn the warning into an error which makes it show up and require matching and prevents the actual interesting exception from being raised. (see the earlier failing builds on this PR)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a way, I don't know if it's the best way:

.. testsetup::

   # doctest seems to turn the warning into an error which makes it
   # show up and require matching and prevents the actual interesting
   # exception from being raised.
   # Manually apply the `catch_warnings` context manager

   import warnings
   catch_warnings = warnings.catch_warnings()
   catch_warnings.__enter__()

.. testcleanup::

   catch_warnings.__exit__()

.. doctest::

    >>> from datetime import datetime
    >>> import warnings
    >>> value = "2/29"
    >>> datetime.strptime(value, "%m/%d")
    Traceback (most recent call last):
    ...
    ValueError: day 29 must be in range 1..28 for month 2 in year 1900
    >>> datetime.strptime(f"1904 {value}", "%Y %m/%d")
    datetime.datetime(1904, 2, 29, 0, 0)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the error is something to do with the C implementation. Depreciation errors don’t show up in the repl unless they are from a C extension resulting in them being forcibly displayed.

... warnings.simplefilter("ignore")
... datetime.strptime(value, "%m/%d")
...
Traceback (most recent call last):
...
ValueError: day 29 must be in range 1..28 for month 2 in year 1900
>>> datetime.strptime(f"1904 {value}", "%Y %m/%d")
datetime.datetime(1904, 2, 29, 0, 0)

Using ``datetime.strptime(date_string, format)`` is equivalent to::

Expand Down Expand Up @@ -2771,7 +2792,7 @@ Notes:
include a year in the format. If the value you need to parse lacks a year,
append an explicit dummy leap year. Otherwise your code will raise an
exception when it encounters leap day because the default year used by the
parser is not a leap year. Users run into this bug every four years...
parser (1900) is not a leap year. Users run into that bug every leap year.

.. doctest::

Expand All @@ -2798,5 +2819,3 @@ Notes:
.. [#] See R. H. van Gent's `guide to the mathematics of the ISO 8601 calendar
<https://web.archive.org/web/20220531051136/https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm>`_
for a good explanation.

.. [#] Passing ``datetime.strptime('Feb 29', '%b %d')`` will fail since 1900 is not a leap year.
Loading