aboutsummaryrefslogtreecommitdiff
path: root/dateutil/parser/isoparser.py
diff options
context:
space:
mode:
Diffstat (limited to 'dateutil/parser/isoparser.py')
-rw-r--r--dateutil/parser/isoparser.py39
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}: