# -*- coding: utf-8 -*-

# Copyright (c) 2025, 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 unittest

import aniso8601
from aniso8601.builders import (
    BaseTimeBuilder,
    DatetimeTuple,
    DateTuple,
    DurationTuple,
    IntervalTuple,
    RepeatingIntervalTuple,
    TimeTuple,
    TimezoneTuple,
    TupleBuilder,
    cast,
)
from aniso8601.exceptions import (
    DayOutOfBoundsError,
    HoursOutOfBoundsError,
    ISOFormatError,
    LeapSecondError,
    MidnightBoundsError,
    MinutesOutOfBoundsError,
    MonthOutOfBoundsError,
    SecondsOutOfBoundsError,
    WeekOutOfBoundsError,
)
from aniso8601.tests.compat import mock


class LeapSecondSupportingTestBuilder(BaseTimeBuilder):
    LEAP_SECONDS_SUPPORTED = True


class TestBuilderFunctions(unittest.TestCase):
    def test_cast(self):
        self.assertEqual(cast("1", int), 1)
        self.assertEqual(cast("-2", int), -2)
        self.assertEqual(cast("3", float), float(3))
        self.assertEqual(cast("-4", float), float(-4))
        self.assertEqual(cast("5.6", float), 5.6)
        self.assertEqual(cast("-7.8", float), -7.8)

    def test_cast_exception(self):
        with self.assertRaises(ISOFormatError):
            cast("asdf", int)

        with self.assertRaises(ISOFormatError):
            cast("asdf", float)

    def test_cast_caughtexception(self):
        def tester(value):
            raise RuntimeError

        with self.assertRaises(ISOFormatError):
            cast("asdf", tester, caughtexceptions=(RuntimeError,))

    def test_cast_thrownexception(self):
        with self.assertRaises(RuntimeError):
            cast("asdf", int, thrownexception=RuntimeError)


