Răsfoiți Sursa

DatePeriod: property 'isoformat' is now writable supporting iso8601 date
period strings with format 'start/end'

Fabian Peter Hammerle 9 ani în urmă
părinte
comite
aafb4b1ce4
3 a modificat fișierele cu 112 adăugiri și 10 ștergeri
  1. 32 8
      ioex/__init__.py
  2. 1 0
      setup.py
  3. 79 2
      tests/test_.py

+ 32 - 8
ioex/__init__.py

@@ -1,9 +1,11 @@
+import contextlib
+import datetime
+import dateutil.parser
+import locale
 import os
+import re
 import sys
-import locale
-import datetime
 import threading
-import contextlib
 
 class UnsupportedLocaleSettingError(locale.Error):
     pass
@@ -49,9 +51,19 @@ def int_input_with_default(prompt, default):
 
 class DatePeriod(object):
 
-    def __init__(self, start = None, end = None):
-        self.start = start
-        self.end = end
+    _timestamp_iso_format = r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?(Z|[-\+]\d{2}:\d{2})?'
+    _timeperiod_iso_format = r'(?P<start>%(t)s)\/(?P<end>%(t)s)' % {'t': _timestamp_iso_format}
+
+    def __init__(self, start = None, end = None, isoformat = None):
+        self._start = None
+        self._end = None
+        if (start or end) and isoformat:
+            raise AttributeError('when providing isoformat no other parameters may be specified')
+        elif isoformat:
+            self.isoformat = isoformat
+        else:
+            self.start = start
+            self.end = end
 
     @property
     def start(self):
@@ -78,6 +90,18 @@ class DatePeriod(object):
         if self.start is None or self.end is None:
             raise ValueError('both start and end must be set')
         return '%s/%s' % (
-                self.start.isoformat().replace('+00:00', 'Z'), 
-                self.end.isoformat().replace('+00:00', 'Z'), 
+                self.start.isoformat().replace('+00:00', 'Z'),
+                self.end.isoformat().replace('+00:00', 'Z'),
                 )
+
+    @isoformat.setter
+    def isoformat(self, text):
+        match = re.search('^%s$' % self.__class__._timeperiod_iso_format, text)
+        if not match:
+            raise ValueError(
+                    "given string '%s' does not match the supported pattern '%s'"
+                     % (text, self.__class__._timeperiod_iso_format)
+                     )
+        attr = match.groupdict()
+        self.start = dateutil.parser.parse(attr['start'])
+        self.end = dateutil.parser.parse(attr['end'])

+ 1 - 0
setup.py

@@ -14,5 +14,6 @@ setup(
     keywords = [],
     classifiers = [],
     scripts = glob.glob('scripts/*'),
+    install_requires = ['python-dateutil'],
     tests_require = ['pytest', 'pytz']
     )

+ 79 - 2
tests/test_.py

@@ -40,7 +40,7 @@ def test_setlocale_strtime(dt, dt_format, locale_code, expected_string):
     [datetime.datetime(2016, 7, 24, 12, 21), None],
     [None, None],
     ])
-def test_dateperiod_init(start, end):
+def test_dateperiod_init_start_end(start, end):
     p = ioex.DatePeriod(start = start, end = end)
     assert p.start == start
     assert p.end == end
@@ -49,10 +49,41 @@ def test_dateperiod_init(start, end):
     [';-)', datetime.datetime(2016, 7, 24, 12, 22)],
     [datetime.datetime(2016, 7, 24, 12, 22), ';-)'],
     ])
-def test_dateperiod_init_fail(start, end):
+def test_dateperiod_init_start_end_fail(start, end):
     with pytest.raises(TypeError):
         ioex.DatePeriod(start = start, end = end)
 
