diff --git a/intervals/interval.py b/intervals/interval.py index b2cf48d..bec1561 100644 --- a/intervals/interval.py +++ b/intervals/interval.py @@ -8,12 +8,45 @@ # -*- coding: utf-8 -*- import operator +import sys from datetime import date, datetime, timedelta from decimal import Decimal from math import ceil, floor from infinity import inf, is_infinite +try: + import arrow + + def date_from_iso(s): + return arrow.get(s).naive.date() + + def datetime_from_iso(s): + return arrow.get(s).naive + +except ImportError: + if ( + sys.version_info.major > 3 or + (sys.version_info.major == 3 and sys.version_info.minor >= 7) + ): + def date_from_iso(s): + return date.fromisoformat(s) + + def datetime_from_iso(s): + return datetime.fromisoformat(s) + else: + def date_from_iso(s): + return datetime.strptime(s, '%Y-%m-%d').date() + + def datetime_from_iso(s): + n = len(s) + if n == 10: + return datetime.strptime(s, '%Y-%m-%d') + elif n == 19: + return datetime.strptime(s, '%Y-%m-%d %H:%M:%S') + elif n > 19: + return datetime.strptime(s, '%Y-%m-%d %H:%M:%S.%f') + from .exc import IllegalArgument, IntervalException, RangeBoundsException from .parser import IntervalParser, IntervalStringParser @@ -725,10 +758,16 @@ class DateInterval(AbstractInterval): step = timedelta(days=1) type = date + def coerce_string(self, value): + return date_from_iso(value) + class DateTimeInterval(AbstractInterval): type = datetime + def coerce_string(self, value): + return datetime_from_iso(value) + class FloatInterval(NumberInterval): type = float diff --git a/tests/interval/test_initialization.py b/tests/interval/test_initialization.py index c28e582..eae660b 100644 --- a/tests/interval/test_initialization.py +++ b/tests/interval/test_initialization.py @@ -1,4 +1,5 @@ from datetime import date +from datetime import datetime from decimal import Decimal from infinity import inf @@ -6,11 +7,13 @@ from intervals import ( CharacterInterval, + DateInterval, + DateTimeInterval, DecimalInterval, FloatInterval, IllegalArgument, - Interval, IntInterval, + Interval, RangeBoundsException ) @@ -186,6 +189,29 @@ def test_raises_exception_for_badly_constructed_range( with raises(RangeBoundsException): constructor(number_range) + def test_dateinterval_fromstring(self): + s = '[2020-01-02, 2020-03-04]' + interval = DateInterval.from_string(s) + assert interval.lower == date(2020, 1, 2) + assert interval.upper == date(2020, 3, 4) + assert interval.lower_inc + assert interval.upper_inc + + def test_datetimeinterval_fromstring(self): + s = '[2020-01-02 03:04:05, 2020-06-07 08:09:10.000001]' + interval = DateTimeInterval.from_string(s) + assert interval.lower == datetime(2020, 1, 2, 3, 4, 5) + assert interval.upper == datetime(2020, 6, 7, 8, 9, 10, 1) + assert interval.lower_inc + assert interval.upper_inc + # without time part + s = '[2020-01-02, 2020-06-07]' + interval = DateTimeInterval.from_string(s) + assert interval.lower == datetime(2020, 1, 2, 0, 0, 0) + assert interval.upper == datetime(2020, 6, 7, 0, 0, 0) + assert interval.lower_inc + assert interval.upper_inc + class TestTypeGuessing(object): @mark.parametrize(