diff options
Diffstat (limited to 'dateutil/parser/isoparser.py')
-rw-r--r-- | dateutil/parser/isoparser.py | 39 |
1 files changed, 24 insertions, 15 deletions
diff --git a/dateutil/parser/isoparser.py b/dateutil/parser/isoparser.py index 844bb5d..e3cf6d8 100644 --- a/dateutil/parser/isoparser.py +++ b/dateutil/parser/isoparser.py @@ -4,6 +4,8 @@ This module offers a parser for ISO-8601 strings It is intended to support all valid date, time and datetime formats per the ISO-8601 specification. + +..versionadded:: 2.7.0 """ from datetime import datetime, timedelta, time, date import calendar @@ -86,10 +88,12 @@ class isoparser(object): - ``hh`` - ``hh:mm`` or ``hhmm`` - ``hh:mm:ss`` or ``hhmmss`` - - ``hh:mm:ss.sss`` or ``hh:mm:ss.ssssss`` (3-6 sub-second digits) + - ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits) Midnight is a special case for `hh`, as the standard supports both - 00:00 and 24:00 as a representation. + 00:00 and 24:00 as a representation. The decimal separator can be + either a dot or a comma. + .. caution:: @@ -124,6 +128,8 @@ class isoparser(object): currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not guaranteed to continue failing in future versions if they encode a valid date. + + .. versionadded:: 2.7.0 """ components, pos = self._parse_isodate(dt_str) @@ -133,6 +139,10 @@ class isoparser(object): else: raise ValueError('String contains unknown ISO components') + if len(components) > 3 and components[3] == 24: + components[3] = 0 + return datetime(*components) + timedelta(days=1) + return datetime(*components) @_takes_ascii @@ -163,7 +173,10 @@ class isoparser(object): :return: Returns a :class:`datetime.time` object """ - return time(*self._parse_isotime(timestr)) + components = self._parse_isotime(timestr) + if components[0] == 24: + components[0] = 0 + return time(*components) @_takes_ascii def parse_tzstr(self, tzstr, zero_as_utc=True): @@ -186,10 +199,9 @@ class isoparser(object): return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc) # Constants - _MICROSECOND_END_REGEX = re.compile(b'[-+Z]+') _DATE_SEP = b'-' _TIME_SEP = b':' - _MICRO_SEP = b'.' + _FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)') def _parse_isodate(self, dt_str): try: @@ -329,7 +341,7 @@ class isoparser(object): while pos < len_str and comp < 5: comp += 1 - if timestr[pos:pos + 1] in b'-+Z': + if timestr[pos:pos + 1] in b'-+Zz': # Detect time zone boundary components[-1] = self._parse_tzstr(timestr[pos:]) pos = len_str @@ -344,16 +356,14 @@ class isoparser(object): pos += 1 if comp == 3: - # Microsecond - if timestr[pos:pos + 1] != self._MICRO_SEP: + # Fraction of a second + frac = self._FRACTION_REGEX.match(timestr[pos:]) + if not frac: continue - pos += 1 - us_str = self._MICROSECOND_END_REGEX.split(timestr[pos:pos + 6], - 1)[0] - + us_str = frac.group(1)[:6] # Truncate to microseconds components[comp] = int(us_str) * 10**(6 - len(us_str)) - pos += len(us_str) + pos += len(frac.group()) if pos < len_str: raise ValueError('Unused components in ISO string') @@ -362,12 +372,11 @@ class isoparser(object): # Standard supports 00:00 and 24:00 as representations of midnight if any(component != 0 for component in components[1:4]): raise ValueError('Hour may only be 24 at 24:00:00.000') - components[0] = 0 return components def _parse_tzstr(self, tzstr, zero_as_utc=True): - if tzstr == b'Z': + if tzstr == b'Z' or tzstr == b'z': return tz.tzutc() if len(tzstr) not in {3, 5, 6}: |