diff -r 6c122e4e1cb2 Lib/datetime.py
--- a/Lib/datetime.py Sun Feb 14 03:25:48 2016 +0000
+++ b/Lib/datetime.py Mon Feb 15 17:03:56 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
@@ -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,36 @@
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.
+ Microseconds values after 6 digits are discared.
+
+ Raises ValueError if the input is not well formatted or not a valid datetime.
+ """
+ match = datetime_re.match(value)
+ if not match:
+ raise ValueError('invalid {} iso8601 string : {}'.format(cls.__name__, value))
+ 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_hours = int(tzinfo[1:3])
+ offset = timedelta(hours=offset_hours, minutes=offset_mins)
+ if tzinfo[0] == '-':
+ offset = -offset
+ tzinfo = timezone(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 Mon Feb 15 17:03:56 2016 +1030
@@ -1847,6 +1847,32 @@
self.assertEqual(expected.tm_min, got.minute)
self.assertEqual(expected.tm_sec, got.second)
+ def test_fromisoformat(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(hours=-3, minutes=-20))))
+ self.assertEqual(parse_datetime('2012-04-23T10:20:30.400+02:30'),
+ datetime(2012, 4, 23, 10, 20, 30, 400000, timezone(timedelta(hours=2, minutes=30))))
+ self.assertEqual(parse_datetime('2012-04-23T10:20:30.400+02'),
+ datetime(2012, 4, 23, 10, 20, 30, 400000, timezone(timedelta(hours=2))))
+ self.assertEqual(parse_datetime('2012-04-23T10:20:30.400-02'),
+ datetime(2012, 4, 23, 10, 20, 30, 400000, timezone(timedelta(hours=-2))))
+ self.assertEqual(parse_datetime('2012-04-23T10:20:30.4000099-02'),
+ datetime(2012, 4, 23, 10, 20, 30, 400009, timezone(timedelta(hours=-2))))
+
+ # Invalid inputs
+ with self.assertRaises(ValueError):
+ parse_datetime('20120423091500')
+ parse_datetime('2012-04-56T09:15:90')
+
+
def test_fromtimestamp(self):
import time