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

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 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
58 changes: 43 additions & 15 deletions Doc/library/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,20 @@ 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`.

.. versionadded:: 3.14


Class attributes:

Expand Down Expand Up @@ -1821,7 +1835,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 @@ -1863,6 +1877,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
nineteendo marked this conversation as resolved.
Show resolved Hide resolved
can't be parsed by :func:`time.strptime` or if it returns a value which isn't a
nineteendo marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -2361,24 +2391,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
nineteendo marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -92,6 +92,12 @@ ast
Added :func:`ast.compare` for comparing two ASTs.
(Contributed by Batuhan Taskaya and Jeremy Hylton in :issue:`15987`.)

datetime
--------

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

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 @@ -267,7 +267,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(_swappedbytes_)
STRUCT_FOR_ID(_type_)
STRUCT_FOR_ID(_uninitialized_submodules)
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.

8 changes: 7 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 @@ -908,6 +908,7 @@ class date:
fromtimestamp()
today()
fromordinal()
strptime()

Operators:

Expand Down Expand Up @@ -1008,6 +1009,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):
'string, format -> new date parsed from a string (like time.strptime()).'
nineteendo marked this conversation as resolved.
Show resolved Hide resolved
import _strptime
return _strptime._strptime_datetime_date(cls, date_string, format)

# Conversions to string

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

__new__()
strptime()

Operators:

Expand Down Expand Up @@ -1386,6 +1394,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()).'
nineteendo marked this conversation as resolved.
Show resolved Hide resolved
import _strptime
return _strptime._strptime_datetime_time(cls, date_string, format)

# Read-only field accessors
@property
def hour(self):
Expand Down Expand Up @@ -2092,7 +2106,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
27 changes: 25 additions & 2 deletions Lib/_strptime.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,8 +567,31 @@ 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)
Copy link
Contributor Author

@nineteendo nineteendo Jun 19, 2024

Choose a reason for hiding this comment

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

Should we check the format here? The previous pull request used a C function for that, but that obviously wouldn't work if the C implementation isn't available. I think it would be a bit weird if strftime() worked, but strptime() didn't.

Copy link
Member

Choose a reason for hiding this comment

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

I think we do need to check that this doesn't have any non-date components in it, no? We might need to push that into _strptime, though. We could check that it returns all 0s, but then things like 2024-01-01T00:00 will fail to raise an exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Shouldn't strptime() be able to parse the output produced by strftime()? I would prefer if both raised an error for the format or neither, but checking for the fallback date or time seems fine to me.

>>> datetime.date(9999, 12, 31).strftime("%Y-%m-%dT%H:%M:%S")
'9999-12-31T00:00:00'
>>> datetime.time(23, 59, 59).strftime("%Y-%m-%dT%H:%M:%S")
'1900-01-01T23:59:59'

args = tt[:3]
return cls(*args)

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[3: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,)
nineteendo marked this conversation as resolved.
Show resolved Hide resolved

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:]
Expand Down
Loading
Loading