commit e4684bb5b0e6393b4c3f0433345f7a860d2192d4 Author: deronnax Date: Thu Feb 18 14:45:11 2016 +1030 silentghost diff --git a/Lib/datetime.py b/Lib/datetime.py index 14edea6..0672e0f 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -318,6 +318,28 @@ def _divide_and_round(a, b): return q +def _parse_isodatetime(cls, string): + match = _ISO_REGEX[cls].match(string) + if not match: + raise ValueError('invalid RFC 3339 %s string: %r' % (cls.__name__, string)) + kw = match.groupdict() + tzinfo = kw.pop('tzinfo', None) + if tzinfo == 'Z' or tzinfo == 'z': + tzinfo = timezone.utc + elif tzinfo is not None: + offset_hours, _, offset_mins = tzinfo[1:].partition(':') + offset = timedelta(hours=int(offset_hours), minutes=int(offset_mins)) + if tzinfo[0] == '-': + offset = -offset + tzinfo = timezone(offset) + us = kw.pop('microsecond', None) + kw = {k: int(v) for k, v in kw.items() if v} + if us: + us = round(float(us), 6) + kw['microsecond'] = int(us * 1e6) + if tzinfo: + kw['tzinfo'] = tzinfo + return cls(**kw) class timedelta: """Represent the difference between two datetime objects. @@ -641,8 +663,6 @@ timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59, microseconds=999999) timedelta.resolution = timedelta(microseconds=1) -_DATE_RE = re.compile(r'(?P\d{4})-(?P\d{2})-(?P\d{2})$', re.ASCII) - class date: """Concrete date type. @@ -720,17 +740,12 @@ class date: return cls(y, m, d) @classmethod - def fromisoformat(cls, date_string): + def fromisoformat(cls, string): """Constructs a date from an RFC 3339 string, a strict subset of ISO 8601 Raises ValueError in case of ill-formatted or invalid string. """ - m = _DATE_RE.match(date_string) - if not m: - raise ValueError('invalid RFC 3339 date string: %r' % date_string) - kw = m.groupdict() - kw = {k: int(v) for k, v in kw.items()} - return cls(**kw) + return _parse_isodatetime(cls, string) # Conversions to string @@ -1020,10 +1035,6 @@ class tzinfo: _tzinfo_class = tzinfo -_TIME_RE = re.compile(r'(?P\d{2}):(?P\d{2}):(?P\d{2})' - r'(?P\.\d*)?(?PZ|([+-]\d{2}:\d{2}))?$', - re.ASCII|re.IGNORECASE) - class time: """Time with time zone. @@ -1076,38 +1087,15 @@ class time: self._hashcode = -1 return self - @staticmethod - def _parse_isotime(reg, isostring, class_name): - match = reg.match(isostring) - if not match: - raise ValueError('invalid RFC 3339 %s string: %r' % (class_name, isostring)) - kw = match.groupdict() - tzinfo = kw.pop('tzinfo', None) - if tzinfo == 'Z' or tzinfo == 'z': - tzinfo = timezone.utc - elif tzinfo is not None: - offset_hours, _, offset_mins = tzinfo[1:].partition(':') - offset = timedelta(hours=int(offset_hours), minutes=int(offset_mins)) - if tzinfo[0] == '-': - offset = -offset - tzinfo = timezone(offset) - us = kw.pop('microsecond', None) - kw = {k: int(v) for k, v in kw.items() if v is not None} - if us: - us = round(float(us), 6) - kw['microsecond'] = int(us * 1e6) - kw['tzinfo'] = tzinfo - return kw @classmethod - def fromisoformat(cls, time_string): + def fromisoformat(cls, string): """Constructs a time from an RFC 3339 string, a strict subset of ISO 8601 Microseconds are rounded to 6 digits. Raises ValueError in case of ill-formatted or invalid string. """ - kw = cls._parse_isotime(_TIME_RE, time_string, cls.__name__) - return cls(**kw) + return _parse_isodatetime(cls, string) # Read-only field accessors @@ -1364,9 +1352,6 @@ time.min = time(0, 0, 0) time.max = time(23, 59, 59, 999999) time.resolution = timedelta(microseconds=1) -_DATETIME_RE = re.compile(_DATE_RE.pattern[:-1] + r'[T ]' + _TIME_RE.pattern, - re.ASCII|re.IGNORECASE) - class datetime(date): """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]]) @@ -1487,14 +1472,13 @@ class datetime(date): time.tzinfo) @classmethod - def fromisoformat(cls, datetime_string): + def fromisoformat(cls, string): """Constructs a datetime from an RFC 3339 string, a strict subset of ISO 8601 Microseconds are rounded to 6 digits. Raises ValueError if string is ill formatted or invalid """ - kw = time._parse_isotime(_DATETIME_RE, datetime_string, cls.__name__) - return cls(**kw) + return _parse_isodatetime(cls, string) def timetuple(self): "Return local time tuple compatible with time.localtime()." @@ -1998,6 +1982,15 @@ timezone.min = timezone._create(timezone._minoffset) timezone.max = timezone._create(timezone._maxoffset) _EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) +_ISO_REGEX = { + date: re.compile(r'(?P\d{4})-(?P\d{2})-(?P\d{2})$', re.ASCII), + time: re.compile(r'(?P\d{2}):(?P\d{2}):(?P\d{2})' + r'(?P\.\d*)?(?PZ|([+-]\d{2}:\d{2}))?$', + re.ASCII|re.IGNORECASE), +} +_ISO_REGEX[datetime] = re.compile(_ISO_REGEX[date].pattern[:-1] + r'[T ]' + _ISO_REGEX[time].pattern, + re.ASCII|re.IGNORECASE) + # Some time zone algebra. For a datetime x, let # x.n = x stripped of its timezone -- its naive time. # x.o = x.utcoffset(), and assuming that doesn't raise an exception or