diff --git a/wsme/tests/test_utils.py b/wsme/tests/test_utils.py index 81b8296..a6165dd 100644 --- a/wsme/tests/test_utils.py +++ b/wsme/tests/test_utils.py @@ -1,5 +1,6 @@ import datetime import unittest +import pytz from wsme import utils @@ -26,6 +27,11 @@ class TestUtils(unittest.TestCase): good_times = [ ('12:03:54', datetime.time(12, 3, 54)), ('23:59:59.000004', datetime.time(23, 59, 59, 4)), + ('01:02:03+00:00', datetime.time(1, 2, 3, 0, pytz.UTC)), + ('01:02:03+23:59', datetime.time(1, 2, 3, 0, + pytz.FixedOffset(1439))), + ('01:02:03-23:59', datetime.time(1, 2, 3, 0, + pytz.FixedOffset(-1439))), ] ill_formatted_times = [ '24-12-2004' @@ -33,6 +39,8 @@ class TestUtils(unittest.TestCase): out_of_range_times = [ '32:12:00', '00:54:60', + '01:02:03-24:00', + '01:02:03+24:00', ] for s, t in good_times: assert utils.parse_isotime(s) == t @@ -42,16 +50,27 @@ class TestUtils(unittest.TestCase): def test_parse_isodatetime(self): good_datetimes = [ ('2008-02-12T12:03:54', - datetime.datetime(2008, 2, 12, 12, 3, 54)), + datetime.datetime(2008, 2, 12, 12, 3, 54)), ('2012-05-14T23:59:59.000004', - datetime.datetime(2012, 5, 14, 23, 59, 59, 4)), + datetime.datetime(2012, 5, 14, 23, 59, 59, 4)), + ('1856-07-10T01:02:03+00:00', + datetime.datetime(1856, 7, 10, 1, 2, 3, 0, pytz.UTC)), + ('1856-07-10T01:02:03+23:59', + datetime.datetime(1856, 7, 10, 1, 2, 3, 0, + pytz.FixedOffset(1439))), + ('1856-07-10T01:02:03-23:59', + datetime.datetime(1856, 7, 10, 1, 2, 3, 0, + pytz.FixedOffset(-1439))), ] ill_formatted_datetimes = [ - '24-12-2004' + '24-12-2004', + '1856-07-10+33:00' ] out_of_range_datetimes = [ '2008-02-12T32:12:00', '2012-13-12T00:54:60', + '1856-07-10T01:02:03-24:00', + '1856-07-10T01:02:03+24:00', ] for s, t in good_datetimes: assert utils.parse_isodatetime(s) == t diff --git a/wsme/utils.py b/wsme/utils.py index d7f3486..e0c65dc 100644 --- a/wsme/utils.py +++ b/wsme/utils.py @@ -1,5 +1,6 @@ import decimal import datetime +import pytz import re from six.moves import builtins, http_client @@ -17,7 +18,7 @@ tz_re = r'((?P[+-])(?P\d{2}):(?P\d{2}))' + \ datetime_re = re.compile( '%sT%s(%s)?' % (date_re, time_re, tz_re)) date_re = re.compile(date_re) -time_re = re.compile(time_re) +time_re = re.compile('%s(%s)?' % (time_re, tz_re)) if hasattr(builtins, '_'): @@ -50,16 +51,17 @@ def parse_isotime(value): f = decimal.Decimal('0.' + m.group('sec_frac')) f = str(f.quantize(decimal.Decimal('0.000001'))) ms = int(f[2:]) + tz = _parse_tzparts(m.groupdict()) return datetime.time( int(m.group('hour')), int(m.group('min')), int(m.group('sec')), - ms) + ms, + tz) except ValueError: raise ValueError("'%s' is a out-of-range time" % (value)) -# TODO handle timezone def parse_isodatetime(value): if dateutil: return dateutil.parser.parse(value) @@ -72,6 +74,7 @@ def parse_isodatetime(value): f = decimal.Decimal('0.' + m.group('sec_frac')) f = f.quantize(decimal.Decimal('0.000001')) ms = int(str(f)[2:]) + tz = _parse_tzparts(m.groupdict()) return datetime.datetime( int(m.group('year')), int(m.group('month')), @@ -79,11 +82,24 @@ def parse_isodatetime(value): int(m.group('hour')), int(m.group('min')), int(m.group('sec')), - ms) + ms, + tz) except ValueError: raise ValueError("'%s' is a out-of-range datetime" % (value)) +def _parse_tzparts(parts): + if 'tz_z' in parts and parts['tz_z'] == 'Z': + return pytz.UTC + if 'tz_min' not in parts or not parts['tz_min']: + return None + + tz_minute_offset = (int(parts['tz_hour']) * 60 + int(parts['tz_min'])) + tz_multiplier = -1 if parts['tz_sign'] == '-' else 1 + + return pytz.FixedOffset(tz_multiplier * tz_minute_offset) + + def is_valid_code(code_value): """ This function checks if incoming value in http response codes range.