class TestBaseTimeBuilder(unittest.TestCase):
    def test_build_date(self):
        with self.assertRaises(NotImplementedError):
            BaseTimeBuilder.build_date()

    def test_build_time(self):
        with self.assertRaises(NotImplementedError):
            BaseTimeBuilder.build_time()

    def test_build_datetime(self):
        with self.assertRaises(NotImplementedError):
            BaseTimeBuilder.build_datetime(None, None)

    def test_build_duration(self):
        with self.assertRaises(NotImplementedError):
            BaseTimeBuilder.build_duration()

    def test_build_interval(self):
        with self.assertRaises(NotImplementedError):
            BaseTimeBuilder.build_interval()

    def test_build_repeating_interval(self):
        with self.assertRaises(NotImplementedError):
            BaseTimeBuilder.build_repeating_interval()

    def test_build_timezone(self):
        with self.assertRaises(NotImplementedError):
            BaseTimeBuilder.build_timezone()

    def test_range_check_date(self):
        # Check the calendar for day ranges
        with self.assertRaises(DayOutOfBoundsError):
            BaseTimeBuilder.range_check_date(YYYY="0007", MM="02", DD="30")

        with self.assertRaises(DayOutOfBoundsError):
            BaseTimeBuilder.range_check_date(YYYY="0007", DDD="366")

        with self.assertRaises(MonthOutOfBoundsError):
            BaseTimeBuilder.range_check_date(YYYY="4333", MM="30", DD="30")

        # 0 isn't a valid week number
        with self.assertRaises(WeekOutOfBoundsError):
            BaseTimeBuilder.range_check_date(YYYY="2003", Www="00")

        # Week must not be larger than 53
        with self.assertRaises(WeekOutOfBoundsError):
            BaseTimeBuilder.range_check_date(YYYY="2004", Www="54")

        # 0 isn't a valid day number
        with self.assertRaises(DayOutOfBoundsError):
            BaseTimeBuilder.range_check_date(YYYY="2001", Www="02", D="0")

        # Day must not be larger than 7
        with self.assertRaises(DayOutOfBoundsError):
            BaseTimeBuilder.range_check_date(YYYY="2001", Www="02", D="8")

        with self.assertRaises(DayOutOfBoundsError):
            BaseTimeBuilder.range_check_date(YYYY="1981", DDD="000")

        # Day must be 365, or 366, not larger
        with self.assertRaises(DayOutOfBoundsError):
            BaseTimeBuilder.range_check_date(YYYY="1234", DDD="000")

        with self.assertRaises(DayOutOfBoundsError):
            BaseTimeBuilder.range_check_date(YYYY="1234", DDD="367")

        # https://bitbucket.org/nielsenb/aniso8601/issues/14/parsing-ordinal-dates-should-only-allow
        with self.assertRaises(DayOutOfBoundsError):
            BaseTimeBuilder.range_check_date(YYYY="1981", DDD="366")

        # Make sure Nones pass through unmodified
        self.assertEqual(
            BaseTimeBuilder.range_check_date(rangedict={}),
            (None, None, None, None, None, None),
        )

    def test_range_check_time(self):
        # Leap seconds not supported
        # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
        # https://bitbucket.org/nielsenb/aniso8601/issues/13/parsing-of-leap-second-gives-wildly
        with self.assertRaises(LeapSecondError):
            BaseTimeBuilder.range_check_time(hh="23", mm="59", ss="60")

        with self.assertRaises(SecondsOutOfBoundsError):
            BaseTimeBuilder.range_check_time(hh="00", mm="00", ss="60")

        with self.assertRaises(SecondsOutOfBoundsError):
            BaseTimeBuilder.range_check_time(hh="00", mm="00", ss="61")

        with self.assertRaises(MinutesOutOfBoundsError):
            BaseTimeBuilder.range_check_time(hh="00", mm="61")

        with self.assertRaises(MinutesOutOfBoundsError):
            BaseTimeBuilder.range_check_time(hh="00", mm="60")

        with self.assertRaises(MinutesOutOfBoundsError):
            BaseTimeBuilder.range_check_time(hh="00", mm="60.1")

        with self.assertRaises(HoursOutOfBoundsError):
            BaseTimeBuilder.range_check_time(hh="25")

        # Hour 24 can only represent midnight
        with self.assertRaises(MidnightBoundsError):
            BaseTimeBuilder.range_check_time(hh="24", mm="00", ss="01")

        with self.assertRaises(MidnightBoundsError):
            BaseTimeBuilder.range_check_time(hh="24", mm="00.1")

        with self.assertRaises(MidnightBoundsError):
            BaseTimeBuilder.range_check_time(hh="24", mm="01")

        with self.assertRaises(MidnightBoundsError):
            BaseTimeBuilder.range_check_time(hh="24.1")

        # Leap seconds not supported
        # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
        # https://bitbucket.org/nielsenb/aniso8601/issues/13/parsing-of-leap-second-gives-wildly
        with self.assertRaises(LeapSecondError):
            BaseTimeBuilder.range_check_time(hh="23", mm="59", ss="60")

        # Make sure Nones pass through unmodified
        self.assertEqual(
            BaseTimeBuilder.range_check_time(rangedict={}), (None, None, None, None)
        )

    def test_range_check_time_leap_seconds_supported(self):
        self.assertEqual(
            LeapSecondSupportingTestBuilder.range_check_time(hh="23", mm="59", ss="60"),
            (23, 59, 60, None),
        )

        with self.assertRaises(SecondsOutOfBoundsError):
            LeapSecondSupportingTestBuilder.range_check_time(hh="01", mm="02", ss="60")

    def test_range_check_duration(self):
        self.assertEqual(
            BaseTimeBuilder.range_check_duration(),
            (None, None, None, None, None, None, None),
        )

        self.assertEqual(
            BaseTimeBuilder.range_check_duration(rangedict={}),
            (None, None, None, None, None, None, None),
        )

    def test_range_check_repeating_interval(self):
        self.assertEqual(
            BaseTimeBuilder.range_check_repeating_interval(), (None, None, None)
        )

        self.assertEqual(
            BaseTimeBuilder.range_check_repeating_interval(rangedict={}),
            (None, None, None),
        )

    def test_range_check_timezone(self):
        self.assertEqual(
            BaseTimeBuilder.range_check_timezone(), (None, None, None, None, "")
        )

        self.assertEqual(
            BaseTimeBuilder.range_check_timezone(rangedict={}),
            (None, None, None, None, ""),
        )

    def test_build_object(self):
        datetest = (
            DateTuple("1", "2", "3", "4", "5", "6"),
            {"YYYY": "1", "MM": "2", "DD": "3", "Www": "4", "D": "5", "DDD": "6"},
        )

        timetest = (
            TimeTuple("1", "2", "3", TimezoneTuple(False, False, "4", "5", "tz name")),
            {
                "hh": "1",
                "mm": "2",
                "ss": "3",
                "tz": TimezoneTuple(False, False, "4", "5", "tz name"),
            },
        )

        datetimetest = (
            DatetimeTuple(
                DateTuple("1", "2", "3", "4", "5", "6"),
                TimeTuple(
                    "7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name")
                ),
            ),
            (
                DateTuple("1", "2", "3", "4", "5", "6"),
                TimeTuple(
                    "7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name")
                ),
            ),
        )

        durationtest = (
            DurationTuple("1", "2", "3", "4", "5", "6", "7"),
            {
                "PnY": "1",
                "PnM": "2",
                "PnW": "3",
                "PnD": "4",
                "TnH": "5",
                "TnM": "6",
                "TnS": "7",
            },
        )

        intervaltests = (
            (
                IntervalTuple(
                    DateTuple("1", "2", "3", "4", "5", "6"),
                    DateTuple("7", "8", "9", "10", "11", "12"),
                    None,
                ),
                {
                    "start": DateTuple("1", "2", "3", "4", "5", "6"),
                    "end": DateTuple("7", "8", "9", "10", "11", "12"),
                    "duration": None,
                },
            ),
            (
                IntervalTuple(
                    DateTuple("1", "2", "3", "4", "5", "6"),
                    None,
                    DurationTuple("7", "8", "9", "10", "11", "12", "13"),
                ),
                {
                    "start": DateTuple("1", "2", "3", "4", "5", "6"),
                    "end": None,
                    "duration": DurationTuple("7", "8", "9", "10", "11", "12", "13"),
                },
            ),
            (
                IntervalTuple(
                    None,
                    TimeTuple(
                        "1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name")
                    ),
                    DurationTuple("6", "7", "8", "9", "10", "11", "12"),
                ),
                {
                    "start": None,
                    "end": TimeTuple(
                        "1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name")
                    ),
                    "duration": DurationTuple("6", "7", "8", "9", "10", "11", "12"),
                },
            ),
        )

        repeatingintervaltests = (
            (
                RepeatingIntervalTuple(
                    True,
                    None,
                    IntervalTuple(
                        DateTuple("1", "2", "3", "4", "5", "6"),
                        DateTuple("7", "8", "9", "10", "11", "12"),
                        None,
                    ),
                ),
                {
                    "R": True,
                    "Rnn": None,
                    "interval": IntervalTuple(
                        DateTuple("1", "2", "3", "4", "5", "6"),
                        DateTuple("7", "8", "9", "10", "11", "12"),
                        None,
                    ),
                },
            ),
            (
                RepeatingIntervalTuple(
                    False,
                    "1",
                    IntervalTuple(
                        DatetimeTuple(
                            DateTuple("2", "3", "4", "5", "6", "7"),
                            TimeTuple("8", "9", "10", None),
                        ),
                        DatetimeTuple(
                            DateTuple("11", "12", "13", "14", "15", "16"),
                            TimeTuple("17", "18", "19", None),
                        ),
                        None,
                    ),
                ),
                {
                    "R": False,
                    "Rnn": "1",
                    "interval": IntervalTuple(
                        DatetimeTuple(
                            DateTuple("2", "3", "4", "5", "6", "7"),
                            TimeTuple("8", "9", "10", None),
                        ),
                        DatetimeTuple(
                            DateTuple("11", "12", "13", "14", "15", "16"),
                            TimeTuple("17", "18", "19", None),
                        ),
                        None,
                    ),
                },
            ),
        )

        timezonetest = (
            TimezoneTuple(False, False, "1", "2", "+01:02"),
            {"negative": False, "Z": False, "hh": "1", "mm": "2", "name": "+01:02"},
        )

        with mock.patch.object(
            aniso8601.builders.BaseTimeBuilder, "build_date"
        ) as mock_build:
            mock_build.return_value = datetest[0]

            result = BaseTimeBuilder._build_object(datetest[0])

            self.assertEqual(result, datetest[0])
            mock_build.assert_called_once_with(**datetest[1])

        with mock.patch.object(
            aniso8601.builders.BaseTimeBuilder, "build_time"
        ) as mock_build:
            mock_build.return_value = timetest[0]

            result = BaseTimeBuilder._build_object(timetest[0])

            self.assertEqual(result, timetest[0])
            mock_build.assert_called_once_with(**timetest[1])

        with mock.patch.object(
            aniso8601.builders.BaseTimeBuilder, "build_datetime"
        ) as mock_build:
            mock_build.return_value = datetimetest[0]

            result = BaseTimeBuilder._build_object(datetimetest[0])

            self.assertEqual(result, datetimetest[0])
            mock_build.assert_called_once_with(*datetimetest[1])

        with mock.patch.object(
            aniso8601.builders.BaseTimeBuilder, "build_duration"
        ) as mock_build:
            mock_build.return_value = durationtest[0]

            result = BaseTimeBuilder._build_object(durationtest[0])

            self.assertEqual(result, durationtest[0])
            mock_build.assert_called_once_with(**durationtest[1])

        for intervaltest in intervaltests:
            with mock.patch.object(
                aniso8601.builders.BaseTimeBuilder, "build_interval"
            ) as mock_build:
                mock_build.return_value = intervaltest[0]

                result = BaseTimeBuilder._build_object(intervaltest[0])

                self.assertEqual(result, intervaltest[0])
                mock_build.assert_called_once_with(**intervaltest[1])

        for repeatingintervaltest in repeatingintervaltests:
            with mock.patch.object(
                aniso8601.builders.BaseTimeBuilder, "build_repeating_interval"
            ) as mock_build:
                mock_build.return_value = repeatingintervaltest[0]

                result = BaseTimeBuilder._build_object(repeatingintervaltest[0])

                self.assertEqual(result, repeatingintervaltest[0])
                mock_build.assert_called_once_with(**repeatingintervaltest[1])

        with mock.patch.object(
            aniso8601.builders.BaseTimeBuilder, "build_timezone"
        ) as mock_build:
            mock_build.return_value = timezonetest[0]

            result = BaseTimeBuilder._build_object(timezonetest[0])

            self.assertEqual(result, timezonetest[0])
            mock_build.assert_called_once_with(**timezonetest[1])

    def test_is_interval_end_concise(self):
        self.assertTrue(
            BaseTimeBuilder._is_interval_end_concise(TimeTuple("1", "2", "3", None))
        )
        self.assertTrue(
            BaseTimeBuilder._is_interval_end_concise(
                DateTuple(None, "2", "3", "4", "5", "6")
            )
        )
        self.assertTrue(
            BaseTimeBuilder._is_interval_end_concise(
                DatetimeTuple(
                    DateTuple(None, "2", "3", "4", "5", "6"),
                    TimeTuple("7", "8", "9", None),
                )
            )
        )

        self.assertFalse(
            BaseTimeBuilder._is_interval_end_concise(
                DateTuple("1", "2", "3", "4", "5", "6")
            )
        )
        self.assertFalse(
            BaseTimeBuilder._is_interval_end_concise(
                DatetimeTuple(
                    DateTuple("1", "2", "3", "4", "5", "6"),
                    TimeTuple("7", "8", "9", None),
                )
            )
        )

    def test_combine_concise_interval_tuples(self):
        testtuples = (
            (
                DateTuple("2020", "01", "01", None, None, None),
                DateTuple(None, None, "02", None, None, None),
                DateTuple("2020", "01", "02", None, None, None),
            ),
            (
                DateTuple("2008", "02", "15", None, None, None),
                DateTuple(None, "03", "14", None, None, None),
                DateTuple("2008", "03", "14", None, None, None),
            ),
            (
                DatetimeTuple(
                    DateTuple("2007", "12", "14", None, None, None),
                    TimeTuple("13", "30", None, None),
                ),
                TimeTuple("15", "30", None, None),
                DatetimeTuple(
                    DateTuple("2007", "12", "14", None, None, None),
                    TimeTuple("15", "30", None, None),
                ),
            ),
            (
                DatetimeTuple(
                    DateTuple("2007", "11", "13", None, None, None),
                    TimeTuple("09", "00", None, None),
                ),
                DatetimeTuple(
                    DateTuple(None, None, "15", None, None, None),
                    TimeTuple("17", "00", None, None),
                ),
                DatetimeTuple(
                    DateTuple("2007", "11", "15", None, None, None),
                    TimeTuple("17", "00", None, None),
                ),
            ),
            (
                DatetimeTuple(
                    DateTuple("2007", "11", "13", None, None, None),
                    TimeTuple("00", "00", None, None),
                ),
                DatetimeTuple(
                    DateTuple(None, None, "16", None, None, None),
                    TimeTuple("00", "00", None, None),
                ),
                DatetimeTuple(
                    DateTuple("2007", "11", "16", None, None, None),
                    TimeTuple("00", "00", None, None),
                ),
            ),
            (
                DatetimeTuple(
                    DateTuple("2007", "11", "13", None, None, None),
                    TimeTuple(
                        "09", "00", None, TimezoneTuple(False, True, None, None, "Z")
                    ),
                ),
                DatetimeTuple(
                    DateTuple(None, None, "15", None, None, None),
                    TimeTuple("17", "00", None, None),
                ),
                DatetimeTuple(
                    DateTuple("2007", "11", "15", None, None, None),
                    TimeTuple(
                        "17", "00", None, TimezoneTuple(False, True, None, None, "Z")
                    ),
                ),
            ),
        )

        for testtuple in testtuples:
            result = BaseTimeBuilder._combine_concise_interval_tuples(
                testtuple[0], testtuple[1]
            )
            self.assertEqual(result, testtuple[2])


