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

bpo-34903: For datetime.datetime.strptime(), the leading zero for some two-digit formats is optional. #14149

Merged
merged 10 commits into from
Jun 18, 2019
35 changes: 20 additions & 15 deletions Doc/library/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2093,7 +2093,7 @@ format codes.
| | where 0 is Sunday and 6 is | | |
| | Saturday. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%d`` | Day of the month as a | 01, 02, ..., 31 | |
| ``%d`` | Day of the month as a | 01, 02, ..., 31 | \(9) |
| | zero-padded decimal number. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%b`` | Month as locale's abbreviated || Jan, Feb, ..., Dec | \(1) |
Expand All @@ -2106,29 +2106,29 @@ format codes.
| | || Januar, Februar, ..., | |
| | | Dezember (de_DE) | |
+-----------+--------------------------------+------------------------+-------+
| ``%m`` | Month as a zero-padded | 01, 02, ..., 12 | |
| ``%m`` | Month as a zero-padded | 01, 02, ..., 12 | \(9) |
| | decimal number. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%y`` | Year without century as a | 00, 01, ..., 99 | |
| ``%y`` | Year without century as a | 00, 01, ..., 99 | \(9) |
| | zero-padded decimal number. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%Y`` | Year with century as a decimal | 0001, 0002, ..., 2013, | \(2) |
| | number. | 2014, ..., 9998, 9999 | |
+-----------+--------------------------------+------------------------+-------+
| ``%H`` | Hour (24-hour clock) as a | 00, 01, ..., 23 | |
| ``%H`` | Hour (24-hour clock) as a | 00, 01, ..., 23 | \(9) |
| | zero-padded decimal number. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%I`` | Hour (12-hour clock) as a | 01, 02, ..., 12 | |
| ``%I`` | Hour (12-hour clock) as a | 01, 02, ..., 12 | \(9) |
| | zero-padded decimal number. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%p`` | Locale's equivalent of either || AM, PM (en_US); | \(1), |
| | AM or PM. || am, pm (de_DE) | \(3) |
+-----------+--------------------------------+------------------------+-------+
| ``%M`` | Minute as a zero-padded | 00, 01, ..., 59 | |
| ``%M`` | Minute as a zero-padded | 00, 01, ..., 59 | \(9) |
| | decimal number. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%S`` | Second as a zero-padded | 00, 01, ..., 59 | \(4) |
| | decimal number. | | |
| ``%S`` | Second as a zero-padded | 00, 01, ..., 59 | \(4), |
| | decimal number. | | \(9) |
+-----------+--------------------------------+------------------------+-------+
| ``%f`` | Microsecond as a decimal | 000000, 000001, ..., | \(5) |
| | number, zero-padded on the | 999999 | |
Expand All @@ -2142,19 +2142,19 @@ format codes.
| ``%Z`` | Time zone name (empty string | (empty), UTC, EST, CST | |
| | if the object is naive). | | |
+-----------+--------------------------------+------------------------+-------+
| ``%j`` | Day of the year as a | 001, 002, ..., 366 | |
| ``%j`` | Day of the year as a | 001, 002, ..., 366 | \(9) |
| | zero-padded decimal number. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%U`` | Week number of the year | 00, 01, ..., 53 | \(7) |
| | (Sunday as the first day of | | |
| ``%U`` | Week number of the year | 00, 01, ..., 53 | \(7), |
| | (Sunday as the first day of | | \(9) |
| | the week) as a zero padded | | |
| | decimal number. All days in a | | |
| | new year preceding the first | | |
| | Sunday are considered to be in | | |
| | week 0. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%W`` | Week number of the year | 00, 01, ..., 53 | \(7) |
| | (Monday as the first day of | | |
| ``%W`` | Week number of the year | 00, 01, ..., 53 | \(7), |
| | (Monday as the first day of | | \(9) |
| | the week) as a decimal number. | | |
| | All days in a new year | | |
| | preceding the first Monday | | |
Expand Down Expand Up @@ -2194,8 +2194,8 @@ incomplete or ambiguous ISO 8601 directives will raise a :exc:`ValueError`.
| ``%u`` | ISO 8601 weekday as a decimal | 1, 2, ..., 7 | |
| | number where 1 is Monday. | | |
+-----------+--------------------------------+------------------------+-------+
| ``%V`` | ISO 8601 week as a decimal | 01, 02, ..., 53 | \(8) |
| | number with Monday as | | |
| ``%V`` | ISO 8601 week as a decimal | 01, 02, ..., 53 | \(8), |
| | number with Monday as | | \(9) |
| | the first day of the week. | | |
| | Week 01 is the week containing | | |
| | Jan 4. | | |
Expand Down Expand Up @@ -2291,6 +2291,11 @@ Notes:
:meth:`strptime` format string. Also note that ``%G`` and ``%Y`` are not
interchangeable.

(9)
When used with the :meth:`strptime` method, the leading zero is optional
for formats ``%d``, ``%m``, ``%H``, ``%I``, ``%M``, ``%S``, ``%J``, ``%U``,
``%W``, and ``%V``. Format ``%y`` does require a leading zero.

.. rubric:: Footnotes

.. [#] If, that is, we ignore the effects of Relativity
Expand Down
35 changes: 35 additions & 0 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -2501,6 +2501,7 @@ def test_strptime(self):
self.assertEqual(expected, got)

strptime = self.theclass.strptime

self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
self.assertEqual(
Expand Down Expand Up @@ -2535,6 +2536,40 @@ def test_strptime(self):
with self.assertRaises(ValueError): strptime("-2400", "%z")
with self.assertRaises(ValueError): strptime("-000", "%z")

def test_strptime_single_digit(self):
# bpo-34903: Check that single digit dates and times are allowed.

strptime = self.theclass.strptime

with self.assertRaises(ValueError):
# %y does require two digits.
newdate = strptime('01/02/3 04:05:06', '%d/%m/%y %H:%M:%S')
dt1 = self.theclass(2003, 2, 1, 4, 5, 6)
dt2 = self.theclass(2003, 1, 2, 4, 5, 6)
dt3 = self.theclass(2003, 2, 1, 0, 0, 0)
dt4 = self.theclass(2003, 1, 25, 0, 0, 0)
inputs = [
('%d', '1/02/03 4:5:6', '%d/%m/%y %H:%M:%S', dt1),
('%m', '01/2/03 4:5:6', '%d/%m/%y %H:%M:%S', dt1),
('%H', '01/02/03 4:05:06', '%d/%m/%y %H:%M:%S', dt1),
('%M', '01/02/03 04:5:06', '%d/%m/%y %H:%M:%S', dt1),
('%S', '01/02/03 04:05:6', '%d/%m/%y %H:%M:%S', dt1),
('%j', '2/03 04am:05:06', '%j/%y %I%p:%M:%S',dt2),
('%I', '02/03 4am:05:06', '%j/%y %I%p:%M:%S',dt2),
('%w', '6/04/03', '%w/%U/%y', dt3),
# %u requires a single digit.
('%W', '6/4/2003', '%u/%W/%Y', dt3),
('%V', '6/4/2003', '%u/%V/%G', dt4),
]
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)

def test_more_timetuple(self):
# This tests fields beyond those tested by the TestDate.test_timetuple.
t = self.theclass(2004, 12, 31, 6, 22, 33)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Documented that in :meth:`datetime.datetime.strptime()`, the leading zero in some two-digit formats is optional. Patch by Mike Gleen.