diff -r 6c122e4e1cb2 Lib/datetime.py
--- a/Lib/datetime.py Sun Feb 14 03:25:48 2016 +0000
+++ b/Lib/datetime.py Sun Feb 14 22:17:06 2016 +1030
@@ -6,6 +6,7 @@
import time as _time
import math as _math
+import re
def _cmp(x, y):
return 0 if x == y else 1 if x > y else -1
@@ -40,7 +41,7 @@
def _days_before_year(year):
"year -> number of days before January 1st of year."
y = year - 1
- return y*365 + y//4 - y//100 + y//400
+ return y * 365 + y // 4 - y // 100 + y // 400
def _days_in_month(year, month):
"year, month -> number of days in that month in that year."
@@ -146,6 +147,12 @@
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
_DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+datetime_re = re.compile(
+ r'(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})'
+ r'[T ](?P\d{1,2}):(?P\d{1,2})'
+ r'(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?'
+ r'(?PZ|[+-]\d{2}(?::?\d{2})?)?$'
+)
def _build_struct_time(y, m, d, hh, mm, ss, dstflag):
wday = (_ymd2ord(y, m, d) + 6) % 7
@@ -1401,6 +1408,34 @@
return result
@classmethod
+ def fromisoformat(cls, value):
+ """Parses a string and return a datetime.datetime.
+
+ This function supports time zone offsets. When the input contains one,
+ the output uses a timezone with a fixed offset from UTC.
+
+ Raises ValueError if the input is well formatted but not a valid datetime.
+ Returns None if the input isn't well formatted.
+ """
+ match = datetime_re.match(value)
+ if match:
+ kw = match.groupdict()
+ if kw['microsecond']:
+ kw['microsecond'] = kw['microsecond'].ljust(6, '0')
+ tzinfo = kw.pop('tzinfo')
+ if tzinfo == 'Z':
+ tzinfo = timezone.utc
+ elif tzinfo is not None:
+ offset_mins = int(tzinfo[-2:]) if len(tzinfo) > 3 else 0
+ offset = 60 * int(tzinfo[1:3]) + offset_mins
+ if tzinfo[0] == '-':
+ offset = -offset
+ tzinfo = timezone(timedelta(minutes=offset))
+ kw = {k: int(v) for k, v in kw.items() if v is not None}
+ kw['tzinfo'] = tzinfo
+ return cls(**kw)
+
+ @classmethod
def utcfromtimestamp(cls, t):
"""Construct a naive UTC datetime from a POSIX timestamp."""
return cls._fromtimestamp(t, True, None)
diff -r 6c122e4e1cb2 Lib/test/datetimetester.py
--- a/Lib/test/datetimetester.py Sun Feb 14 03:25:48 2016 +0000
+++ b/Lib/test/datetimetester.py Sun Feb 14 22:17:06 2016 +1030
@@ -1847,6 +1847,34 @@
self.assertEqual(expected.tm_min, got.minute)
self.assertEqual(expected.tm_sec, got.second)
+ def test_fromisoformat(self):
+ t = self.theclass(2012, 12, 1, 23, 59, 0, 411, tzinfo=timezone.utc)
+ self.assertEqual(t, self.theclass.fromisoformat(t.isoformat()))
+
+ def test_parse_datetime(self):
+ # Valid inputs
+
+ parse_datetime = self.theclass.fromisoformat
+ self.assertEqual(parse_datetime('2012-04-23T09:15:00'),
+ datetime(2012, 4, 23, 9, 15))
+ self.assertEqual(parse_datetime('2012-4-9 4:8:16'),
+ datetime(2012, 4, 9, 4, 8, 16))
+ self.assertEqual(parse_datetime('2012-04-23T09:15:00Z'),
+ datetime(2012, 4, 23, 9, 15, 0, 0, timezone(timedelta(minutes=0))))
+ self.assertEqual(parse_datetime('2012-4-9 4:8:16-0320'),
+ datetime(2012, 4, 9, 4, 8, 16, 0, timezone(timedelta(minutes=-200))))
+ self.assertEqual(parse_datetime('2012-04-23T10:20:30.400+02:30'),
+ datetime(2012, 4, 23, 10, 20, 30, 400000, timezone(timedelta(minutes=150))))
+ self.assertEqual(parse_datetime('2012-04-23T10:20:30.400+02'),
+ datetime(2012, 4, 23, 10, 20, 30, 400000, timezone(timedelta(minutes=120))))
+ self.assertEqual(parse_datetime('2012-04-23T10:20:30.400-02'),
+ datetime(2012, 4, 23, 10, 20, 30, 400000, timezone(timedelta(minutes=-120))))
+ # Invalid inputs
+ self.assertEqual(parse_datetime('20120423091500'), None)
+ with self.assertRaises(ValueError):
+ parse_datetime('2012-04-56T09:15:90')
+
+
def test_fromtimestamp(self):
import time