mirror of https://github.com/python/cpython
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 <10796600+picnixz@users.noreply.github.com> * Remove parentheses * Use helper function * Remove bad return * Link to github issue * Fix directive * Apply suggestions from code review Co-authored-by: Paul Ganssle <1377457+pganssle@users.noreply.github.com> * Fix test cases --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Paul Ganssle <1377457+pganssle@users.noreply.github.com>
This commit is contained in:
parent
b0c6cf5f17
commit
9968caa0cc
|
@ -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:
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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]))
|
||||
|
||||
: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
|
||||
:meth:`time.fromisoformat`.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
|
||||
Instance methods:
|
||||
|
||||
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
--
|
||||
|
||||
|
|
|
@ -768,7 +768,9 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
|||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_shutdown));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_slotnames));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_strptime));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_strptime_datetime));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_strptime_datetime_date));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_strptime_datetime_datetime));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_strptime_datetime_time));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_type_));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_uninitialized_submodules));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_warn_unawaited_coroutine));
|
||||
|
|
|
@ -257,7 +257,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)
|
||||
|
|
|
@ -766,7 +766,9 @@ extern "C" {
|
|||
INIT_ID(_shutdown), \
|
||||
INIT_ID(_slotnames), \
|
||||
INIT_ID(_strptime), \
|
||||
INIT_ID(_strptime_datetime), \
|
||||
INIT_ID(_strptime_datetime_date), \
|
||||
INIT_ID(_strptime_datetime_datetime), \
|
||||
INIT_ID(_strptime_datetime_time), \
|
||||
INIT_ID(_type_), \
|
||||
INIT_ID(_uninitialized_submodules), \
|
||||
INIT_ID(_warn_unawaited_coroutine), \
|
||||
|
|
|
@ -828,7 +828,15 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
|||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||
string = &_Py_ID(_strptime_datetime);
|
||||
string = &_Py_ID(_strptime_datetime_date);
|
||||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||
string = &_Py_ID(_strptime_datetime_datetime);
|
||||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||
string = &_Py_ID(_strptime_datetime_time);
|
||||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||
|
|
|
@ -951,6 +951,7 @@ class date:
|
|||
fromtimestamp()
|
||||
today()
|
||||
fromordinal()
|
||||
strptime()
|
||||
|
||||
Operators:
|
||||
|
||||
|
@ -1051,6 +1052,12 @@ class date:
|
|||
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):
|
||||
|
@ -1371,6 +1378,7 @@ class time:
|
|||
Constructors:
|
||||
|
||||
__new__()
|
||||
strptime()
|
||||
|
||||
Operators:
|
||||
|
||||
|
@ -1429,6 +1437,12 @@ class time:
|
|||
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):
|
||||
|
@ -2152,7 +2166,7 @@ class datetime(date):
|
|||
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
|
||||
|
|
|
@ -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)
|
||||
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[3:6] + (fraction,)
|
||||
if gmtoff is None:
|
||||
return cls(*args)
|
||||
else:
|
||||
tz = _parse_tz(tzname, gmtoff, gmtoff_fraction)
|
||||
return cls(*args, tz)
|
||||
|
||||
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 not None:
|
||||
tzdelta = datetime_timedelta(seconds=gmtoff, microseconds=gmtoff_fraction)
|
||||
if tzname:
|
||||
tz = datetime_timezone(tzdelta, tzname)
|
||||
else:
|
||||
tz = datetime_timezone(tzdelta)
|
||||
args += (tz,)
|
||||
|
||||
return cls(*args)
|
||||
if gmtoff is None:
|
||||
return cls(*args)
|
||||
else:
|
||||
tz = _parse_tz(tzname, gmtoff, gmtoff_fraction)
|
||||
return cls(*args, tz)
|
||||
|
|
|
@ -1106,6 +1106,85 @@ class TestDateOnly(unittest.TestCase):
|
|||
dt2 = dt - delta
|
||||
self.assertEqual(dt2, dt - days)
|
||||
|
||||
def test_strptime(self):
|
||||
inputs = [
|
||||
# Basic valid cases
|
||||
(date(1998, 2, 3), '1998-02-03', '%Y-%m-%d'),
|
||||
(date(2004, 12, 2), '2004-12-02', '%Y-%m-%d'),
|
||||
|
||||
# Edge cases: Leap year
|
||||
(date(2020, 2, 29), '2020-02-29', '%Y-%m-%d'), # Valid leap year date
|
||||
|
||||
# bpo-34482: Handle surrogate pairs
|
||||
(date(2004, 12, 2), '2004-12\ud80002', '%Y-%m\ud800%d'),
|
||||
(date(2004, 12, 2), '2004\ud80012-02', '%Y\ud800%m-%d'),
|
||||
|
||||
# Month/day variations
|
||||
(date(2004, 2, 1), '2004-02', '%Y-%m'), # No day provided
|
||||
(date(2004, 2, 1), '02-2004', '%m-%Y'), # Month and year swapped
|
||||
|
||||
# Different day-month-year formats
|
||||
(date(2004, 12, 2), '02/12/2004', '%d/%m/%Y'), # Day/Month/Year
|
||||
(date(2004, 12, 2), '12/02/2004', '%m/%d/%Y'), # Month/Day/Year
|
||||
|
||||
# Different separators
|
||||
(date(2023, 9, 24), '24.09.2023', '%d.%m.%Y'), # Dots as separators
|
||||
(date(2023, 9, 24), '24-09-2023', '%d-%m-%Y'), # Dashes
|
||||
(date(2023, 9, 24), '2023/09/24', '%Y/%m/%d'), # Slashes
|
||||
|
||||
# Handling years with fewer digits
|
||||
(date(127, 2, 3), '0127-02-03', '%Y-%m-%d'),
|
||||
(date(99, 2, 3), '0099-02-03', '%Y-%m-%d'),
|
||||
(date(5, 2, 3), '0005-02-03', '%Y-%m-%d'),
|
||||
|
||||
# Variations on ISO 8601 format
|
||||
(date(2023, 9, 25), '2023-W39-1', '%G-W%V-%u'), # ISO week date (Week 39, Monday)
|
||||
(date(2023, 9, 25), '2023-268', '%Y-%j'), # Year and day of the year (Julian)
|
||||
]
|
||||
for expected, string, format in inputs:
|
||||
with self.subTest(string=string, format=format):
|
||||
got = date.strptime(string, format)
|
||||
self.assertEqual(expected, got)
|
||||
self.assertIs(type(got), date)
|
||||
|
||||
def test_strptime_single_digit(self):
|
||||
# bpo-34903: Check that single digit dates are allowed.
|
||||
strptime = date.strptime
|
||||
with self.assertRaises(ValueError):
|
||||
# %y does require two digits.
|
||||
newdate = strptime('01/02/3', '%d/%m/%y')
|
||||
|
||||
d1 = date(2003, 2, 1)
|
||||
d2 = date(2003, 1, 2)
|
||||
d3 = date(2003, 1, 25)
|
||||
inputs = [
|
||||
('%d', '1/02/03', '%d/%m/%y', d1),
|
||||
('%m', '01/2/03', '%d/%m/%y', d1),
|
||||
('%j', '2/03', '%j/%y', d2),
|
||||
('%w', '6/04/03', '%w/%U/%y', d1),
|
||||
# %u requires a single digit.
|
||||
('%W', '6/4/2003', '%u/%W/%Y', d1),
|
||||
('%V', '6/4/2003', '%u/%V/%G', d3),
|
||||
]
|
||||
for reason, string, format, target in inputs:
|
||||
reason = 'test single digit ' + reason
|
||||
with self.subTest(reason=reason,
|
||||
string=string,
|
||||
format=format,
|
||||
target=target):
|
||||
newdate = strptime(string, format)
|
||||
self.assertEqual(newdate, target, msg=reason)
|
||||
|
||||
@warnings_helper.ignore_warnings(category=DeprecationWarning)
|
||||
def test_strptime_leap_year(self):
|
||||
# GH-70647: warns if parsing a format with a day and no year.
|
||||
with self.assertRaises(ValueError):
|
||||
# The existing behavior that GH-70647 seeks to change.
|
||||
date.strptime('02-29', '%m-%d')
|
||||
with self._assertNotWarns(DeprecationWarning):
|
||||
date.strptime('20-03-14', '%y-%m-%d')
|
||||
date.strptime('02-29,2024', '%m-%d,%Y')
|
||||
|
||||
class SubclassDate(date):
|
||||
sub_var = 1
|
||||
|
||||
|
@ -2732,7 +2811,8 @@ class TestDateTime(TestDate):
|
|||
def test_strptime(self):
|
||||
string = '2004-12-01 13:02:47.197'
|
||||
format = '%Y-%m-%d %H:%M:%S.%f'
|
||||
expected = _strptime._strptime_datetime(self.theclass, string, format)
|
||||
expected = _strptime._strptime_datetime_datetime(self.theclass, string,
|
||||
format)
|
||||
got = self.theclass.strptime(string, format)
|
||||
self.assertEqual(expected, got)
|
||||
self.assertIs(type(expected), self.theclass)
|
||||
|
@ -2746,8 +2826,8 @@ class TestDateTime(TestDate):
|
|||
]
|
||||
for string, format in inputs:
|
||||
with self.subTest(string=string, format=format):
|
||||
expected = _strptime._strptime_datetime(self.theclass, string,
|
||||
format)
|
||||
expected = _strptime._strptime_datetime_datetime(self.theclass,
|
||||
string, format)
|
||||
got = self.theclass.strptime(string, format)
|
||||
self.assertEqual(expected, got)
|
||||
|
||||
|
@ -3749,6 +3829,78 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase):
|
|||
derived = loads(data, encoding='latin1')
|
||||
self.assertEqual(derived, expected)
|
||||
|
||||
def test_strptime(self):
|
||||
# bpo-34482: Check that surrogates are handled properly.
|
||||
inputs = [
|
||||
(self.theclass(13, 2, 47, 197000), '13:02:47.197', '%H:%M:%S.%f'),
|
||||
(self.theclass(13, 2, 47, 197000), '13:02\ud80047.197', '%H:%M\ud800%S.%f'),
|
||||
(self.theclass(13, 2, 47, 197000), '13\ud80002:47.197', '%H\ud800%M:%S.%f'),
|
||||
]
|
||||
for expected, string, format in inputs:
|
||||
with self.subTest(string=string, format=format):
|
||||
got = self.theclass.strptime(string, format)
|
||||
self.assertEqual(expected, got)
|
||||
self.assertIs(type(got), self.theclass)
|
||||
|
||||
def test_strptime_tz(self):
|
||||
strptime = self.theclass.strptime
|
||||
self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
|
||||
self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
|
||||
self.assertEqual(
|
||||
strptime("-00:02:01.000003", "%z").utcoffset(),
|
||||
-timedelta(minutes=2, seconds=1, microseconds=3)
|
||||
)
|
||||
# Only local timezone and UTC are supported
|
||||
for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
|
||||
(-_time.timezone, _time.tzname[0])):
|
||||
if tzseconds < 0:
|
||||
sign = '-'
|
||||
seconds = -tzseconds
|
||||
else:
|
||||
sign ='+'
|
||||
seconds = tzseconds
|
||||
hours, minutes = divmod(seconds//60, 60)
|
||||
tstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
|
||||
with self.subTest(tstr=tstr):
|
||||
t = strptime(tstr, "%z %Z")
|
||||
self.assertEqual(t.utcoffset(), timedelta(seconds=tzseconds))
|
||||
self.assertEqual(t.tzname(), tzname)
|
||||
self.assertIs(type(t), self.theclass)
|
||||
|
||||
# Can produce inconsistent time
|
||||
tstr, fmt = "+1234 UTC", "%z %Z"
|
||||
t = strptime(tstr, fmt)
|
||||
self.assertEqual(t.utcoffset(), 12 * HOUR + 34 * MINUTE)
|
||||
self.assertEqual(t.tzname(), 'UTC')
|
||||
# yet will roundtrip
|
||||
self.assertEqual(t.strftime(fmt), tstr)
|
||||
|
||||
# Produce naive time if no %z is provided
|
||||
self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
|
||||
|
||||
def test_strptime_errors(self):
|
||||
for tzstr in ("-2400", "-000", "z"):
|
||||
with self.assertRaises(ValueError):
|
||||
self.theclass.strptime(tzstr, "%z")
|
||||
|
||||
def test_strptime_single_digit(self):
|
||||
# bpo-34903: Check that single digit times are allowed.
|
||||
t = self.theclass(4, 5, 6)
|
||||
inputs = [
|
||||
('%H', '4:05:06', '%H:%M:%S', t),
|
||||
('%M', '04:5:06', '%H:%M:%S', t),
|
||||
('%S', '04:05:6', '%H:%M:%S', t),
|
||||
('%I', '4am:05:06', '%I%p:%M:%S', t),
|
||||
]
|
||||
for reason, string, format, target in inputs:
|
||||
reason = 'test single digit ' + reason
|
||||
with self.subTest(reason=reason,
|
||||
string=string,
|
||||
format=format,
|
||||
target=target):
|
||||
newdate = self.theclass.strptime(string, format)
|
||||
self.assertEqual(newdate, target, msg=reason)
|
||||
|
||||
def test_bool(self):
|
||||
# time is always True.
|
||||
cls = self.theclass
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Add :meth:`datetime.time.strptime` and :meth:`datetime.date.strptime`.
|
||||
Contributed by Wannes Boeykens.
|
|
@ -3445,6 +3445,27 @@ date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw)
|
|||
return new_date_subclass_ex(year, month, day, cls);
|
||||
}
|
||||
|
||||
/* Return new date from _strptime.strptime_datetime_date(). */
|
||||
static PyObject *
|
||||
date_strptime(PyObject *cls, PyObject *args)
|
||||
{
|
||||
PyObject *string, *format, *result;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "UU:strptime", &string, &format)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *module = PyImport_Import(&_Py_ID(_strptime));
|
||||
if (module == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
result = PyObject_CallMethodObjArgs(module,
|
||||
&_Py_ID(_strptime_datetime_date), cls,
|
||||
string, format, NULL);
|
||||
Py_DECREF(module);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Date arithmetic.
|
||||
|
@ -3910,6 +3931,11 @@ static PyMethodDef date_methods[] = {
|
|||
"number and weekday.\n\n"
|
||||
"This is the inverse of the date.isocalendar() function")},
|
||||
|
||||
{"strptime", (PyCFunction)date_strptime,
|
||||
METH_VARARGS | METH_CLASS,
|
||||
PyDoc_STR("string, format -> new date parsed from a string "
|
||||
"(like time.strptime()).")},
|
||||
|
||||
{"today", (PyCFunction)date_today, METH_NOARGS | METH_CLASS,
|
||||
PyDoc_STR("Current date or datetime: same as "
|
||||
"self.__class__.fromtimestamp(time.time()).")},
|
||||
|
@ -4644,6 +4670,27 @@ time_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
|||
return self;
|
||||
}
|
||||
|
||||
/* Return new time from _strptime.strptime_datetime_time(). */
|
||||
static PyObject *
|
||||
time_strptime(PyObject *cls, PyObject *args)
|
||||
{
|
||||
PyObject *string, *format, *result;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "UU:strptime", &string, &format)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *module = PyImport_Import(&_Py_ID(_strptime));
|
||||
if (module == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
result = PyObject_CallMethodObjArgs(module,
|
||||
&_Py_ID(_strptime_datetime_time), cls,
|
||||
string, format, NULL);
|
||||
Py_DECREF(module);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destructor.
|
||||
*/
|
||||
|
@ -5079,6 +5126,15 @@ time_reduce(PyDateTime_Time *self, PyObject *arg)
|
|||
|
||||
static PyMethodDef time_methods[] = {
|
||||
|
||||
/* Class method: */
|
||||
|
||||
{"strptime", (PyCFunction)time_strptime,
|
||||
METH_VARARGS | METH_CLASS,
|
||||
PyDoc_STR("string, format -> new time parsed from a string "
|
||||
"(like time.strptime()).")},
|
||||
|
||||
/* Instance methods: */
|
||||
|
||||
{"isoformat", _PyCFunction_CAST(time_isoformat), METH_VARARGS | METH_KEYWORDS,
|
||||
PyDoc_STR("Return string in ISO 8601 format, [HH[:MM[:SS[.mmm[uuu]]]]]"
|
||||
"[+HH:MM].\n\n"
|
||||
|
@ -5586,7 +5642,7 @@ datetime_utcfromtimestamp(PyObject *cls, PyObject *args)
|
|||
return result;
|
||||
}
|
||||
|
||||
/* Return new datetime from _strptime.strptime_datetime(). */
|
||||
/* Return new datetime from _strptime.strptime_datetime_datetime(). */
|
||||
static PyObject *
|
||||
datetime_strptime(PyObject *cls, PyObject *args)
|
||||
{
|
||||
|
@ -5599,7 +5655,8 @@ datetime_strptime(PyObject *cls, PyObject *args)
|
|||
if (module == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
result = PyObject_CallMethodObjArgs(module, &_Py_ID(_strptime_datetime),
|
||||
result = PyObject_CallMethodObjArgs(module,
|
||||
&_Py_ID(_strptime_datetime_datetime),
|
||||
cls, string, format, NULL);
|
||||
Py_DECREF(module);
|
||||
return result;
|
||||
|
|
Loading…
Reference in New Issue