summaryrefslogtreecommitdiff
path: root/Windows/dateutil/test/test_isoparser.py
diff options
context:
space:
mode:
Diffstat (limited to 'Windows/dateutil/test/test_isoparser.py')
-rw-r--r--Windows/dateutil/test/test_isoparser.py516
1 files changed, 516 insertions, 0 deletions
diff --git a/Windows/dateutil/test/test_isoparser.py b/Windows/dateutil/test/test_isoparser.py
new file mode 100644
index 00000000..ecd6e84a
--- /dev/null
+++ b/Windows/dateutil/test/test_isoparser.py
@@ -0,0 +1,516 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from datetime import datetime, timedelta, date, time
+import itertools as it
+
+from dateutil.tz import tz
+from dateutil.parser import isoparser, isoparse
+
+import pytest
+import six
+
+UTC = tz.tzutc()
+
+def _generate_tzoffsets(limited):
+ def _mkoffset(hmtuple, fmt):
+ h, m = hmtuple
+ m_td = (-1 if h < 0 else 1) * m
+
+ tzo = tz.tzoffset(None, timedelta(hours=h, minutes=m_td))
+ return tzo, fmt.format(h, m)
+
+ out = []
+ if not limited:
+ # The subset that's just hours
+ hm_out_h = [(h, 0) for h in (-23, -5, 0, 5, 23)]
+ out.extend([_mkoffset(hm, '{:+03d}') for hm in hm_out_h])
+
+ # Ones that have hours and minutes
+ hm_out = [] + hm_out_h
+ hm_out += [(-12, 15), (11, 30), (10, 2), (5, 15), (-5, 30)]
+ else:
+ hm_out = [(-5, -0)]
+
+ fmts = ['{:+03d}:{:02d}', '{:+03d}{:02d}']
+ out += [_mkoffset(hm, fmt) for hm in hm_out for fmt in fmts]
+
+ # Also add in UTC and naive
+ out.append((tz.tzutc(), 'Z'))
+ out.append((None, ''))
+
+ return out
+
+FULL_TZOFFSETS = _generate_tzoffsets(False)
+FULL_TZOFFSETS_AWARE = [x for x in FULL_TZOFFSETS if x[1]]
+TZOFFSETS = _generate_tzoffsets(True)
+
+DATES = [datetime(1996, 1, 1), datetime(2017, 1, 1)]
+@pytest.mark.parametrize('dt', tuple(DATES))
+def test_year_only(dt):
+ dtstr = dt.strftime('%Y')
+
+ assert isoparse(dtstr) == dt
+
+DATES += [datetime(2000, 2, 1), datetime(2017, 4, 1)]
+@pytest.mark.parametrize('dt', tuple(DATES))
+def test_year_month(dt):
+ fmt = '%Y-%m'
+ dtstr = dt.strftime(fmt)
+
+ assert isoparse(dtstr) == dt
+
+DATES += [datetime(2016, 2, 29), datetime(2018, 3, 15)]
+YMD_FMTS = ('%Y%m%d', '%Y-%m-%d')
+@pytest.mark.parametrize('dt', tuple(DATES))
+@pytest.mark.parametrize('fmt', YMD_FMTS)
+def test_year_month_day(dt, fmt):
+ dtstr = dt.strftime(fmt)
+
+ assert isoparse(dtstr) == dt
+
+def _isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset,
+ microsecond_precision=None):
+ tzi, offset_str = tzoffset
+ fmt = date_fmt + 'T' + time_fmt
+ dt = dt.replace(tzinfo=tzi)
+ dtstr = dt.strftime(fmt)
+
+ if microsecond_precision is not None:
+ if not fmt.endswith('%f'):
+ raise ValueError('Time format has no microseconds!')
+
+ if microsecond_precision != 6:
+ dtstr = dtstr[:-(6 - microsecond_precision)]
+ elif microsecond_precision > 6:
+ raise ValueError('Precision must be 1-6')
+
+ dtstr += offset_str
+
+ assert isoparse(dtstr) == dt
+
+DATETIMES = [datetime(1998, 4, 16, 12),
+ datetime(2019, 11, 18, 23),
+ datetime(2014, 12, 16, 4)]
+@pytest.mark.parametrize('dt', tuple(DATETIMES))
+@pytest.mark.parametrize('date_fmt', YMD_FMTS)
+@pytest.mark.parametrize('tzoffset', TZOFFSETS)
+def test_ymd_h(dt, date_fmt, tzoffset):
+ _isoparse_date_and_time(dt, date_fmt, '%H', tzoffset)
+
+DATETIMES = [datetime(2012, 1, 6, 9, 37)]
+@pytest.mark.parametrize('dt', tuple(DATETIMES))
+@pytest.mark.parametrize('date_fmt', YMD_FMTS)
+@pytest.mark.parametrize('time_fmt', ('%H%M', '%H:%M'))
+@pytest.mark.parametrize('tzoffset', TZOFFSETS)
+def test_ymd_hm(dt, date_fmt, time_fmt, tzoffset):
+ _isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset)
+
+DATETIMES = [datetime(2003, 9, 2, 22, 14, 2),
+ datetime(2003, 8, 8, 14, 9, 14),
+ datetime(2003, 4, 7, 6, 14, 59)]
+HMS_FMTS = ('%H%M%S', '%H:%M:%S')
+@pytest.mark.parametrize('dt', tuple(DATETIMES))
+@pytest.mark.parametrize('date_fmt', YMD_FMTS)
+@pytest.mark.parametrize('time_fmt', HMS_FMTS)
+@pytest.mark.parametrize('tzoffset', TZOFFSETS)
+def test_ymd_hms(dt, date_fmt, time_fmt, tzoffset):
+ _isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset)
+
+DATETIMES = [datetime(2017, 11, 27, 6, 14, 30, 123456)]
+@pytest.mark.parametrize('dt', tuple(DATETIMES))
+@pytest.mark.parametrize('date_fmt', YMD_FMTS)
+@pytest.mark.parametrize('time_fmt', (x + sep + '%f' for x in HMS_FMTS
+ for sep in '.,'))
+@pytest.mark.parametrize('tzoffset', TZOFFSETS)
+@pytest.mark.parametrize('precision', list(range(3, 7)))
+def test_ymd_hms_micro(dt, date_fmt, time_fmt, tzoffset, precision):
+ # Truncate the microseconds to the desired precision for the representation
+ dt = dt.replace(microsecond=int(round(dt.microsecond, precision-6)))
+
+ _isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset, precision)
+
+###
+# Truncation of extra digits beyond microsecond precision
+@pytest.mark.parametrize('dt_str', [
+ '2018-07-03T14:07:00.123456000001',
+ '2018-07-03T14:07:00.123456999999',
+])
+def test_extra_subsecond_digits(dt_str):
+ assert isoparse(dt_str) == datetime(2018, 7, 3, 14, 7, 0, 123456)
+
+@pytest.mark.parametrize('tzoffset', FULL_TZOFFSETS)
+def test_full_tzoffsets(tzoffset):
+ dt = datetime(2017, 11, 27, 6, 14, 30, 123456)
+ date_fmt = '%Y-%m-%d'
+ time_fmt = '%H:%M:%S.%f'
+
+ _isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset)
+
+@pytest.mark.parametrize('dt_str', [
+ '2014-04-11T00',
+ '2014-04-10T24',
+ '2014-04-11T00:00',
+ '2014-04-10T24:00',
+ '2014-04-11T00:00:00',
+ '2014-04-10T24:00:00',
+ '2014-04-11T00:00:00.000',
+ '2014-04-10T24:00:00.000',
+ '2014-04-11T00:00:00.000000',
+ '2014-04-10T24:00:00.000000']
+)
+def test_datetime_midnight(dt_str):
+ assert isoparse(dt_str) == datetime(2014, 4, 11, 0, 0, 0, 0)
+
+@pytest.mark.parametrize('datestr', [
+ '2014-01-01',
+ '20140101',
+])
+@pytest.mark.parametrize('sep', [' ', 'a', 'T', '_', '-'])
+def test_isoparse_sep_none(datestr, sep):
+ isostr = datestr + sep + '14:33:09'
+ assert isoparse(isostr) == datetime(2014, 1, 1, 14, 33, 9)
+
+##
+# Uncommon date formats
+TIME_ARGS = ('time_args',
+ ((None, time(0), None), ) + tuple(('%H:%M:%S.%f', _t, _tz)
+ for _t, _tz in it.product([time(0), time(9, 30), time(14, 47)],
+ TZOFFSETS)))
+
+@pytest.mark.parametrize('isocal,dt_expected',[
+ ((2017, 10), datetime(2017, 3, 6)),
+ ((2020, 1), datetime(2019, 12, 30)), # ISO year != Cal year
+ ((2004, 53), datetime(2004, 12, 27)), # Only half the week is in 2014
+])
+def test_isoweek(isocal, dt_expected):
+ # TODO: Figure out how to parametrize this on formats, too
+ for fmt in ('{:04d}-W{:02d}', '{:04d}W{:02d}'):
+ dtstr = fmt.format(*isocal)
+ assert isoparse(dtstr) == dt_expected
+
+@pytest.mark.parametrize('isocal,dt_expected',[
+ ((2016, 13, 7), datetime(2016, 4, 3)),
+ ((2004, 53, 7), datetime(2005, 1, 2)), # ISO year != Cal year
+ ((2009, 1, 2), datetime(2008, 12, 30)), # ISO year < Cal year
+ ((2009, 53, 6), datetime(2010, 1, 2)) # ISO year > Cal year
+])
+def test_isoweek_day(isocal, dt_expected):
+ # TODO: Figure out how to parametrize this on formats, too
+ for fmt in ('{:04d}-W{:02d}-{:d}', '{:04d}W{:02d}{:d}'):
+ dtstr = fmt.format(*isocal)
+ assert isoparse(dtstr) == dt_expected
+
+@pytest.mark.parametrize('isoord,dt_expected', [
+ ((2004, 1), datetime(2004, 1, 1)),
+ ((2016, 60), datetime(2016, 2, 29)),
+ ((2017, 60), datetime(2017, 3, 1)),
+ ((2016, 366), datetime(2016, 12, 31)),
+ ((2017, 365), datetime(2017, 12, 31))
+])
+def test_iso_ordinal(isoord, dt_expected):
+ for fmt in ('{:04d}-{:03d}', '{:04d}{:03d}'):
+ dtstr = fmt.format(*isoord)
+
+ assert isoparse(dtstr) == dt_expected
+
+
+###
+# Acceptance of bytes
+@pytest.mark.parametrize('isostr,dt', [
+ (b'2014', datetime(2014, 1, 1)),
+ (b'20140204', datetime(2014, 2, 4)),
+ (b'2014-02-04', datetime(2014, 2, 4)),
+ (b'2014-02-04T12', datetime(2014, 2, 4, 12)),
+ (b'2014-02-04T12:30', datetime(2014, 2, 4, 12, 30)),
+ (b'2014-02-04T12:30:15', datetime(2014, 2, 4, 12, 30, 15)),
+ (b'2014-02-04T12:30:15.224', datetime(2014, 2, 4, 12, 30, 15, 224000)),
+ (b'20140204T123015.224', datetime(2014, 2, 4, 12, 30, 15, 224000)),
+ (b'2014-02-04T12:30:15.224Z', datetime(2014, 2, 4, 12, 30, 15, 224000,
+ tz.tzutc())),
+ (b'2014-02-04T12:30:15.224z', datetime(2014, 2, 4, 12, 30, 15, 224000,
+ tz.tzutc())),
+ (b'2014-02-04T12:30:15.224+05:00',
+ datetime(2014, 2, 4, 12, 30, 15, 224000,
+ tzinfo=tz.tzoffset(None, timedelta(hours=5))))])
+def test_bytes(isostr, dt):
+ assert isoparse(isostr) == dt
+
+
+###
+# Invalid ISO strings
+@pytest.mark.parametrize('isostr,exception', [
+ ('201', ValueError), # ISO string too short
+ ('2012-0425', ValueError), # Inconsistent date separators
+ ('201204-25', ValueError), # Inconsistent date separators
+ ('20120425T0120:00', ValueError), # Inconsistent time separators
+ ('20120425T012500-334', ValueError), # Wrong microsecond separator
+ ('2001-1', ValueError), # YYYY-M not valid
+ ('2012-04-9', ValueError), # YYYY-MM-D not valid
+ ('201204', ValueError), # YYYYMM not valid
+ ('20120411T03:30+', ValueError), # Time zone too short
+ ('20120411T03:30+1234567', ValueError), # Time zone too long
+ ('20120411T03:30-25:40', ValueError), # Time zone invalid
+ ('2012-1a', ValueError), # Invalid month
+ ('20120411T03:30+00:60', ValueError), # Time zone invalid minutes
+ ('20120411T03:30+00:61', ValueError), # Time zone invalid minutes
+ ('20120411T033030.123456012:00', # No sign in time zone
+ ValueError),
+ ('2012-W00', ValueError), # Invalid ISO week
+ ('2012-W55', ValueError), # Invalid ISO week
+ ('2012-W01-0', ValueError), # Invalid ISO week day
+ ('2012-W01-8', ValueError), # Invalid ISO week day
+ ('2013-000', ValueError), # Invalid ordinal day
+ ('2013-366', ValueError), # Invalid ordinal day
+ ('2013366', ValueError), # Invalid ordinal day
+ ('2014-03-12Т12:30:14', ValueError), # Cyrillic T
+ ('2014-04-21T24:00:01', ValueError), # Invalid use of 24 for midnight
+ ('2014_W01-1', ValueError), # Invalid separator
+ ('2014W01-1', ValueError), # Inconsistent use of dashes
+ ('2014-W011', ValueError), # Inconsistent use of dashes
+
+])
+def test_iso_raises(isostr, exception):
+ with pytest.raises(exception):
+ isoparse(isostr)
+
+
+@pytest.mark.parametrize('sep_act, valid_sep, exception', [
+ ('T', 'C', ValueError),
+ ('C', 'T', ValueError),
+])
+def test_iso_with_sep_raises(sep_act, valid_sep, exception):
+ parser = isoparser(sep=valid_sep)
+ isostr = '2012-04-25' + sep_act + '01:25:00'
+ with pytest.raises(exception):
+ parser.isoparse(isostr)
+
+
+@pytest.mark.xfail()
+@pytest.mark.parametrize('isostr,exception', [
+ ('20120425T01:2000', ValueError), # Inconsistent time separators
+])
+def test_iso_raises_failing(isostr, exception):
+ # These are test cases where the current implementation is too lenient
+ # and need to be fixed
+ with pytest.raises(exception):
+ isoparse(isostr)
+
+
+###
+# Test ISOParser constructor
+@pytest.mark.parametrize('sep', [' ', '9', '🍛'])
+def test_isoparser_invalid_sep(sep):
+ with pytest.raises(ValueError):
+ isoparser(sep=sep)
+
+
+# This only fails on Python 3
+@pytest.mark.xfail(six.PY3, reason="Fails on Python 3 only")
+def test_isoparser_byte_sep():
+ dt = datetime(2017, 12, 6, 12, 30, 45)
+ dt_str = dt.isoformat(sep=str('T'))
+
+ dt_rt = isoparser(sep=b'T').isoparse(dt_str)
+
+ assert dt == dt_rt
+
+
+###
+# Test parse_tzstr
+@pytest.mark.parametrize('tzoffset', FULL_TZOFFSETS)
+def test_parse_tzstr(tzoffset):
+ dt = datetime(2017, 11, 27, 6, 14, 30, 123456)
+ date_fmt = '%Y-%m-%d'
+ time_fmt = '%H:%M:%S.%f'
+
+ _isoparse_date_and_time(dt, date_fmt, time_fmt, tzoffset)
+
+
+@pytest.mark.parametrize('tzstr', [
+ '-00:00', '+00:00', '+00', '-00', '+0000', '-0000'
+])
+@pytest.mark.parametrize('zero_as_utc', [True, False])
+def test_parse_tzstr_zero_as_utc(tzstr, zero_as_utc):
+ tzi = isoparser().parse_tzstr(tzstr, zero_as_utc=zero_as_utc)
+ assert tzi == tz.tzutc()
+ assert (type(tzi) == tz.tzutc) == zero_as_utc
+
+
+@pytest.mark.parametrize('tzstr,exception', [
+ ('00:00', ValueError), # No sign
+ ('05:00', ValueError), # No sign
+ ('_00:00', ValueError), # Invalid sign
+ ('+25:00', ValueError), # Offset too large
+ ('00:0000', ValueError), # String too long
+])
+def test_parse_tzstr_fails(tzstr, exception):
+ with pytest.raises(exception):
+ isoparser().parse_tzstr(tzstr)
+
+###
+# Test parse_isodate
+def __make_date_examples():
+ dates_no_day = [
+ date(1999, 12, 1),
+ date(2016, 2, 1)
+ ]
+
+ if six.PY3:
+ # strftime does not support dates before 1900 in Python 2
+ dates_no_day.append(date(1000, 11, 1))
+
+ # Only one supported format for dates with no day
+ o = zip(dates_no_day, it.repeat('%Y-%m'))
+
+ dates_w_day = [
+ date(1969, 12, 31),
+ date(1900, 1, 1),
+ date(2016, 2, 29),
+ date(2017, 11, 14)
+ ]
+
+ dates_w_day_fmts = ('%Y%m%d', '%Y-%m-%d')
+ o = it.chain(o, it.product(dates_w_day, dates_w_day_fmts))
+
+ return list(o)
+
+
+@pytest.mark.parametrize('d,dt_fmt', __make_date_examples())
+@pytest.mark.parametrize('as_bytes', [True, False])
+def test_parse_isodate(d, dt_fmt, as_bytes):
+ d_str = d.strftime(dt_fmt)
+ if isinstance(d_str, six.text_type) and as_bytes:
+ d_str = d_str.encode('ascii')
+ elif isinstance(d_str, bytes) and not as_bytes:
+ d_str = d_str.decode('ascii')
+
+ iparser = isoparser()
+ assert iparser.parse_isodate(d_str) == d
+
+
+@pytest.mark.parametrize('isostr,exception', [
+ ('243', ValueError), # ISO string too short
+ ('2014-0423', ValueError), # Inconsistent date separators
+ ('201404-23', ValueError), # Inconsistent date separators
+ ('2014日03月14', ValueError), # Not ASCII
+ ('2013-02-29', ValueError), # Not a leap year
+ ('2014/12/03', ValueError), # Wrong separators
+ ('2014-04-19T', ValueError), # Unknown components
+])
+def test_isodate_raises(isostr, exception):
+ with pytest.raises(exception):
+ isoparser().parse_isodate(isostr)
+
+
+###
+# Test parse_isotime
+def __make_time_examples():
+ outputs = []
+
+ # HH
+ time_h = [time(0), time(8), time(22)]
+ time_h_fmts = ['%H']
+
+ outputs.append(it.product(time_h, time_h_fmts))
+
+ # HHMM / HH:MM
+ time_hm = [time(0, 0), time(0, 30), time(8, 47), time(16, 1)]
+ time_hm_fmts = ['%H%M', '%H:%M']
+
+ outputs.append(it.product(time_hm, time_hm_fmts))
+
+ # HHMMSS / HH:MM:SS
+ time_hms = [time(0, 0, 0), time(0, 15, 30),
+ time(8, 2, 16), time(12, 0), time(16, 2), time(20, 45)]
+
+ time_hms_fmts = ['%H%M%S', '%H:%M:%S']
+
+ outputs.append(it.product(time_hms, time_hms_fmts))
+
+ # HHMMSS.ffffff / HH:MM:SS.ffffff
+ time_hmsu = [time(0, 0, 0, 0), time(4, 15, 3, 247993),
+ time(14, 21, 59, 948730),
+ time(23, 59, 59, 999999)]
+
+ time_hmsu_fmts = ['%H%M%S.%f', '%H:%M:%S.%f']
+
+ outputs.append(it.product(time_hmsu, time_hmsu_fmts))
+
+ outputs = list(map(list, outputs))
+
+ # Time zones
+ ex_naive = list(it.chain.from_iterable(x[0:2] for x in outputs))
+ o = it.product(ex_naive, TZOFFSETS) # ((time, fmt), (tzinfo, offsetstr))
+ o = ((t.replace(tzinfo=tzi), fmt + off_str)
+ for (t, fmt), (tzi, off_str) in o)
+
+ outputs.append(o)
+
+ return list(it.chain.from_iterable(outputs))
+
+
+@pytest.mark.parametrize('time_val,time_fmt', __make_time_examples())
+@pytest.mark.parametrize('as_bytes', [True, False])
+def test_isotime(time_val, time_fmt, as_bytes):
+ tstr = time_val.strftime(time_fmt)
+ if isinstance(time_val, six.text_type) and as_bytes:
+ tstr = tstr.encode('ascii')
+ elif isinstance(time_val, bytes) and not as_bytes:
+ tstr = tstr.decode('ascii')
+
+ iparser = isoparser()
+
+ assert iparser.parse_isotime(tstr) == time_val
+
+
+@pytest.mark.parametrize('isostr', [
+ '24:00',
+ '2400',
+ '24:00:00',
+ '240000',
+ '24:00:00.000',
+ '24:00:00,000',
+ '24:00:00.000000',
+ '24:00:00,000000',
+])
+def test_isotime_midnight(isostr):
+ iparser = isoparser()
+ assert iparser.parse_isotime(isostr) == time(0, 0, 0, 0)
+
+
+@pytest.mark.parametrize('isostr,exception', [
+ ('3', ValueError), # ISO string too short
+ ('14時30分15秒', ValueError), # Not ASCII
+ ('14_30_15', ValueError), # Invalid separators
+ ('1430:15', ValueError), # Inconsistent separator use
+ ('25', ValueError), # Invalid hours
+ ('25:15', ValueError), # Invalid hours
+ ('14:60', ValueError), # Invalid minutes
+ ('14:59:61', ValueError), # Invalid seconds
+ ('14:30:15.34468305:00', ValueError), # No sign in time zone
+ ('14:30:15+', ValueError), # Time zone too short
+ ('14:30:15+1234567', ValueError), # Time zone invalid
+ ('14:59:59+25:00', ValueError), # Invalid tz hours
+ ('14:59:59+12:62', ValueError), # Invalid tz minutes
+ ('14:59:30_344583', ValueError), # Invalid microsecond separator
+ ('24:01', ValueError), # 24 used for non-midnight time
+ ('24:00:01', ValueError), # 24 used for non-midnight time
+ ('24:00:00.001', ValueError), # 24 used for non-midnight time
+ ('24:00:00.000001', ValueError), # 24 used for non-midnight time
+])
+def test_isotime_raises(isostr, exception):
+ iparser = isoparser()
+ with pytest.raises(exception):
+ iparser.parse_isotime(isostr)
+
+
+@pytest.mark.xfail()
+@pytest.mark.parametrize('isostr,exception', [
+ ('14:3015', ValueError), # Inconsistent separator use
+ ('201202', ValueError) # Invalid ISO format
+])
+def test_isotime_raises_xfail(isostr, exception):
+ iparser = isoparser()
+ with pytest.raises(exception):
+ iparser.parse_isotime(isostr)