Skip to content
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

gh-41431: Add datetime.time.strptime() and datetime.date.strptime() #120752

Merged
merged 19 commits into from
Sep 25, 2024
Merged
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
77 changes: 62 additions & 15 deletions Doc/library/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,39 @@ Other constructors, all class methods:

.. versionadded:: 3.8

.. classmethod:: date.strptime(date_string, format)

Return a :class:`.date` corresponding to *date_string*, parsed according to
*format*. This is equivalent to::

date(*(time.strptime(date_string, format)[0:3]))

:exc:`ValueError` is raised if the date_string and format
can't be parsed by :func:`time.strptime` or if it returns a value which isn't a
time tuple. See also :ref:`strftime-strptime-behavior` and
:meth:`date.fromisoformat`.

.. note::

If *format* specifies a day of month without a year a
:exc:`DeprecationWarning` is 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
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:

.. doctest::

>>> from datetime import date
>>> date_string = "02/29"
>>> when = date.strptime(f"{date_string};1984", "%m/%d;%Y") # Avoids leap year bug.
>>> when.strftime("%B %d") # doctest: +SKIP
'February 29'

.. versionadded:: 3.14


Class attributes:

Expand Down Expand Up @@ -1827,7 +1860,7 @@ In Boolean contexts, a :class:`.time` object is always considered to be true.
details.


Other constructor:
Other constructors:

.. classmethod:: time.fromisoformat(time_string)

Expand Down Expand Up @@ -1869,6 +1902,22 @@ Other constructor:
Previously, this method only supported formats that could be emitted by
:meth:`time.isoformat`.

.. classmethod:: time.strptime(date_string, format)

Return a :class:`.time` corresponding to *date_string*, parsed according to
*format*.

If *format* does not contain microseconds or timezone information, this is equivalent to::

time(*(time.strptime(date_string, format)[3:6]))
nineteendo marked this conversation as resolved.
Show resolved Hide resolved

:exc:`ValueError` is raised if the *date_string* and *format*
cannot be parsed by :func:`time.strptime` or if it returns a value which is not a
time tuple. See also :ref:`strftime-strptime-behavior` and
nineteendo marked this conversation as resolved.
Show resolved Hide resolved
:meth:`time.fromisoformat`.

.. versionadded:: 3.14


Instance methods:

Expand Down Expand Up @@ -2367,24 +2416,22 @@ Class attributes:
``strftime(format)`` method, to create a string representing the time under the
control of an explicit format string.

Conversely, the :meth:`datetime.strptime` class method creates a
:class:`.datetime` object from a string representing a date and time and a
corresponding format string.
Conversely, the :meth:`date.strptime`, :meth:`datetime.strptime` and
:meth:`time.strptime` class methods create an object from a string
representing the time and a corresponding format string.

The table below provides a high-level comparison of :meth:`~.datetime.strftime`
versus :meth:`~.datetime.strptime`:

+----------------+--------------------------------------------------------+------------------------------------------------------------------------------+
| | ``strftime`` | ``strptime`` |
+================+========================================================+==============================================================================+
| Usage | Convert object to a string according to a given format | Parse a string into a :class:`.datetime` object given a corresponding format |
+----------------+--------------------------------------------------------+------------------------------------------------------------------------------+
| Type of method | Instance method | Class method |
+----------------+--------------------------------------------------------+------------------------------------------------------------------------------+
| Method of | :class:`date`; :class:`.datetime`; :class:`.time` | :class:`.datetime` |
+----------------+--------------------------------------------------------+------------------------------------------------------------------------------+
| Signature | ``strftime(format)`` | ``strptime(date_string, format)`` |
+----------------+--------------------------------------------------------+------------------------------------------------------------------------------+
+----------------+--------------------------------------------------------+------------------------------------------------------------+
| | ``strftime`` | ``strptime`` |
+================+========================================================+============================================================+
| Usage | Convert object to a string according to a given format | Parse a string into an object given a corresponding format |
+----------------+--------------------------------------------------------+------------------------------------------------------------+
| Type of method | Instance method | Class method |
+----------------+--------------------------------------------------------+------------------------------------------------------------+
| Signature | ``strftime(format)`` | ``strptime(date_string, format)`` |
+----------------+--------------------------------------------------------+------------------------------------------------------------+


.. _format-codes:
Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@ operator
(Contributed by Raymond Hettinger and Nico Mexis in :gh:`115808`.)


datetime
--------

Add :meth:`datetime.time.strptime` and :meth:`datetime.date.strptime`.
(Contributed by Wannes Boeykens in :gh:`41431`.)

