diff --git a/CHANGES.txt b/CHANGES.txt index cea315b..21af8c1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,8 @@ Unreleased Features ~~~~~~~~ +- Support spec-mandated truncations of ISO-8601 timezones. + - Support spec-mandated truncations of ISO-8601 datetimes. - Allow specifying custom representations of values for boolean fields. diff --git a/colander/iso8601.py b/colander/iso8601.py index d2c89b4..6c74aae 100644 --- a/colander/iso8601.py +++ b/colander/iso8601.py @@ -41,10 +41,10 @@ __all__ = ["parse_date", "ParseError", "Utc", "FixedOffset"] ISO8601_REGEX = re.compile( r"(?P[0-9]{4})(-(?P[0-9]{1,2})(-(?P[0-9]{1,2})" r"((?P.)(?P[0-9]{2})(:(?P[0-9]{2})(:(?P[0-9]{2})(\.(?P[0-9]+))?)?)?" - r"(?PZ|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?" + r"(?PZ|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?" ) TIMEZONE_REGEX = re.compile( - "(?P[+-])(?P[0-9]{2}).(?P[0-9]{2})") + "(?P[+-])(?P[0-9]{2})(:?(?P[0-9]{2}))?") class ParseError(Exception): """Raised when there is a problem parsing a date string""" @@ -97,8 +97,13 @@ def parse_timezone(tzstring, default_timezone=UTC): if tzstring is None: return default_timezone m = TIMEZONE_REGEX.match(tzstring) - prefix, hours, minutes = m.groups() - hours, minutes = int(hours), int(minutes) + prefix = m.group('prefix') + hours = int(m.group('hours')) + minutes = m.group('minutes') + if minutes is None: + minutes = 0 + else: + minutes = int(minutes) if prefix == "-": hours = -hours minutes = -minutes diff --git a/colander/tests/test_iso8601.py b/colander/tests/test_iso8601.py index a8b5de7..95501ee 100644 --- a/colander/tests/test_iso8601.py +++ b/colander/tests/test_iso8601.py @@ -70,12 +70,36 @@ class Test_parse_timezone(unittest.TestCase): self.assertEqual(result.utcoffset(None), datetime.timedelta(hours=1, minutes=0)) + def test_positive_without_colon(self): + tzstring = "+0100" + result = self._callFUT(tzstring) + self.assertEqual(result.utcoffset(None), + datetime.timedelta(hours=1, minutes=0)) + + def test_positive_without_minutes(self): + tzstring = "+01" + result = self._callFUT(tzstring) + self.assertEqual(result.utcoffset(None), + datetime.timedelta(hours=1, minutes=0)) + def test_negative(self): tzstring = "-01:00" result = self._callFUT(tzstring) self.assertEqual(result.utcoffset(None), datetime.timedelta(hours=-1, minutes=0)) + def test_negative_without_colon(self): + tzstring = "-0100" + result = self._callFUT(tzstring) + self.assertEqual(result.utcoffset(None), + datetime.timedelta(hours=-1, minutes=0)) + + def test_negative_without_minutes(self): + tzstring = "-01" + result = self._callFUT(tzstring) + self.assertEqual(result.utcoffset(None), + datetime.timedelta(hours=-1, minutes=0)) + class Test_parse_date(unittest.TestCase): def _callFUT(self, datestring): from ..iso8601 import parse_date