Skip to content

Commit 9968caa

Browse files
nineteendoblurb-it[bot]picnixzpganssle
authored
gh-41431: Add datetime.time.strptime() and datetime.date.strptime() (#120752)
* Python implementation * C implementation * Test `date.strptime` * Test `time.strptime` * 📜🤖 Added by blurb_it. * Update whatsnew * Update documentation * Add leap year note * Update 2024-06-19-19-53-42.gh-issue-41431.gnkUc5.rst * Apply suggestions from code review Co-authored-by: Bénédikt Tran <[email protected]> * Remove parentheses * Use helper function * Remove bad return * Link to github issue * Fix directive * Apply suggestions from code review Co-authored-by: Paul Ganssle <[email protected]> * Fix test cases --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Bénédikt Tran <[email protected]> Co-authored-by: Paul Ganssle <[email protected]>
1 parent b0c6cf5 commit 9968caa

11 files changed

+350
-36
lines changed

Doc/library/datetime.rst

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,39 @@ Other constructors, all class methods:
548548

549549
.. versionadded:: 3.8
550550

551+
.. classmethod:: date.strptime(date_string, format)
552+
553+
Return a :class:`.date` corresponding to *date_string*, parsed according to
554+
*format*. This is equivalent to::
555+
556+
date(*(time.strptime(date_string, format)[0:3]))
557+
558+
:exc:`ValueError` is raised if the date_string and format
559+
can't be parsed by :func:`time.strptime` or if it returns a value which isn't a
560+
time tuple. See also :ref:`strftime-strptime-behavior` and
561+
:meth:`date.fromisoformat`.
562+
563+
.. note::
564+
565+
If *format* specifies a day of month without a year a
566+
:exc:`DeprecationWarning` is emitted. This is to avoid a quadrennial
567+
leap year bug in code seeking to parse only a month and day as the
568+
default year used in absence of one in the format is not a leap year.
569+
Such *format* values may raise an error as of Python 3.15. The
570+
workaround is to always include a year in your *format*. If parsing
571+
*date_string* values that do not have a year, explicitly add a year that
572+
is a leap year before parsing:
573+
574+
.. doctest::
575+
576+
>>> from datetime import date
577+
>>> date_string = "02/29"
578+
>>> when = date.strptime(f"{date_string};1984", "%m/%d;%Y") # Avoids leap year bug.
579+
>>> when.strftime("%B %d") # doctest: +SKIP
580+
'February 29'
581+
582+
.. versionadded:: 3.14
583+
551584

552585
Class attributes:
553586

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

18291862

1830-
Other constructor:
1863+
Other constructors:
18311864

18321865
.. classmethod:: time.fromisoformat(time_string)
18331866

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

1905+
.. classmethod:: time.strptime(date_string, format)
1906+
1907+
Return a :class:`.time` corresponding to *date_string*, parsed according to
1908+
*format*.
1909+
1910+
If *format* does not contain microseconds or timezone information, this is equivalent to::
1911+
1912+
time(*(time.strptime(date_string, format)[3:6]))
1913+
1914+
:exc:`ValueError` is raised if the *date_string* and *format*
1915+
cannot be parsed by :func:`time.strptime` or if it returns a value which is not a
1916+
time tuple. See also :ref:`strftime-strptime-behavior` and
1917+
:meth:`time.fromisoformat`.
1918+
1919+
.. versionadded:: 3.14
1920+
18721921

18731922
Instance methods:
18741923

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

2370-
Conversely, the :meth:`datetime.strptime` class method creates a
2371-
:class:`.datetime` object from a string representing a date and time and a
2372-
corresponding format string.
2419+
Conversely, the :meth:`date.strptime`, :meth:`datetime.strptime` and
2420+
:meth:`time.strptime` class methods create an object from a string
2421+
representing the time and a corresponding format string.
23732422

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

2377-
+----------------+--------------------------------------------------------+------------------------------------------------------------------------------+
2378-
| | ``strftime`` | ``strptime`` |
2379-
+================+========================================================+==============================================================================+
2380-
| Usage | Convert object to a string according to a given format | Parse a string into a :class:`.datetime` object given a corresponding format |
2381-
+----------------+--------------------------------------------------------+------------------------------------------------------------------------------+
2382-
| Type of method | Instance method | Class method |
2383-
+----------------+--------------------------------------------------------+------------------------------------------------------------------------------+
2384-
| Method of | :class:`date`; :class:`.datetime`; :class:`.time` | :class:`.datetime` |
2385-
+----------------+--------------------------------------------------------+------------------------------------------------------------------------------+
2386-
| Signature | ``strftime(format)`` | ``strptime(date_string, format)`` |
2387-
+----------------+--------------------------------------------------------+------------------------------------------------------------------------------+
2426+
+----------------+--------------------------------------------------------+------------------------------------------------------------+
2427+
| | ``strftime`` | ``strptime`` |
2428+
+================+========================================================+============================================================+
2429+
| Usage | Convert object to a string according to a given format | Parse a string into an object given a corresponding format |
2430+
+----------------+--------------------------------------------------------+------------------------------------------------------------+
2431+
| Type of method | Instance method | Class method |
2432+
+----------------+--------------------------------------------------------+------------------------------------------------------------+
2433+
| Signature | ``strftime(format)`` | ``strptime(date_string, format)`` |
2434+
+----------------+--------------------------------------------------------+------------------------------------------------------------+
23882435

23892436

23902437
.. _format-codes:

Doc/whatsnew/3.14.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,12 @@ operator
285285
(Contributed by Raymond Hettinger and Nico Mexis in :gh:`115808`.)
286286

287287

288+
datetime
289+
--------
290+
291+
Add :meth:`datetime.time.strptime` and :meth:`datetime.date.strptime`.
292+
(Contributed by Wannes Boeykens in :gh:`41431`.)
293+
288294
os
289295
--
290296

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,9 @@ struct _Py_global_strings {
257257
STRUCT_FOR_ID(_shutdown)
258258
STRUCT_FOR_ID(_slotnames)
259259
STRUCT_FOR_ID(_strptime)
260-
STRUCT_FOR_ID(_strptime_datetime)
260+
STRUCT_FOR_ID(_strptime_datetime_date)
261+
STRUCT_FOR_ID(_strptime_datetime_datetime)
262+
STRUCT_FOR_ID(_strptime_datetime_time)
261263
STRUCT_FOR_ID(_type_)
262264
STRUCT_FOR_ID(_uninitialized_submodules)
263265
STRUCT_FOR_ID(_warn_unawaited_coroutine)

Include/internal/pycore_runtime_init_generated.h

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/_pydatetime.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,7 @@ class date:
951951
fromtimestamp()
952952
today()
953953
fromordinal()
954+
strptime()
954955
955956
Operators:
956957
@@ -1051,6 +1052,12 @@ def fromisocalendar(cls, year, week, day):
10511052
This is the inverse of the date.isocalendar() function"""
10521053
return cls(*_isoweek_to_gregorian(year, week, day))
10531054

1055+
@classmethod
1056+
def strptime(cls, date_string, format):
1057+
"""Parse a date string according to the given format (like time.strptime())."""
1058+
import _strptime
1059+
return _strptime._strptime_datetime_date(cls, date_string, format)
1060+
10541061
# Conversions to string
10551062

10561063
def __repr__(self):
@@ -1371,6 +1378,7 @@ class time:
13711378
Constructors:
13721379
13731380
__new__()
1381+
strptime()
13741382
13751383
Operators:
13761384
@@ -1429,6 +1437,12 @@ def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold
14291437
self._fold = fold
14301438
return self
14311439

1440+
@classmethod
1441+
def strptime(cls, date_string, format):
1442+
"""string, format -> new time parsed from a string (like time.strptime())."""
1443+
import _strptime
1444+
return _strptime._strptime_datetime_time(cls, date_string, format)
1445+
14321446
# Read-only field accessors
14331447
@property
14341448
def hour(self):
@@ -2152,7 +2166,7 @@ def __str__(self):
21522166
def strptime(cls, date_string, format):
21532167
'string, format -> new datetime parsed from a string (like time.strptime()).'
21542168
import _strptime
2155-
return _strptime._strptime_datetime(cls, date_string, format)
2169+
return _strptime._strptime_datetime_datetime(cls, date_string, format)
21562170

21572171
def utcoffset(self):
21582172
"""Return the timezone offset as timedelta positive east of UTC (negative west of

Lib/_strptime.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -567,18 +567,40 @@ def _strptime_time(data_string, format="%a %b %d %H:%M:%S %Y"):
567567
tt = _strptime(data_string, format)[0]
568568
return time.struct_time(tt[:time._STRUCT_TM_ITEMS])
569569

570-
def _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"):
571-
"""Return a class cls instance based on the input string and the
570+
def _strptime_datetime_date(cls, data_string, format="%a %b %d %Y"):
571+
"""Return a date instance based on the input string and the
572+
format string."""
573+
tt, _, _ = _strptime(data_string, format)
574+
args = tt[:3]
575+
return cls(*args)
576+
577+
def _parse_tz(tzname, gmtoff, gmtoff_fraction):
578+
tzdelta = datetime_timedelta(seconds=gmtoff, microseconds=gmtoff_fraction)
579+
if tzname:
580+
return datetime_timezone(tzdelta, tzname)
581+
else:
582+
return datetime_timezone(tzdelta)
583+
584+
def _strptime_datetime_time(cls, data_string, format="%H:%M:%S"):
585+
"""Return a time instance based on the input string and the
572586
format string."""
573587
tt, fraction, gmtoff_fraction = _strptime(data_string, format)
574588
tzname, gmtoff = tt[-2:]
575-
args = tt[:6] + (fraction,)
576-
if gmtoff is not None:
577-
tzdelta = datetime_timedelta(seconds=gmtoff, microseconds=gmtoff_fraction)
578-
if tzname:
579-
tz = datetime_timezone(tzdelta, tzname)
580-
else:
581-
tz = datetime_timezone(tzdelta)
582-
args += (tz,)
589+
args = tt[3:6] + (fraction,)
590+
if gmtoff is None:
591+
return cls(*args)
592+
else:
593+
tz = _parse_tz(tzname, gmtoff, gmtoff_fraction)
594+
return cls(*args, tz)
583595

584-
return cls(*args)
596+
def _strptime_datetime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"):
597+
"""Return a datetime instance based on the input string and the
598+
format string."""
599+
tt, fraction, gmtoff_fraction = _strptime(data_string, format)
600+
tzname, gmtoff = tt[-2:]
601+
args = tt[:6] + (fraction,)
602+
if gmtoff is None:
603+
return cls(*args)
604+
else:
605+
tz = _parse_tz(tzname, gmtoff, gmtoff_fraction)
606+
return cls(*args, tz)

0 commit comments

Comments
 (0)