class TestTupleBuilder(unittest.TestCase):
    def test_build_date(self):
        datetuple = TupleBuilder.build_date()

        self.assertEqual(datetuple, DateTuple(None, None, None, None, None, None))

        datetuple = TupleBuilder.build_date(
            YYYY="1", MM="2", DD="3", Www="4", D="5", DDD="6"
        )

        self.assertEqual(datetuple, DateTuple("1", "2", "3", "4", "5", "6"))

    def test_build_time(self):
        testtuples = (
            ({}, TimeTuple(None, None, None, None)),
            (
                {"hh": "1", "mm": "2", "ss": "3", "tz": None},
                TimeTuple("1", "2", "3", None),
            ),
            (
                {
                    "hh": "1",
                    "mm": "2",
                    "ss": "3",
                    "tz": TimezoneTuple(False, False, "4", "5", "tz name"),
                },
                TimeTuple(
                    "1", "2", "3", TimezoneTuple(False, False, "4", "5", "tz name")
                ),
            ),
        )

        for testtuple in testtuples:
            self.assertEqual(TupleBuilder.build_time(**testtuple[0]), testtuple[1])

    def test_build_datetime(self):
        testtuples = (
            (
                {
                    "date": DateTuple("1", "2", "3", "4", "5", "6"),
                    "time": TimeTuple("7", "8", "9", None),
                },
                DatetimeTuple(
                    DateTuple("1", "2", "3", "4", "5", "6"),
                    TimeTuple("7", "8", "9", None),
                ),
            ),
            (
                {
                    "date": DateTuple("1", "2", "3", "4", "5", "6"),
                    "time": TimeTuple(
                        "7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name")
                    ),
                },
                DatetimeTuple(
                    DateTuple("1", "2", "3", "4", "5", "6"),
                    TimeTuple(
                        "7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name")
                    ),
                ),
            ),
        )

        for testtuple in testtuples:
            self.assertEqual(TupleBuilder.build_datetime(**testtuple[0]), testtuple[1])

    def test_build_duration(self):
        testtuples = (
            ({}, DurationTuple(None, None, None, None, None, None, None)),
            (
                {
                    "PnY": "1",
                    "PnM": "2",
                    "PnW": "3",
                    "PnD": "4",
                    "TnH": "5",
                    "TnM": "6",
                    "TnS": "7",
                },
                DurationTuple("1", "2", "3", "4", "5", "6", "7"),
            ),
        )

        for testtuple in testtuples:
            self.assertEqual(TupleBuilder.build_duration(**testtuple[0]), testtuple[1])

    def test_build_interval(self):
        testtuples = (
            ({}, IntervalTuple(None, None, None)),
            (
                {
                    "start": DateTuple("1", "2", "3", "4", "5", "6"),
                    "end": DateTuple("7", "8", "9", "10", "11", "12"),
                },
                IntervalTuple(
                    DateTuple("1", "2", "3", "4", "5", "6"),
                    DateTuple("7", "8", "9", "10", "11", "12"),
                    None,
                ),
            ),
            (
                {
                    "start": TimeTuple(
                        "1", "2", "3", TimezoneTuple(True, False, "7", "8", "tz name")
                    ),
                    "end": TimeTuple(
                        "4", "5", "6", TimezoneTuple(False, False, "9", "10", "tz name")
                    ),
                },
                IntervalTuple(
                    TimeTuple(
                        "1", "2", "3", TimezoneTuple(True, False, "7", "8", "tz name")
                    ),
                    TimeTuple(
                        "4", "5", "6", TimezoneTuple(False, False, "9", "10", "tz name")
                    ),
                    None,
                ),
            ),
            (
                {
                    "start": DatetimeTuple(
                        DateTuple("1", "2", "3", "4", "5", "6"),
                        TimeTuple(
                            "7",
                            "8",
                            "9",
                            TimezoneTuple(True, False, "10", "11", "tz name"),
                        ),
                    ),
                    "end": DatetimeTuple(
                        DateTuple("12", "13", "14", "15", "16", "17"),
                        TimeTuple(
                            "18",
                            "19",
                            "20",
                            TimezoneTuple(False, False, "21", "22", "tz name"),
                        ),
                    ),
                },
                IntervalTuple(
                    DatetimeTuple(
                        DateTuple("1", "2", "3", "4", "5", "6"),
                        TimeTuple(
                            "7",
                            "8",
                            "9",
                            TimezoneTuple(True, False, "10", "11", "tz name"),
                        ),
                    ),
                    DatetimeTuple(
                        DateTuple("12", "13", "14", "15", "16", "17"),
                        TimeTuple(
                            "18",
                            "19",
                            "20",
                            TimezoneTuple(False, False, "21", "22", "tz name"),
                        ),
                    ),
                    None,
                ),
            ),
            (
                {
                    "start": DateTuple("1", "2", "3", "4", "5", "6"),
                    "end": None,
                    "duration": DurationTuple("7", "8", "9", "10", "11", "12", "13"),
                },
                IntervalTuple(
                    DateTuple("1", "2", "3", "4", "5", "6"),
                    None,
                    DurationTuple("7", "8", "9", "10", "11", "12", "13"),
                ),
            ),
            (
                {
                    "start": None,
                    "end": TimeTuple(
                        "1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name")
                    ),
                    "duration": DurationTuple("6", "7", "8", "9", "10", "11", "12"),
                },
                IntervalTuple(
                    None,
                    TimeTuple(
                        "1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name")
                    ),
                    DurationTuple("6", "7", "8", "9", "10", "11", "12"),
                ),
            ),
        )

        for testtuple in testtuples:
            self.assertEqual(TupleBuilder.build_interval(**testtuple[0]), testtuple[1])

    def test_build_repeating_interval(self):
        testtuples = (
            ({}, RepeatingIntervalTuple(None, None, None)),
            (
                {
                    "R": True,
                    "interval": IntervalTuple(
                        DateTuple("1", "2", "3", "4", "5", "6"),
                        DateTuple("7", "8", "9", "10", "11", "12"),
                        None,
                    ),
                },
                RepeatingIntervalTuple(
                    True,
                    None,
                    IntervalTuple(
                        DateTuple("1", "2", "3", "4", "5", "6"),
                        DateTuple("7", "8", "9", "10", "11", "12"),
                        None,
                    ),
                ),
            ),
            (
                {
                    "R": False,
                    "Rnn": "1",
                    "interval": IntervalTuple(
                        DatetimeTuple(
                            DateTuple("2", "3", "4", "5", "6", "7"),
                            TimeTuple("8", "9", "10", None),
                        ),
                        DatetimeTuple(
                            DateTuple("11", "12", "13", "14", "15", "16"),
                            TimeTuple("17", "18", "19", None),
                        ),
                        None,
                    ),
                },
                RepeatingIntervalTuple(
                    False,
                    "1",
                    IntervalTuple(
                        DatetimeTuple(
                            DateTuple("2", "3", "4", "5", "6", "7"),
                            TimeTuple("8", "9", "10", None),
                        ),
                        DatetimeTuple(
                            DateTuple("11", "12", "13", "14", "15", "16"),
                            TimeTuple("17", "18", "19", None),
                        ),
                        None,
                    ),
                ),
            ),
        )

        for testtuple in testtuples:
            result = TupleBuilder.build_repeating_interval(**testtuple[0])
            self.assertEqual(result, testtuple[1])

    def test_build_timezone(self):
        testtuples = (
            ({}, TimezoneTuple(None, None, None, None, "")),
            (
                {"negative": False, "Z": True, "name": "UTC"},
                TimezoneTuple(False, True, None, None, "UTC"),
            ),
            (
                {"negative": False, "Z": False, "hh": "1", "mm": "2", "name": "+01:02"},
                TimezoneTuple(False, False, "1", "2", "+01:02"),
            ),
            (
                {"negative": True, "Z": False, "hh": "1", "mm": "2", "name": "-01:02"},
                TimezoneTuple(True, False, "1", "2", "-01:02"),
            ),
        )

        for testtuple in testtuples:
            result = TupleBuilder.build_timezone(**testtuple[0])
            self.assertEqual(result, testtuple[1])