os
--

Expand Down
4 changes: 3 additions & 1 deletion Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,9 @@ struct _Py_global_strings {
STRUCT_FOR_ID(_shutdown)
STRUCT_FOR_ID(_slotnames)
STRUCT_FOR_ID(_strptime)
STRUCT_FOR_ID(_strptime_datetime)
STRUCT_FOR_ID(_strptime_datetime_date)
STRUCT_FOR_ID(_strptime_datetime_datetime)
STRUCT_FOR_ID(_strptime_datetime_time)
STRUCT_FOR_ID(_type_)
STRUCT_FOR_ID(_uninitialized_submodules)
STRUCT_FOR_ID(_warn_unawaited_coroutine)
Expand Down
4 changes: 3 additions & 1 deletion Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion Lib/_pydatetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,7 @@ class date:
fromtimestamp()
today()
fromordinal()
strptime()

Operators:

Expand Down Expand Up @@ -1040,6 +1041,12 @@ def fromisocalendar(cls, year, week, day):
This is the inverse of the date.isocalendar() function"""
return cls(*_isoweek_to_gregorian(year, week, day))

@classmethod
def strptime(cls, date_string, format):
"""Parse a date string according to the given format (like time.strptime())."""
import _strptime
return _strptime._strptime_datetime_date(cls, date_string, format)

# Conversions to string

def __repr__(self):
Expand Down Expand Up @@ -1360,6 +1367,7 @@ class time:
Constructors:

__new__()
strptime()

Operators:

Expand Down Expand Up @@ -1418,6 +1426,12 @@ def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold
self._fold = fold
return self

@classmethod
def strptime(cls, date_string, format):
"""string, format -> new time parsed from a string (like time.strptime())."""
import _strptime
return _strptime._strptime_datetime_time(cls, date_string, format)

# Read-only field accessors
@property
def hour(self):
Expand Down Expand Up @@ -2124,7 +2138,7 @@ def __str__(self):
def strptime(cls, date_string, format):
'string, format -> new datetime parsed from a string (like time.strptime()).'
import _strptime
return _strptime._strptime_datetime(cls, date_string, format)
return _strptime._strptime_datetime_datetime(cls, date_string, format)

def utcoffset(self):
"""Return the timezone offset as timedelta positive east of UTC (negative west of
Expand Down
44 changes: 33 additions & 11 deletions Lib/_strptime.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,18 +567,40 @@ def _strptime_time(data_string, format="%a %b %d %H:%M:%S %Y"):
tt = _strptime(data_string, format)[0]
return time.struct_time(tt[:time._STRUCT_TM_ITEMS])

def _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"):
"""Return a class cls instance based on the input string and the
def _strptime_datetime_date(cls, data_string, format="%a %b %d %Y"):
"""Return a date instance based on the input string and the
format string."""
tt, _, _ = _strptime(data_string, format)
pganssle marked this conversation as resolved.
Show resolved Hide resolved
args = tt[:3]
return cls(*args)

def _parse_tz(tzname, gmtoff, gmtoff_fraction):
tzdelta = datetime_timedelta(seconds=gmtoff, microseconds=gmtoff_fraction)
if tzname:
return datetime_timezone(tzdelta, tzname)
else:
return datetime_timezone(tzdelta)

def _strptime_datetime_time(cls, data_string, format="%H:%M:%S"):
"""Return a time instance based on the input string and the
format string."""
tt, fraction, gmtoff_fraction = _strptime(data_string, format)
tzname, gmtoff = tt[-2:]
args = tt[:6] + (fraction,)
if gmtoff is not None:
tzdelta = datetime_timedelta(seconds=gmtoff, microseconds=gmtoff_fraction)
if tzname:
tz = datetime_timezone(tzdelta, tzname)
else:
tz = datetime_timezone(tzdelta)
args += (tz,)
args = tt[3:6] + (fraction,)
if gmtoff is None:
return cls(*args)
else:
tz = _parse_tz(tzname, gmtoff, gmtoff_fraction)
return cls(*args, tz)

return cls(*args)
def _strptime_datetime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"):
"""Return a datetime instance based on the input string and the
format string."""
tt, fraction, gmtoff_fraction = _strptime(data_string, format)
tzname, gmtoff = tt[-2:]
args = tt[:6] + (fraction,)
if gmtoff is None:
return cls(*args)
else:
tz = _parse_tz(tzname, gmtoff, gmtoff_fraction)
return cls(*args, tz)
Loading
Loading