From 6174093f205f8edc0eefd5fdcc3523d6cf533158 Mon Sep 17 00:00:00 2001 From: Mike Gleen Date: Mon, 17 Jun 2019 08:34:00 +0100 Subject: [PATCH 1/9] Update rst file --- Doc/library/datetime.rst | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index fb41aeeb5bed03..c935e3526512fd 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -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) | @@ -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 | | @@ -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 | | | @@ -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. | | | @@ -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 From 74e9a1ec25d478871ecf6d647268532b577423a1 Mon Sep 17 00:00:00 2001 From: Mike Gleen Date: Mon, 17 Jun 2019 08:40:04 +0100 Subject: [PATCH 2/9] Add tests for single digit dates and times. --- Lib/test/datetimetester.py | 41 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 239a0b5ac8308a..e379f33522c5af 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2501,6 +2501,47 @@ def test_strptime(self): self.assertEqual(expected, got) strptime = self.theclass.strptime + + # bpo-34903: Check that single digit dates and times are allowed. + with self.assertRaises(ValueError): + # %y does require two digits + newdate = strptime('01/02/3 04:05:06', '%d/%m/%y %H:%M:%S') + inputs = [ + ('%d', + '1/02/03 4:5:6', '%d/%m/%y %H:%M:%S', '2003-02-01T04:05:06'), + ('%m', + '01/2/03 4:5:6', '%d/%m/%y %H:%M:%S', '2003-02-01T04:05:06'), + ('%H', + '01/02/03 4:05:06', '%d/%m/%y %H:%M:%S', '2003-02-01T04:05:06'), + ('%M', + '01/02/03 04:5:06', '%d/%m/%y %H:%M:%S', '2003-02-01T04:05:06'), + ('%S', + '01/02/03 04:05:6', '%d/%m/%y %H:%M:%S', '2003-02-01T04:05:06'), + ('%j', + '2/03 04am:05:06', '%j/%y %I%p:%M:%S','2003-01-02T04:05:06'), + ('%I', + '02/03 4am:05:06', '%j/%y %I%p:%M:%S','2003-01-02T04:05:06'), + ('%w', + '6/04/03', '%w/%U/%y', '2003-02-01T00:00:00'), + ('%W', + '6/4/2003', '%u/%W/%Y', '2003-02-01T00:00:00'), + ('%V', + '6/4/2003', '%u/%V/%G', '2003-01-25T00:00:00'), + ] + def time_helper(reason, string, format, target): + reason = 'test single digit ' + reason + with self.subTest(reason=reason, + string=string, + format=format, + target=target): + newdate = strptime(string, format) + self.assertEqual(newdate.isoformat(timespec='seconds'), target, + msg=reason) + for testcase in inputs: + time_helper(*testcase) + + # end bpo-34903 + self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE) self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE) self.assertEqual( From 0447ebfdb21f6fcff171742da90c2b3cf966befa Mon Sep 17 00:00:00 2001 From: Mike Gleen Date: Mon, 17 Jun 2019 08:46:00 +0100 Subject: [PATCH 3/9] Cosmetic. --- Lib/test/datetimetester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index e379f33522c5af..d4763fec510016 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2504,7 +2504,7 @@ def test_strptime(self): # bpo-34903: Check that single digit dates and times are allowed. with self.assertRaises(ValueError): - # %y does require two digits + # %y does require two digits. newdate = strptime('01/02/3 04:05:06', '%d/%m/%y %H:%M:%S') inputs = [ ('%d', From 8f6e06ecf2c24b78255c7cfa6dda71fa8d3ab790 Mon Sep 17 00:00:00 2001 From: Mike Gleen Date: Mon, 17 Jun 2019 08:49:13 +0100 Subject: [PATCH 4/9] Add comment. --- Lib/test/datetimetester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index d4763fec510016..3c4847aefa4ec5 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2523,7 +2523,7 @@ def test_strptime(self): '02/03 4am:05:06', '%j/%y %I%p:%M:%S','2003-01-02T04:05:06'), ('%w', '6/04/03', '%w/%U/%y', '2003-02-01T00:00:00'), - ('%W', + ('%W', # %u requires a single digit. '6/4/2003', '%u/%W/%Y', '2003-02-01T00:00:00'), ('%V', '6/4/2003', '%u/%V/%G', '2003-01-25T00:00:00'), From d12a6da2f6f2b12f74ee3fe014b25ad68a18cabe Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" Date: Mon, 17 Jun 2019 09:36:48 +0000 Subject: [PATCH 5/9] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst diff --git a/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst b/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst new file mode 100644 index 00000000000000..216b9938a66ff4 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst @@ -0,0 +1,3 @@ +For datetime.datetime.strptime(), the leading zero for some two-digit formats is optional. + +Add a footnote to that effect and add tests to datetimetester.py \ No newline at end of file From fab6869ab197fee8b06959b325db5e3567306939 Mon Sep 17 00:00:00 2001 From: Mike Gleen Date: Mon, 17 Jun 2019 17:40:24 +0100 Subject: [PATCH 6/9] Update 2019-06-17-09-36-46.bpo-34903.r_wGRc.rst --- .../Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst b/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst index 216b9938a66ff4..95cbf5fadc6961 100644 --- a/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst +++ b/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst @@ -1,3 +1 @@ -For datetime.datetime.strptime(), the leading zero for some two-digit formats is optional. - -Add a footnote to that effect and add tests to datetimetester.py \ No newline at end of file +Documented that in :meth:datetime.datetime.strptime(), the leading zero in some two-digit formats is optional. Patch by Mike Gleen. From 5376b81e51e9da6627819d57fe5a5a819c9657fc Mon Sep 17 00:00:00 2001 From: Mike Gleen Date: Mon, 17 Jun 2019 18:00:45 +0100 Subject: [PATCH 7/9] Move the subtest logic in line with the for loop. Replace the string used as a target with a datetime instance. --- Lib/test/datetimetester.py | 42 +++++++++++++++----------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 3c4847aefa4ec5..77772682f21086 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2506,39 +2506,31 @@ def test_strptime(self): 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', '2003-02-01T04:05:06'), - ('%m', - '01/2/03 4:5:6', '%d/%m/%y %H:%M:%S', '2003-02-01T04:05:06'), - ('%H', - '01/02/03 4:05:06', '%d/%m/%y %H:%M:%S', '2003-02-01T04:05:06'), - ('%M', - '01/02/03 04:5:06', '%d/%m/%y %H:%M:%S', '2003-02-01T04:05:06'), - ('%S', - '01/02/03 04:05:6', '%d/%m/%y %H:%M:%S', '2003-02-01T04:05:06'), - ('%j', - '2/03 04am:05:06', '%j/%y %I%p:%M:%S','2003-01-02T04:05:06'), - ('%I', - '02/03 4am:05:06', '%j/%y %I%p:%M:%S','2003-01-02T04:05:06'), - ('%w', - '6/04/03', '%w/%U/%y', '2003-02-01T00:00:00'), - ('%W', # %u requires a single digit. - '6/4/2003', '%u/%W/%Y', '2003-02-01T00:00:00'), - ('%V', - '6/4/2003', '%u/%V/%G', '2003-01-25T00:00:00'), + ('%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), ] - def time_helper(reason, string, format, target): + 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.isoformat(timespec='seconds'), target, - msg=reason) - for testcase in inputs: - time_helper(*testcase) + self.assertEqual(newdate, target, msg=reason) # end bpo-34903 From 6c001c108be4fe106eed01570925ed7e79651014 Mon Sep 17 00:00:00 2001 From: Mike Gleen Date: Mon, 17 Jun 2019 19:18:05 +0100 Subject: [PATCH 8/9] Update 2019-06-17-09-36-46.bpo-34903.r_wGRc.rst Add backticks so Sphinx can turn the method name into a link to the relevant method. --- .../next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst b/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst index 95cbf5fadc6961..7e277f4ec1aeda 100644 --- a/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst +++ b/Misc/NEWS.d/next/Documentation/2019-06-17-09-36-46.bpo-34903.r_wGRc.rst @@ -1 +1 @@ -Documented that in :meth:datetime.datetime.strptime(), the leading zero in some two-digit formats is optional. Patch by Mike Gleen. +Documented that in :meth:`datetime.datetime.strptime()`, the leading zero in some two-digit formats is optional. Patch by Mike Gleen. From b29515f8139ab5222721de03e556aa07cd3344d3 Mon Sep 17 00:00:00 2001 From: Mike Gleen Date: Tue, 18 Jun 2019 17:18:21 +0100 Subject: [PATCH 9/9] Move code to a separate method. --- Lib/test/datetimetester.py | 66 ++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 77772682f21086..53de0658773c7b 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2502,38 +2502,6 @@ def test_strptime(self): strptime = self.theclass.strptime - # bpo-34903: Check that single digit dates and times are allowed. - 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) - - # end bpo-34903 - self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE) self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE) self.assertEqual( @@ -2568,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)