+@pytest.mark.parametrize(('start', 'end', 'iso'), [
+    [
+        datetime.datetime(2016, 7, 24, 12, 20, 0, microsecond = 25500),
+        datetime.datetime(2016, 7, 24, 12, 21, 0, microsecond = 130000, tzinfo = pytz.utc),
+        '2016-07-24T12:20:00.0255/2016-07-24T12:21:00.13Z',
+        ],
+    ])
+def test_dateperiod_init_isoformat(start, end, iso):
+    p = ioex.DatePeriod(isoformat = iso)
+    assert p.start == start
+    assert p.end == end
+
+@pytest.mark.parametrize(('params'), [
+    {
+        'start': datetime.datetime(2016, 7, 24, 12, 20, 0),
+        'end': datetime.datetime(2016, 7, 24, 12, 21, 0),
+        'isoformat': '2016-07-24T12:20:00Z/2016-07-24T12:21:00Z',
+        },
+    {
+        'start': datetime.datetime(2016, 7, 24, 12, 20, 0),
+        'isoformat': '2016-07-24T12:20:00Z/2016-07-24T12:21:00Z',
+        },
+    {
+        'end': datetime.datetime(2016, 7, 24, 12, 21, 0),
+        'isoformat': '2016-07-24T12:20:00Z/2016-07-24T12:21:00Z',
+        },
+    ])
+def test_dateperiod_init_param_fail(params):
+    with pytest.raises(StandardError):
+        ioex.DatePeriod(**params)
+
 @pytest.mark.parametrize(('start'), [
     datetime.datetime(2016, 7, 24, 12, 21),
     None,
@@ -119,3 +150,49 @@ def test_dateperiod_set_end_fail(end):
 def test_dateperiod_get_isoformat(start, end, iso):
     p = ioex.DatePeriod(start = start, end = end)
     assert p.isoformat == iso
+
+@pytest.mark.parametrize(('start', 'end', 'iso'), [
+    [
+        datetime.datetime(2016, 7, 24, 12, 21, 0),
+        datetime.datetime(2016, 7, 24, 12, 22, 13),
+        '2016-07-24T12:21:00/2016-07-24T12:22:13',
+        ],
+    [
+        datetime.datetime(2016, 7, 24, 12, 21, 0, tzinfo = pytz.utc),
+        datetime.datetime(2016, 7, 24, 12, 22, 13, tzinfo = pytz.utc),
+        '2016-07-24T12:21:00Z/2016-07-24T12:22:13Z',
+        ],
+    [
+        datetime.datetime(2016, 7, 24, 12, 21, 0, tzinfo = pytz.utc),
+        pytz.timezone('Europe/Vienna').localize(datetime.datetime(2016, 7, 24, 12, 22, 13)),
+        '2016-07-24T12:21:00Z/2016-07-24T12:22:13+02:00',
+        ],
+    [
+        pytz.timezone('US/Pacific').localize(datetime.datetime(2016, 1, 12, 12, 22, 13)),
+        pytz.timezone('Europe/London').localize(datetime.datetime(2016, 1, 24, 12, 22, 13)),
+        '2016-01-12T12:22:13-08:00/2016-01-24T12:22:13Z',
+        ],
+    [
+        datetime.datetime(2016, 7, 24, 12, 20, 0, microsecond = 25500),
+        datetime.datetime(2016, 7, 24, 12, 21, 0, microsecond = 13, tzinfo = pytz.utc),
+        '2016-07-24T12:20:00.025500/2016-07-24T12:21:00.000013Z',
+        ],
+    [
+        datetime.datetime(2016, 7, 24, 12, 20, 0, microsecond = 25500),
+        datetime.datetime(2016, 7, 24, 12, 21, 0, microsecond = 130000, tzinfo = pytz.utc),
+        '2016-07-24T12:20:00.0255/2016-07-24T12:21:00.13Z',
+        ],
+    ])
+def test_dateperiod_set_isoformat(start, end, iso):
+    p = ioex.DatePeriod()
+    p.isoformat = iso
+    assert p.start == start
+    assert p.end == end
+
+@pytest.mark.parametrize(('iso'), [
+    '2016-07-24T12:20:0<INVALID>0.0255/2016-07-24T12:21:00.13Z',
+    ])
+def test_dateperiod_set_isoformat_fail(iso):
+    p = ioex.DatePeriod()
+    with pytest.raises(ValueError):
+        p.isoformat = iso