bazarr/libs/aniso8601/builders/tests/test_python.py

1711 lines
60 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
import datetime
import unittest
from aniso8601 import compat
from aniso8601.builders import (
DatetimeTuple,
DateTuple,
DurationTuple,
IntervalTuple,
Limit,
TimeTuple,
TimezoneTuple,
)
from aniso8601.builders.python import (
FractionalComponent,
PythonTimeBuilder,
_cast_to_fractional_component,
fractional_range_check,
year_range_check,
)
from aniso8601.exceptions import (
DayOutOfBoundsError,
HoursOutOfBoundsError,
ISOFormatError,
LeapSecondError,
MidnightBoundsError,
MinutesOutOfBoundsError,
MonthOutOfBoundsError,
SecondsOutOfBoundsError,
WeekOutOfBoundsError,
YearOutOfBoundsError,
)
from aniso8601.utcoffset import UTCOffset
class TestPythonTimeBuilder_UtiltyFunctions(unittest.TestCase):
def test_year_range_check(self):
yearlimit = Limit(
"Invalid year string.",
0000,
9999,
YearOutOfBoundsError,
"Year must be between 1..9999.",
None,
)
self.assertEqual(year_range_check("1", yearlimit), 1000)
def test_fractional_range_check(self):
limit = Limit(
"Invalid string.", -1, 1, ValueError, "Value must be between -1..1.", None
)
self.assertEqual(fractional_range_check(10, "1", limit), 1)
self.assertEqual(fractional_range_check(10, "-1", limit), -1)
self.assertEqual(
fractional_range_check(10, "0.1", limit), FractionalComponent(0, 1)
)
self.assertEqual(
fractional_range_check(10, "-0.1", limit), FractionalComponent(-0, 1)
)
with self.assertRaises(ValueError):
fractional_range_check(10, "1.1", limit)
with self.assertRaises(ValueError):
fractional_range_check(10, "-1.1", limit)
def test_cast_to_fractional_component(self):
self.assertEqual(
_cast_to_fractional_component(10, "1.1"), FractionalComponent(1, 1)
)
self.assertEqual(
_cast_to_fractional_component(10, "-1.1"), FractionalComponent(-1, 1)
)
self.assertEqual(
_cast_to_fractional_component(100, "1.1"), FractionalComponent(1, 10)
)
self.assertEqual(
_cast_to_fractional_component(100, "-1.1"), FractionalComponent(-1, 10)
)
class TestPythonTimeBuilder(unittest.TestCase):
def test_build_date(self):
testtuples = (
(
{
"YYYY": "2013",
"MM": None,
"DD": None,
"Www": None,
"D": None,
"DDD": None,
},
datetime.date(2013, 1, 1),
),
(
{
"YYYY": "0001",
"MM": None,
"DD": None,
"Www": None,
"D": None,
"DDD": None,
},
datetime.date(1, 1, 1),
),
(
{
"YYYY": "1900",
"MM": None,
"DD": None,
"Www": None,
"D": None,
"DDD": None,
},
datetime.date(1900, 1, 1),
),
(
{
"YYYY": "1981",
"MM": "04",
"DD": "05",
"Www": None,
"D": None,
"DDD": None,
},
datetime.date(1981, 4, 5),
),
(
{
"YYYY": "1981",
"MM": "04",
"DD": None,
"Www": None,
"D": None,
"DDD": None,
},
datetime.date(1981, 4, 1),
),
(
{
"YYYY": "1981",
"MM": None,
"DD": None,
"Www": None,
"D": None,
"DDD": "095",
},
datetime.date(1981, 4, 5),
),
(
{
"YYYY": "1981",
"MM": None,
"DD": None,
"Www": None,
"D": None,
"DDD": "365",
},
datetime.date(1981, 12, 31),
),
(
{
"YYYY": "1980",
"MM": None,
"DD": None,
"Www": None,
"D": None,
"DDD": "366",
},
datetime.date(1980, 12, 31),
),
# Make sure we shift in zeros
(
{
"YYYY": "1",
"MM": None,
"DD": None,
"Www": None,
"D": None,
"DDD": None,
},
datetime.date(1000, 1, 1),
),
(
{
"YYYY": "12",
"MM": None,
"DD": None,
"Www": None,
"D": None,
"DDD": None,
},
datetime.date(1200, 1, 1),
),
(
{
"YYYY": "123",
"MM": None,
"DD": None,
"Www": None,
"D": None,
"DDD": None,
},
datetime.date(1230, 1, 1),
),
)
for testtuple in testtuples:
result = PythonTimeBuilder.build_date(**testtuple[0])
self.assertEqual(result, testtuple[1])
# Test weekday
testtuples = (
(
{
"YYYY": "2004",
"MM": None,
"DD": None,
"Www": "53",
"D": None,
"DDD": None,
},
datetime.date(2004, 12, 27),
0,
),
(
{
"YYYY": "2009",
"MM": None,
"DD": None,
"Www": "01",
"D": None,
"DDD": None,
},
datetime.date(2008, 12, 29),
0,
),
(
{
"YYYY": "2010",
"MM": None,
"DD": None,
"Www": "01",
"D": None,
"DDD": None,
},
datetime.date(2010, 1, 4),
0,
),
(
{
"YYYY": "2009",
"MM": None,
"DD": None,
"Www": "53",
"D": None,
"DDD": None,
},
datetime.date(2009, 12, 28),
0,
),
(
{
"YYYY": "2009",
"MM": None,
"DD": None,
"Www": "01",
"D": "1",
"DDD": None,
},
datetime.date(2008, 12, 29),
0,
),
(
{
"YYYY": "2009",
"MM": None,
"DD": None,
"Www": "53",
"D": "7",
"DDD": None,
},
datetime.date(2010, 1, 3),
6,
),
(
{
"YYYY": "2010",
"MM": None,
"DD": None,
"Www": "01",
"D": "1",
"DDD": None,
},
datetime.date(2010, 1, 4),
0,
),
(
{
"YYYY": "2004",
"MM": None,
"DD": None,
"Www": "53",
"D": "6",
"DDD": None,
},
datetime.date(2005, 1, 1),
5,
),
)
for testtuple in testtuples:
result = PythonTimeBuilder.build_date(**testtuple[0])
self.assertEqual(result, testtuple[1])
self.assertEqual(result.weekday(), testtuple[2])
def test_build_time(self):
testtuples = (
({}, datetime.time()),
({"hh": "12.5"}, datetime.time(hour=12, minute=30)),
(
{"hh": "23.99999999997"},
datetime.time(hour=23, minute=59, second=59, microsecond=999999),
),
({"hh": "1", "mm": "23"}, datetime.time(hour=1, minute=23)),
(
{"hh": "1", "mm": "23.4567"},
datetime.time(hour=1, minute=23, second=27, microsecond=402000),
),
(
{"hh": "14", "mm": "43.999999997"},
datetime.time(hour=14, minute=43, second=59, microsecond=999999),
),
(
{"hh": "1", "mm": "23", "ss": "45"},
datetime.time(hour=1, minute=23, second=45),
),
(
{"hh": "23", "mm": "21", "ss": "28.512400"},
datetime.time(hour=23, minute=21, second=28, microsecond=512400),
),
(
{"hh": "01", "mm": "03", "ss": "11.858714"},
datetime.time(hour=1, minute=3, second=11, microsecond=858714),
),
(
{"hh": "14", "mm": "43", "ss": "59.9999997"},
datetime.time(hour=14, minute=43, second=59, microsecond=999999),
),
({"hh": "24"}, datetime.time(hour=0)),
({"hh": "24", "mm": "00"}, datetime.time(hour=0)),
({"hh": "24", "mm": "00", "ss": "00"}, datetime.time(hour=0)),
(
{"tz": TimezoneTuple(False, None, "00", "00", "UTC")},
datetime.time(tzinfo=UTCOffset(name="UTC", minutes=0)),
),
(
{
"hh": "23",
"mm": "21",
"ss": "28.512400",
"tz": TimezoneTuple(False, None, "00", "00", "+00:00"),
},
datetime.time(
hour=23,
minute=21,
second=28,
microsecond=512400,
tzinfo=UTCOffset(name="+00:00", minutes=0),
),
),
(
{
"hh": "1",
"mm": "23",
"tz": TimezoneTuple(False, None, "01", "00", "+1"),
},
datetime.time(
hour=1, minute=23, tzinfo=UTCOffset(name="+1", minutes=60)
),
),
(
{
"hh": "1",
"mm": "23.4567",
"tz": TimezoneTuple(True, None, "01", "00", "-1"),
},
datetime.time(
hour=1,
minute=23,
second=27,
microsecond=402000,
tzinfo=UTCOffset(name="-1", minutes=-60),
),
),
(
{
"hh": "23",
"mm": "21",
"ss": "28.512400",
"tz": TimezoneTuple(False, None, "01", "30", "+1:30"),
},
datetime.time(
hour=23,
minute=21,
second=28,
microsecond=512400,
tzinfo=UTCOffset(name="+1:30", minutes=90),
),
),
(
{
"hh": "23",
"mm": "21",
"ss": "28.512400",
"tz": TimezoneTuple(False, None, "11", "15", "+11:15"),
},
datetime.time(
hour=23,
minute=21,
second=28,
microsecond=512400,
tzinfo=UTCOffset(name="+11:15", minutes=675),
),
),
(
{
"hh": "23",
"mm": "21",
"ss": "28.512400",
"tz": TimezoneTuple(False, None, "12", "34", "+12:34"),
},
datetime.time(
hour=23,
minute=21,
second=28,
microsecond=512400,
tzinfo=UTCOffset(name="+12:34", minutes=754),
),
),
(
{
"hh": "23",
"mm": "21",
"ss": "28.512400",
"tz": TimezoneTuple(False, None, "00", "00", "UTC"),
},
datetime.time(
hour=23,
minute=21,
second=28,
microsecond=512400,
tzinfo=UTCOffset(name="UTC", minutes=0),
),
),
# Make sure we truncate, not round
# https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
# https://bitbucket.org/nielsenb/aniso8601/issues/21/sub-microsecond-precision-is-lost-when
(
{"hh": "14.9999999999999999"},
datetime.time(hour=14, minute=59, second=59, microsecond=999999),
),
({"mm": "0.00000000999"}, datetime.time()),
({"mm": "0.0000000999"}, datetime.time(microsecond=5)),
({"ss": "0.0000001"}, datetime.time()),
({"ss": "2.0000048"}, datetime.time(second=2, microsecond=4)),
)
for testtuple in testtuples:
result = PythonTimeBuilder.build_time(**testtuple[0])
self.assertEqual(result, testtuple[1])
def test_build_datetime(self):
testtuples = (
(
(
DateTuple("2019", "06", "05", None, None, None),
TimeTuple("01", "03", "11.858714", None),
),
datetime.datetime(
2019, 6, 5, hour=1, minute=3, second=11, microsecond=858714
),
),
(
(
DateTuple("1234", "02", "03", None, None, None),
TimeTuple("23", "21", "28.512400", None),
),
datetime.datetime(
1234, 2, 3, hour=23, minute=21, second=28, microsecond=512400
),
),
(
(
DateTuple("1981", "04", "05", None, None, None),
TimeTuple(
"23",
"21",
"28.512400",
TimezoneTuple(False, None, "11", "15", "+11:15"),
),
),
datetime.datetime(
1981,
4,
5,
hour=23,
minute=21,
second=28,
microsecond=512400,
tzinfo=UTCOffset(name="+11:15", minutes=675),
),
),
)
for testtuple in testtuples:
result = PythonTimeBuilder.build_datetime(*testtuple[0])
self.assertEqual(result, testtuple[1])
def test_build_duration(self):
testtuples = (
(
{
"PnY": "1",
"PnM": "2",
"PnD": "3",
"TnH": "4",
"TnM": "54",
"TnS": "6",
},
datetime.timedelta(days=428, hours=4, minutes=54, seconds=6),
),
(
{
"PnY": "1",
"PnM": "2",
"PnD": "3",
"TnH": "4",
"TnM": "54",
"TnS": "6.5",
},
datetime.timedelta(days=428, hours=4, minutes=54, seconds=6.5),
),
({"PnY": "1", "PnM": "2", "PnD": "3"}, datetime.timedelta(days=428)),
({"PnY": "1", "PnM": "2", "PnD": "3.5"}, datetime.timedelta(days=428.5)),
(
{"TnH": "4", "TnM": "54", "TnS": "6.5"},
datetime.timedelta(hours=4, minutes=54, seconds=6.5),
),
(
{"TnH": "1", "TnM": "3", "TnS": "11.858714"},
datetime.timedelta(hours=1, minutes=3, seconds=11, microseconds=858714),
),
(
{"TnH": "4", "TnM": "54", "TnS": "28.512400"},
datetime.timedelta(
hours=4, minutes=54, seconds=28, microseconds=512400
),
),
# Make sure we truncate, not round
# https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
# https://bitbucket.org/nielsenb/aniso8601/issues/21/sub-microsecond-precision-is-lost-when
(
{"PnY": "1999.9999999999999999"},
datetime.timedelta(days=729999, seconds=86399, microseconds=999999),
),
(
{"PnM": "1.9999999999999999"},
datetime.timedelta(
days=59, hours=23, minutes=59, seconds=59, microseconds=999999
),
),
(
{"PnW": "1.9999999999999999"},
datetime.timedelta(
days=13, hours=23, minutes=59, seconds=59, microseconds=999999
),
),
(
{"PnD": "1.9999999999999999"},
datetime.timedelta(
days=1, hours=23, minutes=59, seconds=59, microseconds=999999
),
),
(
{"TnH": "14.9999999999999999"},
datetime.timedelta(
hours=14, minutes=59, seconds=59, microseconds=999999
),
),
({"TnM": "0.00000000999"}, datetime.timedelta(0)),
({"TnM": "0.0000000999"}, datetime.timedelta(microseconds=5)),
({"TnS": "0.0000001"}, datetime.timedelta(0)),
({"TnS": "2.0000048"}, datetime.timedelta(seconds=2, microseconds=4)),
({"PnY": "1"}, datetime.timedelta(days=365)),
({"PnY": "1.5"}, datetime.timedelta(days=547.5)),
({"PnM": "1"}, datetime.timedelta(days=30)),
({"PnM": "1.5"}, datetime.timedelta(days=45)),
({"PnW": "1"}, datetime.timedelta(days=7)),
({"PnW": "1.5"}, datetime.timedelta(days=10.5)),
({"PnD": "1"}, datetime.timedelta(days=1)),
({"PnD": "1.5"}, datetime.timedelta(days=1.5)),
(
{
"PnY": "0003",
"PnM": "06",
"PnD": "04",
"TnH": "12",
"TnM": "30",
"TnS": "05",
},
datetime.timedelta(days=1279, hours=12, minutes=30, seconds=5),
),
(
{
"PnY": "0003",
"PnM": "06",
"PnD": "04",
"TnH": "12",
"TnM": "30",
"TnS": "05.5",
},
datetime.timedelta(days=1279, hours=12, minutes=30, seconds=5.5),
),
# Test timedelta limit
(
{"PnD": "999999999", "TnH": "23", "TnM": "59", "TnS": "59.999999"},
datetime.timedelta.max,
),
# Make sure we truncate, not round
# https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
(
{
"PnY": "0001",
"PnM": "02",
"PnD": "03",
"TnH": "14",
"TnM": "43",
"TnS": "59.9999997",
},
datetime.timedelta(
days=428, hours=14, minutes=43, seconds=59, microseconds=999999
),
),
# Verify overflows
({"TnH": "36"}, datetime.timedelta(days=1, hours=12)),
)
for testtuple in testtuples:
result = PythonTimeBuilder.build_duration(**testtuple[0])
self.assertEqual(result, testtuple[1])
def test_build_interval(self):
testtuples = (
(
{
"end": DatetimeTuple(
DateTuple("1981", "04", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
"duration": DurationTuple(None, "1", None, None, None, None, None),
},
datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1),
datetime.datetime(year=1981, month=3, day=6, hour=1, minute=1),
),
(
{
"end": DateTuple("1981", "04", "05", None, None, None),
"duration": DurationTuple(None, "1", None, None, None, None, None),
},
datetime.date(year=1981, month=4, day=5),
datetime.date(year=1981, month=3, day=6),
),
(
{
"end": DateTuple("2018", "03", "06", None, None, None),
"duration": DurationTuple(
"1.5", None, None, None, None, None, None
),
},
datetime.date(year=2018, month=3, day=6),
datetime.datetime(year=2016, month=9, day=4, hour=12),
),
(
{
"end": DateTuple("2014", "11", "12", None, None, None),
"duration": DurationTuple(None, None, None, None, "1", None, None),
},
datetime.date(year=2014, month=11, day=12),
datetime.datetime(year=2014, month=11, day=11, hour=23),
),
(
{
"end": DateTuple("2014", "11", "12", None, None, None),
"duration": DurationTuple(None, None, None, None, "4", "54", "6.5"),
},
datetime.date(year=2014, month=11, day=12),
datetime.datetime(
year=2014,
month=11,
day=11,
hour=19,
minute=5,
second=53,
microsecond=500000,
),
),
(
{
"end": DatetimeTuple(
DateTuple("2050", "03", "01", None, None, None),
TimeTuple(
"13",
"00",
"00",
TimezoneTuple(False, True, None, None, "Z"),
),
),
"duration": DurationTuple(None, None, None, None, "10", None, None),
},
datetime.datetime(
year=2050,
month=3,
day=1,
hour=13,
tzinfo=UTCOffset(name="UTC", minutes=0),
),
datetime.datetime(
year=2050,
month=3,
day=1,
hour=3,
tzinfo=UTCOffset(name="UTC", minutes=0),
),
),
# Make sure we truncate, not round
# https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
# https://bitbucket.org/nielsenb/aniso8601/issues/21/sub-microsecond-precision-is-lost-when
(
{
"end": DateTuple("2000", "01", "01", None, None, None),
"duration": DurationTuple(
"1999.9999999999999999", None, None, None, None, None, None
),
},
datetime.date(year=2000, month=1, day=1),
datetime.datetime(
year=1, month=4, day=30, hour=0, minute=0, second=0, microsecond=1
),
),
(
{
"end": DateTuple("1989", "03", "01", None, None, None),
"duration": DurationTuple(
None, "1.9999999999999999", None, None, None, None, None
),
},
datetime.date(year=1989, month=3, day=1),
datetime.datetime(
year=1988,
month=12,
day=31,
hour=0,
minute=0,
second=0,
microsecond=1,
),
),
(
{
"end": DateTuple("1989", "03", "01", None, None, None),
"duration": DurationTuple(
None, None, "1.9999999999999999", None, None, None, None
),
},
datetime.date(year=1989, month=3, day=1),
datetime.datetime(
year=1989,
month=2,
day=15,
hour=0,
minute=0,
second=0,
microsecond=1,
),
),
(
{
"end": DateTuple("1989", "03", "01", None, None, None),
"duration": DurationTuple(
None, None, None, "1.9999999999999999", None, None, None
),
},
datetime.date(year=1989, month=3, day=1),
datetime.datetime(
year=1989,
month=2,
day=27,
hour=0,
minute=0,
second=0,
microsecond=1,
),
),
(
{
"end": DateTuple("2001", "01", "01", None, None, None),
"duration": DurationTuple(
None, None, None, None, "14.9999999999999999", None, None
),
},
datetime.date(year=2001, month=1, day=1),
datetime.datetime(
year=2000,
month=12,
day=31,
hour=9,
minute=0,
second=0,
microsecond=1,
),
),
(
{
"end": DateTuple("2001", "01", "01", None, None, None),
"duration": DurationTuple(
None, None, None, None, None, "0.00000000999", None
),
},
datetime.date(year=2001, month=1, day=1),
datetime.datetime(year=2001, month=1, day=1),
),
(
{
"end": DateTuple("2001", "01", "01", None, None, None),
"duration": DurationTuple(
None, None, None, None, None, "0.0000000999", None
),
},
datetime.date(year=2001, month=1, day=1),
datetime.datetime(
year=2000,
month=12,
day=31,
hour=23,
minute=59,
second=59,
microsecond=999995,
),
),
(
{
"end": DateTuple("2018", "03", "06", None, None, None),
"duration": DurationTuple(
None, None, None, None, None, None, "0.0000001"
),
},
datetime.date(year=2018, month=3, day=6),
datetime.datetime(year=2018, month=3, day=6),
),
(
{
"end": DateTuple("2018", "03", "06", None, None, None),
"duration": DurationTuple(
None, None, None, None, None, None, "2.0000048"
),
},
datetime.date(year=2018, month=3, day=6),
datetime.datetime(
year=2018,
month=3,
day=5,
hour=23,
minute=59,
second=57,
microsecond=999996,
),
),
(
{
"start": DatetimeTuple(
DateTuple("1981", "04", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
"duration": DurationTuple(None, "1", None, "1", None, "1", None),
},
datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1),
datetime.datetime(year=1981, month=5, day=6, hour=1, minute=2),
),
(
{
"start": DateTuple("1981", "04", "05", None, None, None),
"duration": DurationTuple(None, "1", None, "1", None, None, None),
},
datetime.date(year=1981, month=4, day=5),
datetime.date(year=1981, month=5, day=6),
),
(
{
"start": DateTuple("2018", "03", "06", None, None, None),
"duration": DurationTuple(
None, "2.5", None, None, None, None, None
),
},
datetime.date(year=2018, month=3, day=6),
datetime.date(year=2018, month=5, day=20),
),
(
{
"start": DateTuple("2014", "11", "12", None, None, None),
"duration": DurationTuple(None, None, None, None, "1", None, None),
},
datetime.date(year=2014, month=11, day=12),
datetime.datetime(year=2014, month=11, day=12, hour=1, minute=0),
),
(
{
"start": DateTuple("2014", "11", "12", None, None, None),
"duration": DurationTuple(None, None, None, None, "4", "54", "6.5"),
},
datetime.date(year=2014, month=11, day=12),
datetime.datetime(
year=2014,
month=11,
day=12,
hour=4,
minute=54,
second=6,
microsecond=500000,
),
),
(
{
"start": DatetimeTuple(
DateTuple("2050", "03", "01", None, None, None),
TimeTuple(
"13",
"00",
"00",
TimezoneTuple(False, True, None, None, "Z"),
),
),
"duration": DurationTuple(None, None, None, None, "10", None, None),
},
datetime.datetime(
year=2050,
month=3,
day=1,
hour=13,
tzinfo=UTCOffset(name="UTC", minutes=0),
),
datetime.datetime(
year=2050,
month=3,
day=1,
hour=23,
tzinfo=UTCOffset(name="UTC", minutes=0),
),
),
# Make sure we truncate, not round
# https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
(
{
"start": DateTuple("0001", "01", "01", None, None, None),
"duration": DurationTuple(
"1999.9999999999999999", None, None, None, None, None, None
),
},
datetime.date(year=1, month=1, day=1),
datetime.datetime(
year=1999,
month=9,
day=3,
hour=23,
minute=59,
second=59,
microsecond=999999,
),
),
(
{
"start": DateTuple("1989", "03", "01", None, None, None),
"duration": DurationTuple(
None, "1.9999999999999999", None, None, None, None, None
),
},
datetime.date(year=1989, month=3, day=1),
datetime.datetime(
year=1989,
month=4,
day=29,
hour=23,
minute=59,
second=59,
microsecond=999999,
),
),
(
{
"start": DateTuple("1989", "03", "01", None, None, None),
"duration": DurationTuple(
None, None, "1.9999999999999999", None, None, None, None
),
},
datetime.date(year=1989, month=3, day=1),
datetime.datetime(
year=1989,
month=3,
day=14,
hour=23,
minute=59,
second=59,
microsecond=999999,
),
),
(
{
"start": DateTuple("1989", "03", "01", None, None, None),
"duration": DurationTuple(
None, None, None, "1.9999999999999999", None, None, None
),
},
datetime.date(year=1989, month=3, day=1),
datetime.datetime(
year=1989,
month=3,
day=2,
hour=23,
minute=59,
second=59,
microsecond=999999,
),
),
(
{
"start": DateTuple("2001", "01", "01", None, None, None),
"duration": DurationTuple(
None, None, None, None, "14.9999999999999999", None, None
),
},
datetime.date(year=2001, month=1, day=1),
datetime.datetime(
year=2001,
month=1,
day=1,
hour=14,
minute=59,
second=59,
microsecond=999999,
),
),
(
{
"start": DateTuple("2001", "01", "01", None, None, None),
"duration": DurationTuple(
None, None, None, None, None, "0.00000000999", None
),
},
datetime.date(year=2001, month=1, day=1),
datetime.datetime(year=2001, month=1, day=1),
),
(
{
"start": DateTuple("2001", "01", "01", None, None, None),
"duration": DurationTuple(
None, None, None, None, None, "0.0000000999", None
),
},
datetime.date(year=2001, month=1, day=1),
datetime.datetime(
year=2001, month=1, day=1, hour=0, minute=0, second=0, microsecond=5
),
),
(
{
"start": DateTuple("2018", "03", "06", None, None, None),
"duration": DurationTuple(
None, None, None, None, None, None, "0.0000001"
),
},
datetime.date(year=2018, month=3, day=6),
datetime.datetime(year=2018, month=3, day=6),
),
(
{
"start": DateTuple("2018", "03", "06", None, None, None),
"duration": DurationTuple(
None, None, None, None, None, None, "2.0000048"
),
},
datetime.date(year=2018, month=3, day=6),
datetime.datetime(
year=2018, month=3, day=6, hour=0, minute=0, second=2, microsecond=4
),
),
(
{
"start": DatetimeTuple(
DateTuple("1980", "03", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
"end": DatetimeTuple(
DateTuple("1981", "04", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
},
datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1),
datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1),
),
(
{
"start": DatetimeTuple(
DateTuple("1980", "03", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
"end": DateTuple("1981", "04", "05", None, None, None),
},
datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1),
datetime.date(year=1981, month=4, day=5),
),
(
{
"start": DateTuple("1980", "03", "05", None, None, None),
"end": DatetimeTuple(
DateTuple("1981", "04", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
},
datetime.date(year=1980, month=3, day=5),
datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1),
),
(
{
"start": DateTuple("1980", "03", "05", None, None, None),
"end": DateTuple("1981", "04", "05", None, None, None),
},
datetime.date(year=1980, month=3, day=5),
datetime.date(year=1981, month=4, day=5),
),
(
{
"start": DateTuple("1981", "04", "05", None, None, None),
"end": DateTuple("1980", "03", "05", None, None, None),
},
datetime.date(year=1981, month=4, day=5),
datetime.date(year=1980, month=3, day=5),
),
(
{
"start": DatetimeTuple(
DateTuple("2050", "03", "01", None, None, None),
TimeTuple(
"13",
"00",
"00",
TimezoneTuple(False, True, None, None, "Z"),
),
),
"end": DatetimeTuple(
DateTuple("2050", "05", "11", None, None, None),
TimeTuple(
"15",
"30",
"00",
TimezoneTuple(False, True, None, None, "Z"),
),
),
},
datetime.datetime(
year=2050,
month=3,
day=1,
hour=13,
tzinfo=UTCOffset(name="UTC", minutes=0),
),
datetime.datetime(
year=2050,
month=5,
day=11,
hour=15,
minute=30,
tzinfo=UTCOffset(name="UTC", minutes=0),
),
),
# Test concise representation
(
{
"start": DateTuple("2020", "01", "01", None, None, None),
"end": DateTuple(None, None, "02", None, None, None),
},
datetime.date(year=2020, month=1, day=1),
datetime.date(year=2020, month=1, day=2),
),
(
{
"start": DateTuple("2008", "02", "15", None, None, None),
"end": DateTuple(None, "03", "14", None, None, None),
},
datetime.date(year=2008, month=2, day=15),
datetime.date(year=2008, month=3, day=14),
),
(
{
"start": DatetimeTuple(
DateTuple("2007", "12", "14", None, None, None),
TimeTuple("13", "30", None, None),
),
"end": TimeTuple("15", "30", None, None),
},
datetime.datetime(year=2007, month=12, day=14, hour=13, minute=30),
datetime.datetime(year=2007, month=12, day=14, hour=15, minute=30),
),
(
{
"start": DatetimeTuple(
DateTuple("2007", "11", "13", None, None, None),
TimeTuple("09", "00", None, None),
),
"end": DatetimeTuple(
DateTuple(None, None, "15", None, None, None),
TimeTuple("17", "00", None, None),
),
},
datetime.datetime(year=2007, month=11, day=13, hour=9),
datetime.datetime(year=2007, month=11, day=15, hour=17),
),
(
{
"start": DatetimeTuple(
DateTuple("2007", "11", "13", None, None, None),
TimeTuple("00", "00", None, None),
),
"end": DatetimeTuple(
DateTuple(None, None, "16", None, None, None),
TimeTuple("00", "00", None, None),
),
},
datetime.datetime(year=2007, month=11, day=13),
datetime.datetime(year=2007, month=11, day=16),
),
(
{
"start": DatetimeTuple(
DateTuple("2007", "11", "13", None, None, None),
TimeTuple(
"09",
"00",
None,
TimezoneTuple(False, True, None, None, "Z"),
),
),
"end": DatetimeTuple(
DateTuple(None, None, "15", None, None, None),
TimeTuple("17", "00", None, None),
),
},
datetime.datetime(
year=2007,
month=11,
day=13,
hour=9,
tzinfo=UTCOffset(name="UTC", minutes=0),
),
datetime.datetime(
year=2007,
month=11,
day=15,
hour=17,
tzinfo=UTCOffset(name="UTC", minutes=0),
),
),
(
{
"start": DatetimeTuple(
DateTuple("2007", "11", "13", None, None, None),
TimeTuple("09", "00", None, None),
),
"end": TimeTuple("12", "34.567", None, None),
},
datetime.datetime(year=2007, month=11, day=13, hour=9),
datetime.datetime(
year=2007,
month=11,
day=13,
hour=12,
minute=34,
second=34,
microsecond=20000,
),
),
(
{
"start": DateTuple("2007", "11", "13", None, None, None),
"end": TimeTuple("12", "34", None, None),
},
datetime.date(year=2007, month=11, day=13),
datetime.datetime(year=2007, month=11, day=13, hour=12, minute=34),
),
# Make sure we truncate, not round
# https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
(
{
"start": DatetimeTuple(
DateTuple("1980", "03", "05", None, None, None),
TimeTuple("01", "01", "00.0000001", None),
),
"end": DatetimeTuple(
DateTuple("1981", "04", "05", None, None, None),
TimeTuple("14", "43", "59.9999997", None),
),
},
datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1),
datetime.datetime(
year=1981,
month=4,
day=5,
hour=14,
minute=43,
second=59,
microsecond=999999,
),
),
)
for testtuple in testtuples:
result = PythonTimeBuilder.build_interval(**testtuple[0])
self.assertEqual(result[0], testtuple[1])
self.assertEqual(result[1], testtuple[2])
def test_build_repeating_interval(self):
args = {
"Rnn": "3",
"interval": IntervalTuple(
DateTuple("1981", "04", "05", None, None, None),
None,
DurationTuple(None, None, None, "1", None, None, None),
),
}
results = list(PythonTimeBuilder.build_repeating_interval(**args))
self.assertEqual(results[0], datetime.date(year=1981, month=4, day=5))
self.assertEqual(results[1], datetime.date(year=1981, month=4, day=6))
self.assertEqual(results[2], datetime.date(year=1981, month=4, day=7))
args = {
"Rnn": "11",
"interval": IntervalTuple(
None,
DatetimeTuple(
DateTuple("1980", "03", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
DurationTuple(None, None, None, None, "1", "2", None),
),
}
results = list(PythonTimeBuilder.build_repeating_interval(**args))
for dateindex in compat.range(0, 11):
self.assertEqual(
results[dateindex],
datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1)
- dateindex * datetime.timedelta(hours=1, minutes=2),
)
args = {
"Rnn": "2",
"interval": IntervalTuple(
DatetimeTuple(
DateTuple("1980", "03", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
DatetimeTuple(
DateTuple("1981", "04", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
None,
),
}
results = list(PythonTimeBuilder.build_repeating_interval(**args))
self.assertEqual(
results[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1)
)
self.assertEqual(
results[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1)
)
args = {
"Rnn": "2",
"interval": IntervalTuple(
DatetimeTuple(
DateTuple("1980", "03", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
DatetimeTuple(
DateTuple("1981", "04", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
None,
),
}
results = list(PythonTimeBuilder.build_repeating_interval(**args))
self.assertEqual(
results[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1)
)
self.assertEqual(
results[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1)
)
args = {
"R": True,
"interval": IntervalTuple(
None,
DatetimeTuple(
DateTuple("1980", "03", "05", None, None, None),
TimeTuple("01", "01", "00", None),
),
DurationTuple(None, None, None, None, "1", "2", None),
),
}
resultgenerator = PythonTimeBuilder.build_repeating_interval(**args)
# Test the first 11 generated
for dateindex in compat.range(0, 11):
self.assertEqual(
next(resultgenerator),
datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1)
- dateindex * datetime.timedelta(hours=1, minutes=2),
)
args = {
"R": True,
"interval": IntervalTuple(
DateTuple("1981", "04", "05", None, None, None),
None,
DurationTuple(None, None, None, "1", None, None, None),
),
}
resultgenerator = PythonTimeBuilder.build_repeating_interval(**args)
# Test the first 11 generated
for dateindex in compat.range(0, 11):
self.assertEqual(
next(resultgenerator),
(
datetime.datetime(year=1981, month=4, day=5, hour=0, minute=0)
+ dateindex * datetime.timedelta(days=1)
).date(),
)
def test_build_timezone(self):
testtuples = (
({"Z": True, "name": "Z"}, datetime.timedelta(hours=0), "UTC"),
(
{"negative": False, "hh": "00", "mm": "00", "name": "+00:00"},
datetime.timedelta(hours=0),
"+00:00",
),
(
{"negative": False, "hh": "01", "mm": "00", "name": "+01:00"},
datetime.timedelta(hours=1),
"+01:00",
),
(
{"negative": True, "hh": "01", "mm": "00", "name": "-01:00"},
-datetime.timedelta(hours=1),
"-01:00",
),
(
{"negative": False, "hh": "00", "mm": "12", "name": "+00:12"},
datetime.timedelta(minutes=12),
"+00:12",
),
(
{"negative": False, "hh": "01", "mm": "23", "name": "+01:23"},
datetime.timedelta(hours=1, minutes=23),
"+01:23",
),
(
{"negative": True, "hh": "01", "mm": "23", "name": "-01:23"},
-datetime.timedelta(hours=1, minutes=23),
"-01:23",
),
(
{"negative": False, "hh": "00", "name": "+00"},
datetime.timedelta(hours=0),
"+00",
),
(
{"negative": False, "hh": "01", "name": "+01"},
datetime.timedelta(hours=1),
"+01",
),
(
{"negative": True, "hh": "01", "name": "-01"},
-datetime.timedelta(hours=1),
"-01",
),
(
{"negative": False, "hh": "12", "name": "+12"},
datetime.timedelta(hours=12),
"+12",
),
(
{"negative": True, "hh": "12", "name": "-12"},
-datetime.timedelta(hours=12),
"-12",
),
)
for testtuple in testtuples:
result = PythonTimeBuilder.build_timezone(**testtuple[0])
self.assertEqual(result.utcoffset(None), testtuple[1])
self.assertEqual(result.tzname(None), testtuple[2])
def test_range_check_date(self):
# 0 isn't a valid year for a Python builder
with self.assertRaises(YearOutOfBoundsError):
PythonTimeBuilder.build_date(YYYY="0000")
# Leap year
# https://bitbucket.org/nielsenb/aniso8601/issues/14/parsing-ordinal-dates-should-only-allow
with self.assertRaises(DayOutOfBoundsError):
PythonTimeBuilder.build_date(YYYY="1981", DDD="366")
def test_range_check_time(self):
# Hour 24 can only represent midnight
with self.assertRaises(MidnightBoundsError):
PythonTimeBuilder.build_time(hh="24", mm="00", ss="01")
with self.assertRaises(MidnightBoundsError):
PythonTimeBuilder.build_time(hh="24", mm="00.1")
with self.assertRaises(MidnightBoundsError):
PythonTimeBuilder.build_time(hh="24", mm="01")
with self.assertRaises(MidnightBoundsError):
PythonTimeBuilder.build_time(hh="24.1")
def test_range_check_duration(self):
with self.assertRaises(YearOutOfBoundsError):
PythonTimeBuilder.build_duration(
PnY=str((datetime.timedelta.max.days // 365) + 1)
)
with self.assertRaises(MonthOutOfBoundsError):
PythonTimeBuilder.build_duration(
PnM=str((datetime.timedelta.max.days // 30) + 1)
)
with self.assertRaises(DayOutOfBoundsError):
PythonTimeBuilder.build_duration(PnD=str(datetime.timedelta.max.days + 1))
with self.assertRaises(WeekOutOfBoundsError):
PythonTimeBuilder.build_duration(
PnW=str((datetime.timedelta.max.days // 7) + 1)
)
with self.assertRaises(HoursOutOfBoundsError):
PythonTimeBuilder.build_duration(
TnH=str((datetime.timedelta.max.days * 24) + 24)
)
with self.assertRaises(MinutesOutOfBoundsError):
PythonTimeBuilder.build_duration(
TnM=str((datetime.timedelta.max.days * 24 * 60) + 24 * 60)
)
with self.assertRaises(SecondsOutOfBoundsError):
PythonTimeBuilder.build_duration(
TnS=str((datetime.timedelta.max.days * 24 * 60 * 60) + 24 * 60 * 60)
)
# Split max range across all parts
maxpart = datetime.timedelta.max.days // 7
with self.assertRaises(DayOutOfBoundsError):
PythonTimeBuilder.build_duration(
PnY=str((maxpart // 365) + 1),
PnM=str((maxpart // 30) + 1),
PnD=str((maxpart + 1)),
PnW=str((maxpart // 7) + 1),
TnH=str((maxpart * 24) + 1),
TnM=str((maxpart * 24 * 60) + 1),
TnS=str((maxpart * 24 * 60 * 60) + 1),
)
def test_range_check_interval(self):
with self.assertRaises(YearOutOfBoundsError):
PythonTimeBuilder.build_interval(
start=DateTuple("0007", None, None, None, None, None),
duration=DurationTuple(
None, None, None, str(datetime.timedelta.max.days), None, None, None
),
)
with self.assertRaises(YearOutOfBoundsError):
PythonTimeBuilder.build_interval(
start=DatetimeTuple(
DateTuple("0007", None, None, None, None, None),
TimeTuple("1", None, None, None),
),
duration=DurationTuple(
str(datetime.timedelta.max.days // 365),
None,
None,
None,
None,
None,
None,
),
)
with self.assertRaises(YearOutOfBoundsError):
PythonTimeBuilder.build_interval(
end=DateTuple("0001", None, None, None, None, None),
duration=DurationTuple("3", None, None, None, None, None, None),
)
with self.assertRaises(YearOutOfBoundsError):
PythonTimeBuilder.build_interval(
end=DatetimeTuple(
DateTuple("0001", None, None, None, None, None),
TimeTuple("1", None, None, None),
),
duration=DurationTuple("2", None, None, None, None, None, None),
)
def test_build_week_date(self):
weekdate = PythonTimeBuilder._build_week_date(2009, 1)
self.assertEqual(weekdate, datetime.date(year=2008, month=12, day=29))
weekdate = PythonTimeBuilder._build_week_date(2009, 53, isoday=7)
self.assertEqual(weekdate, datetime.date(year=2010, month=1, day=3))
def test_build_ordinal_date(self):
ordinaldate = PythonTimeBuilder._build_ordinal_date(1981, 95)
self.assertEqual(ordinaldate, datetime.date(year=1981, month=4, day=5))
def test_iso_year_start(self):
yearstart = PythonTimeBuilder._iso_year_start(2004)
self.assertEqual(yearstart, datetime.date(year=2003, month=12, day=29))
yearstart = PythonTimeBuilder._iso_year_start(2010)
self.assertEqual(yearstart, datetime.date(year=2010, month=1, day=4))
yearstart = PythonTimeBuilder._iso_year_start(2009)
self.assertEqual(yearstart, datetime.date(year=2008, month=12, day=29))
def test_date_generator(self):
startdate = datetime.date(year=2018, month=8, day=29)
timedelta = datetime.timedelta(days=1)
iterations = 10
generator = PythonTimeBuilder._date_generator(startdate, timedelta, iterations)
results = list(generator)
for dateindex in compat.range(0, 10):
self.assertEqual(
results[dateindex],
datetime.date(year=2018, month=8, day=29)
+ dateindex * datetime.timedelta(days=1),
)
def test_date_generator_unbounded(self):
startdate = datetime.date(year=2018, month=8, day=29)
timedelta = datetime.timedelta(days=5)
generator = PythonTimeBuilder._date_generator_unbounded(startdate, timedelta)
# Check the first 10 results
for dateindex in compat.range(0, 10):
self.assertEqual(
next(generator),
datetime.date(year=2018, month=8, day=29)
+ dateindex * datetime.timedelta(days=5),
)
def test_distribute_microseconds(self):
self.assertEqual(PythonTimeBuilder._distribute_microseconds(1, (), ()), (1,))
self.assertEqual(
PythonTimeBuilder._distribute_microseconds(11, (0,), (10,)), (1, 1)
)
self.assertEqual(
PythonTimeBuilder._distribute_microseconds(211, (0, 0), (100, 10)),
(2, 1, 1),
)
self.assertEqual(PythonTimeBuilder._distribute_microseconds(1, (), ()), (1,))
self.assertEqual(
PythonTimeBuilder._distribute_microseconds(11, (5,), (10,)), (6, 1)
)
self.assertEqual(
PythonTimeBuilder._distribute_microseconds(211, (10, 5), (100, 10)),
(12, 6, 1),
)