from datetime import *
from unittest import TestCase
from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod


class TestTimeDelta(Static[TestCase]):
    def test_constructor(self):
        eq = self.assertEqual
        td = timedelta

        # Check keyword args to constructor
        eq(
            td(),
            td(
                weeks=0,
                days=0,
                hours=0,
                minutes=0,
                seconds=0,
                milliseconds=0,
                microseconds=0,
            ),
        )
        eq(td(1), td(days=1))
        eq(td(0, 1), td(seconds=1))
        eq(td(0, 0, 1), td(microseconds=1))
        eq(td(weeks=1), td(days=7))
        eq(td(days=1), td(hours=24))
        eq(td(hours=1), td(minutes=60))
        eq(td(minutes=1), td(seconds=60))
        eq(td(seconds=1), td(milliseconds=1000))
        eq(td(milliseconds=1), td(microseconds=1000))

        # Check float args to constructor
        eq(td(weeks=1.0 / 7), td(days=1))
        eq(td(days=1.0 / 24), td(hours=1))
        eq(td(hours=1.0 / 60), td(minutes=1))
        eq(td(minutes=1.0 / 60), td(seconds=1))
        eq(td(seconds=0.001), td(milliseconds=1))
        eq(td(milliseconds=0.001), td(microseconds=1))

    def test_computations(self):
        eq = self.assertEqual
        td = timedelta

        a = td(7)  # One week
        b = td(0, 60)  # One minute
        c = td(0, 0, 1000)  # One millisecond
        eq(a + b + c, td(7, 60, 1000))
        eq(a - b, td(6, 24 * 3600 - 60))
        # eq(b.__rsub__(a), td(6, 24*3600 - 60))
        eq(-a, td(-7))
        eq(+a, td(7))
        eq(-b, td(-1, 24 * 3600 - 60))
        eq(-c, td(-1, 24 * 3600 - 1, 999000))
        eq(abs(a), a)
        eq(abs(-a), a)
        eq(td(6, 24 * 3600), a)
        eq(td(0, 0, 60 * 1000000), b)
        eq(a * 10, td(70))
        eq(a * 10, 10 * a)
        eq(a * 10, 10 * a)
        eq(b * 10, td(0, 600))
        eq(10 * b, td(0, 600))
        eq(b * 10, td(0, 600))
        eq(c * 10, td(0, 0, 10000))
        eq(10 * c, td(0, 0, 10000))
        eq(c * 10, td(0, 0, 10000))
        eq(a * -1, -a)
        eq(b * -2, -b - b)
        eq(c * -2, -c + -c)
        eq(b * (60 * 24), (b * 60) * 24)
        eq(b * (60 * 24), (60 * b) * 24)
        eq(c * 1000, td(0, 1))
        eq(1000 * c, td(0, 1))
        eq(a // 7, td(1))
        eq(b // 10, td(0, 6))
        eq(c // 1000, td(0, 0, 1))
        eq(a // 10, td(0, 7 * 24 * 360))
        eq(a // 3600000, td(0, 0, 7 * 24 * 1000))
        eq(a / 0.5, td(14))
        eq(b / 0.5, td(0, 120))
        eq(a / 7, td(1))
        eq(b / 10, td(0, 6))
        eq(c / 1000, td(0, 0, 1))
        eq(a / 10, td(0, 7 * 24 * 360))
        eq(a / 3600000, td(0, 0, 7 * 24 * 1000))

        # Multiplication by float
        us = td(microseconds=1)
        eq((3 * us) * 0.5, 2 * us)
        eq((5 * us) * 0.5, 2 * us)
        eq(0.5 * (3 * us), 2 * us)
        eq(0.5 * (5 * us), 2 * us)
        eq((-3 * us) * 0.5, -2 * us)
        eq((-5 * us) * 0.5, -2 * us)

        # TODO: check Python's Issue #23521 and possibly
        # incorporate the same fix here. For now a couple
        # tests are disabled.

        # Issue #23521
        eq(td(seconds=1) * 0.123456, td(microseconds=123456))
        # eq(td(seconds=1) * 0.6112295, td(microseconds=611229))

        # Division by int and float
        eq((3 * us) / 2, 2 * us)
        eq((5 * us) / 2, 2 * us)
        eq((-3 * us) / 2.0, -2 * us)
        eq((-5 * us) / 2.0, -2 * us)
        eq((3 * us) / -2, -2 * us)
        eq((5 * us) / -2, -2 * us)
        eq((3 * us) / -2.0, -2 * us)
        eq((5 * us) / -2.0, -2 * us)
        for i in range(-10, 10):
            eq((i * us / 3) // us, round(i / 3))
        for i in range(-10, 10):
            eq((i * us / -3) // us, round(i / -3))

        # Issue #23521
        # eq(td(seconds=1) / (1 / 0.6112295), td(microseconds=611229))

        # Issue #11576
        eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998), td(0, 0, 1))
        eq(td(999999999, 1, 1) - td(999999999, 1, 0), td(0, 0, 1))

    """
    def test_disallowed_special(self):
        a = timedelta(42)
        NAN = 0. / 0.
        self.assertRaises(ValueError, a.__mul__, NAN)
        self.assertRaises(ValueError, a.__truediv__, NAN)
    """

    def test_basic_attributes(self):
        days, seconds, us = 1, 7, 31
        td = timedelta(days, seconds, us)
        self.assertEqual(td.days, days)
        self.assertEqual(td.seconds, seconds)
        self.assertEqual(td.microseconds, us)

    def test_total_seconds(self):
        td = timedelta(days=365)
        self.assertEqual(td.total_seconds(), 31536000.0)
        for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
            td = timedelta(seconds=total_seconds)
            self.assertEqual(td.total_seconds(), total_seconds)
        # Issue8644: Test that td.total_seconds() has the same
        # accuracy as td / timedelta(seconds=1).
        for ms in [-1, -2, -123]:
            td = timedelta(microseconds=ms)
            self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))

    def test_carries(self):
        t1 = timedelta(
            days=100,
            weeks=-7,
            hours=-24 * (100 - 49),
            minutes=-3,
            seconds=12,
            microseconds=(3 * 60 - 12) * 1e6 + 1,
        )
        t2 = timedelta(microseconds=1)
        self.assertEqual(t1, t2)

    def test_hash_equality(self):
        t1 = timedelta(
            days=100,
            weeks=-7,
            hours=-24 * (100 - 49),
            minutes=-3,
            seconds=12,
            microseconds=(3 * 60 - 12) * 1000000,
        )
        t2 = timedelta()
        self.assertEqual(hash(t1), hash(t2))

        t1 += timedelta(weeks=7)
        t2 += timedelta(days=7 * 7)
        self.assertEqual(t1, t2)
        self.assertEqual(hash(t1), hash(t2))

        d = {t1: 1}
        d[t2] = 2
        self.assertEqual(len(d), 1)
        self.assertEqual(d[t1], 2)

    def test_compare(self):
        t1 = timedelta(2, 3, 4)
        t2 = timedelta(2, 3, 4)
        self.assertEqual(t1, t2)
        self.assertTrue(t1 <= t2)
        self.assertTrue(t1 >= t2)
        self.assertFalse(t1 != t2)
        self.assertFalse(t1 < t2)
        self.assertFalse(t1 > t2)

        for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
            t2 = timedelta(*args)  # this is larger than t1
            self.assertTrue(t1 < t2)
            self.assertTrue(t2 > t1)
            self.assertTrue(t1 <= t2)
            self.assertTrue(t2 >= t1)
            self.assertTrue(t1 != t2)
            self.assertTrue(t2 != t1)
            self.assertFalse(t1 == t2)
            self.assertFalse(t2 == t1)
            self.assertFalse(t1 > t2)
            self.assertFalse(t2 < t1)
            self.assertFalse(t1 >= t2)
            self.assertFalse(t2 <= t1)

    def test_str(self):
        td = timedelta
        eq = self.assertEqual

        eq(str(td(1)), "1 day, 0:00:00")
        eq(str(td(-1)), "-1 day, 0:00:00")
        eq(str(td(2)), "2 days, 0:00:00")
        eq(str(td(-2)), "-2 days, 0:00:00")

        eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
        eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
        eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)), "-210 days, 23:12:34")

        eq(str(td(milliseconds=1)), "0:00:00.001000")
        eq(str(td(microseconds=3)), "0:00:00.000003")

        # Codon's timedelta has a smaller range than Python's
        # since it uses a pure microseconds representation, so
        # below case is not supported.
        """
        eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
                   microseconds=999999)),
           "999999999 days, 23:59:59.999999")
        """

    def test_repr(self):
        td = timedelta
        self.assertEqual(repr(td(1)), "timedelta(days=1)")
        self.assertEqual(repr(td(10, 2)), "timedelta(days=10, seconds=2)")
        self.assertEqual(
            repr(td(-10, 2, 400000)),
            "timedelta(days=-10, seconds=2, microseconds=400000)",
        )
        self.assertEqual(repr(td(seconds=60)), "timedelta(seconds=60)")
        self.assertEqual(repr(td()), "timedelta(0)")
        self.assertEqual(repr(td(microseconds=100)), "timedelta(microseconds=100)")
        self.assertEqual(
            repr(td(days=1, microseconds=100)), "timedelta(days=1, microseconds=100)"
        )
        self.assertEqual(
            repr(td(seconds=1, microseconds=100)),
            "timedelta(seconds=1, microseconds=100)",
        )

    def test_resolution_info(self):
        self.assertTrue(timedelta.max > timedelta.min)
        #self.assertEqual(timedelta.min, timedelta(-999999999))
        #self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
        self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))

    """
    def test_overflow(self):
        tiny = timedelta(microseconds=1)  # timedelta.resolution

        td = timedelta.min + tiny
        td -= tiny  # no problem
        self.assertRaises(OverflowError, td.__sub__, tiny)
        self.assertRaises(OverflowError, td.__add__, -tiny)

        td = timedelta.max - tiny
        td += tiny  # no problem
        self.assertRaises(OverflowError, td.__add__, tiny)
        self.assertRaises(OverflowError, td.__sub__, -tiny)

        self.assertRaises(OverflowError, lambda: -timedelta.max)

        day = timedelta(1)
        self.assertRaises(OverflowError, day.__mul__, 10**9)
        self.assertRaises(OverflowError, day.__mul__, 1e9)
        self.assertRaises(OverflowError, day.__truediv__, 1e-20)
        self.assertRaises(OverflowError, day.__truediv__, 1e-10)
        self.assertRaises(OverflowError, day.__truediv__, 9e-10)

    def _test_overflow_special(self):
        day = timedelta(1)
        INF = 1. / 0.
        self.assertRaises(OverflowError, day.__mul__, INF)
        self.assertRaises(OverflowError, day.__mul__, -INF)
    """

    def test_microsecond_rounding(self):
        td = timedelta
        eq = self.assertEqual

        # Single-field rounding.
        eq(td(milliseconds=0.4 / 1000), td(0))  # rounds to 0
        eq(td(milliseconds=-0.4 / 1000), td(0))  # rounds to 0
        eq(td(milliseconds=0.5 / 1000), td(microseconds=0))
        eq(td(milliseconds=-0.5 / 1000), td(microseconds=-0))
        eq(td(milliseconds=0.6 / 1000), td(microseconds=1))
        eq(td(milliseconds=-0.6 / 1000), td(microseconds=-1))
        eq(td(milliseconds=1.5 / 1000), td(microseconds=2))
        eq(td(milliseconds=-1.5 / 1000), td(microseconds=-2))
        eq(td(seconds=0.5 / 10 ** 6), td(microseconds=0))
        eq(td(seconds=-0.5 / 10 ** 6), td(microseconds=-0))
        eq(td(seconds=1 / 2 ** 7), td(microseconds=7812))
        eq(td(seconds=-1 / 2 ** 7), td(microseconds=-7812))

        # Rounding due to contributions from more than one field.
        us_per_hour = 3600e6
        us_per_day = us_per_hour * 24
        eq(td(days=0.4 / us_per_day), td(0))
        eq(td(hours=0.2 / us_per_hour), td(0))
        eq(td(days=0.4 / us_per_day, hours=0.2 / us_per_hour), td(microseconds=1))

        eq(td(days=-0.4 / us_per_day), td(0))
        eq(td(hours=-0.2 / us_per_hour), td(0))
        eq(td(days=-0.4 / us_per_day, hours=-0.2 / us_per_hour), td(microseconds=-1))

        # Test for a patch in Issue 8860
        eq(td(microseconds=0.5), 0.5 * td(microseconds=1.0))
        resolution = td(microseconds=1)  # td.resolution
        eq(td(microseconds=0.5) // resolution, 0.5 * resolution // resolution)

    def test_massive_normalization(self):
        td = timedelta(microseconds=-1)
        self.assertEqual(
            (td.days, td.seconds, td.microseconds), (-1, 24 * 3600 - 1, 999999)
        )

    def test_bool(self):
        self.assertTrue(timedelta(1))
        self.assertTrue(timedelta(0, 1))
        self.assertTrue(timedelta(0, 0, 1))
        self.assertTrue(timedelta(microseconds=1))
        self.assertFalse(timedelta(0))

    def test_division(self):
        t = timedelta(hours=1, minutes=24, seconds=19)
        second = timedelta(seconds=1)
        self.assertEqual(t / second, 5059.0)
        self.assertEqual(t // second, 5059)

        t = timedelta(minutes=2, seconds=30)
        minute = timedelta(minutes=1)
        self.assertEqual(t / minute, 2.5)
        self.assertEqual(t // minute, 2)

        zerotd = timedelta(0)
        # self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
        # self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)

    def test_remainder(self):
        t = timedelta(minutes=2, seconds=30)
        minute = timedelta(minutes=1)
        r = t % minute
        self.assertEqual(r, timedelta(seconds=30))

        t = timedelta(minutes=-2, seconds=30)
        r = t % minute
        self.assertEqual(r, timedelta(seconds=30))

        zerotd = timedelta(0)
        # self.assertRaises(ZeroDivisionError, mod, t, zerotd)

    def test_divmod(self):
        t = timedelta(minutes=2, seconds=30)
        minute = timedelta(minutes=1)
        q, r = divmod(t, minute)
        self.assertEqual(q, 2)
        self.assertEqual(r, timedelta(seconds=30))

        t = timedelta(minutes=-2, seconds=30)
        q, r = divmod(t, minute)
        self.assertEqual(q, -2)
        self.assertEqual(r, timedelta(seconds=30))

        zerotd = timedelta(0)
        # self.assertRaises(ZeroDivisionError, divmod, t, zerotd)


class TestDateOnly(Static[TestCase]):
    def test_delta_non_days_ignored(self):
        dt = date(2000, 1, 2)
        delta = timedelta(days=1, hours=2, minutes=3, seconds=4, microseconds=5)
        days = timedelta(delta.days)
        self.assertEqual(days, timedelta(1))

        dt2 = dt + delta
        self.assertEqual(dt2, dt + days)

        dt2 = delta + dt
        self.assertEqual(dt2, dt + days)

        dt2 = dt - delta
        self.assertEqual(dt2, dt - days)

        delta = -delta
        days = timedelta(delta.days)
        self.assertEqual(days, timedelta(-2))

        dt2 = dt + delta
        self.assertEqual(dt2, dt + days)

        dt2 = delta + dt
        self.assertEqual(dt2, dt + days)

        dt2 = dt - delta
        self.assertEqual(dt2, dt - days)


class TestDate(Static[TestCase]):
    theclass: type

    def test_basic_attributes(self):
        dt = self.theclass(2002, 3, 1)
        self.assertEqual(dt.year, 2002)
        self.assertEqual(dt.month, 3)
        self.assertEqual(dt.day, 1)

    def test_ordinal_conversions(self):
        # Check some fixed values.
        for y, m, d, n in [
            (1, 1, 1, 1),  # calendar origin
            (1, 12, 31, 365),
            (2, 1, 1, 366),
            # first example from "Calendrical Calculations"
            (1945, 11, 12, 710347),
        ]:
            dt = self.theclass(y, m, d)
            self.assertEqual(n, dt.toordinal())
            fromord = self.theclass.fromordinal(n)
            self.assertEqual(dt, fromord)
            if hasattr(fromord, "hour"):
                # if we're checking something fancier than a date, verify
                # the extra fields have been zeroed out
                self.assertEqual(fromord.hour, 0)
                self.assertEqual(fromord.minute, 0)
                self.assertEqual(fromord.second, 0)
                self.assertEqual(fromord.microsecond, 0)

        # Check first and last days of year spottily across the whole
        # range of years supported.
        for year in range(MINYEAR, MAXYEAR + 1, 7):
            # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
            d = self.theclass(year, 1, 1)
            n = d.toordinal()
            d2 = self.theclass.fromordinal(n)
            self.assertEqual(d, d2)
            # Verify that moving back a day gets to the end of year-1.
            if year > 1:
                d = self.theclass.fromordinal(n - 1)
                d2 = self.theclass(year - 1, 12, 31)
                self.assertEqual(d, d2)
                self.assertEqual(d2.toordinal(), n - 1)

        # Test every day in a leap-year and a non-leap year.
        dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        for year, isleap in (2000, True), (2002, False):
            n = self.theclass(year, 1, 1).toordinal()
            for month, maxday in zip(range(1, 13), dim):
                if month == 2 and isleap:
                    maxday += 1
                for day in range(1, maxday + 1):
                    d = self.theclass(year, month, day)
                    self.assertEqual(d.toordinal(), n)
                    self.assertEqual(d, self.theclass.fromordinal(n))
                    n += 1

    """
    def test_extreme_ordinals(self):
        a = self.theclass.min
        a = self.theclass(a.year, a.month, a.day)  # get rid of time parts
        aord = a.toordinal()
        b = a.fromordinal(aord)
        self.assertEqual(a, b)

        self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))

        b = a + timedelta(days=1)
        self.assertEqual(b.toordinal(), aord + 1)
        self.assertEqual(b, self.theclass.fromordinal(aord + 1))

        a = self.theclass.max
        a = self.theclass(a.year, a.month, a.day)  # get rid of time parts
        aord = a.toordinal()
        b = a.fromordinal(aord)
        self.assertEqual(a, b)

        self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))

        b = a - timedelta(days=1)
        self.assertEqual(b.toordinal(), aord - 1)
        self.assertEqual(b, self.theclass.fromordinal(aord - 1))
    """

    def test_bad_constructor_arguments(self):
        # bad years
        self.theclass(MINYEAR, 1, 1)  # no exception
        self.theclass(MAXYEAR, 1, 1)  # no exception

        def make(theclass, a, b, c):
            return self.theclass(a, b, c)

        self.assertRaises(ValueError, make(self.theclass, ...), MINYEAR - 1, 1, 1)
        self.assertRaises(ValueError, make(self.theclass, ...), MAXYEAR + 1, 1, 1)
        # bad months
        self.theclass(2000, 1, 1)  # no exception
        self.theclass(2000, 12, 1)  # no exception
        self.assertRaises(ValueError, make(self.theclass, ...), 2000, 0, 1)
        self.assertRaises(ValueError, make(self.theclass, ...), 2000, 13, 1)
        # bad days
        self.theclass(2000, 2, 29)  # no exception
        self.theclass(2004, 2, 29)  # no exception
        self.theclass(2400, 2, 29)  # no exception
        self.assertRaises(ValueError, make(self.theclass, ...), 2000, 2, 30)
        self.assertRaises(ValueError, make(self.theclass, ...), 2001, 2, 29)
        self.assertRaises(ValueError, make(self.theclass, ...), 2100, 2, 29)
        self.assertRaises(ValueError, make(self.theclass, ...), 1900, 2, 29)
        self.assertRaises(ValueError, make(self.theclass, ...), 2000, 1, 0)
        self.assertRaises(ValueError, make(self.theclass, ...), 2000, 1, 32)

    def test_hash_equality(self):
        d = self.theclass(2000, 12, 31)
        # same thing
        e = self.theclass(2000, 12, 31)
        self.assertEqual(d, e)
        self.assertEqual(hash(d), hash(e))

        dic = {d: 1}
        dic[e] = 2
        self.assertEqual(len(dic), 1)
        self.assertEqual(dic[d], 2)
        self.assertEqual(dic[e], 2)

        d = self.theclass(2001, 1, 1)
        # same thing
        e = self.theclass(2001, 1, 1)
        self.assertEqual(d, e)
        self.assertEqual(hash(d), hash(e))

        dic = {d: 1}
        dic[e] = 2
        self.assertEqual(len(dic), 1)
        self.assertEqual(dic[d], 2)
        self.assertEqual(dic[e], 2)

    def test_computations(self):
        a = self.theclass(2002, 1, 31)
        b = self.theclass(1956, 1, 31)
        c = self.theclass(2001, 2, 1)

        diff = a - b
        self.assertEqual(diff.days, 46 * 365 + len(range(1956, 2002, 4)))
        self.assertEqual(diff.seconds, 0)
        self.assertEqual(diff.microseconds, 0)

        day = timedelta(1)
        week = timedelta(7)
        a = self.theclass(2002, 3, 2)
        self.assertEqual(a + day, self.theclass(2002, 3, 3))
        self.assertEqual(day + a, self.theclass(2002, 3, 3))
        self.assertEqual(a - day, self.theclass(2002, 3, 1))
        self.assertEqual(-day + a, self.theclass(2002, 3, 1))
        self.assertEqual(a + week, self.theclass(2002, 3, 9))
        self.assertEqual(a - week, self.theclass(2002, 2, 23))
        self.assertEqual(a + 52 * week, self.theclass(2003, 3, 1))
        self.assertEqual(a - 52 * week, self.theclass(2001, 3, 3))
        self.assertEqual((a + week) - a, week)
        self.assertEqual((a + day) - a, day)
        self.assertEqual((a - week) - a, -week)
        self.assertEqual((a - day) - a, -day)
        self.assertEqual(a - (a + week), -week)
        self.assertEqual(a - (a + day), -day)
        self.assertEqual(a - (a - week), week)
        self.assertEqual(a - (a - day), day)
        self.assertEqual(c - (c - day), day)

        """
        # Add/sub ints or floats should be illegal
        for i in 1, 1.0:
            self.assertRaises(TypeError, lambda: a+i)
            self.assertRaises(TypeError, lambda: a-i)
            self.assertRaises(TypeError, lambda: i+a)
            self.assertRaises(TypeError, lambda: i-a)

        # delta - date is senseless.
        self.assertRaises(TypeError, lambda: day - a)
        # mixing date and (delta or date) via * or // is senseless
        self.assertRaises(TypeError, lambda: day * a)
        self.assertRaises(TypeError, lambda: a * day)
        self.assertRaises(TypeError, lambda: day // a)
        self.assertRaises(TypeError, lambda: a // day)
        self.assertRaises(TypeError, lambda: a * a)
        self.assertRaises(TypeError, lambda: a // a)
        # date + date is senseless
        self.assertRaises(TypeError, lambda: a + a)
        """

    """
    def test_overflow(self):
        tiny = self.theclass.resolution

        for delta in [tiny, timedelta(1), timedelta(2)]:
            dt = self.theclass.min + delta
            dt -= delta  # no problem
            self.assertRaises(OverflowError, dt.__sub__, delta)
            self.assertRaises(OverflowError, dt.__add__, -delta)

            dt = self.theclass.max - delta
            dt += delta  # no problem
            self.assertRaises(OverflowError, dt.__add__, delta)
            self.assertRaises(OverflowError, dt.__sub__, -delta)
    """

    def test_fromtimestamp(self):
        import time

        # Try an arbitrary fixed value.
        year, month, day = 1999, 9, 19
        ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
        d = self.theclass.fromtimestamp(ts)
        self.assertEqual(d.year, year)
        self.assertEqual(d.month, month)
        self.assertEqual(d.day, day)

    def test_today(self):
        import time

        # We claim that today() is like fromtimestamp(time.time()), so
        # prove it.
        today = self.theclass.today()
        todayagain = today

        for dummy in range(3):
            today = self.theclass.today()
            ts = time.time()
            todayagain = self.theclass.fromtimestamp(ts)
            if today == todayagain:
                break
            # There are several legit reasons that could fail:
            # 1. It recently became midnight, between the today() and the
            #    time() calls.
            # 2. The platform time() has such fine resolution that we'll
            #    never get the same value twice.
            # 3. The platform time() has poor resolution, and we just
            #    happened to call today() right before a resolution quantum
            #    boundary.
            # 4. The system clock got fiddled between calls.
            # In any case, wait a little while and try again.
            time.sleep(0.1)

        # It worked or it didn't.  If it didn't, assume it's reason #2, and
        # let the test pass if they're within half a second of each other.
        if today != todayagain:
            self.assertAlmostEqual(todayagain, today, delta=timedelta(seconds=0.5))

    def test_weekday(self):
        for i in range(7):
            # March 4, 2002 is a Monday
            self.assertEqual(self.theclass(2002, 3, 4 + i).weekday(), i)
            self.assertEqual(self.theclass(2002, 3, 4 + i).isoweekday(), i + 1)
            # January 2, 1956 is a Monday
            self.assertEqual(self.theclass(1956, 1, 2 + i).weekday(), i)
            self.assertEqual(self.theclass(1956, 1, 2 + i).isoweekday(), i + 1)

    def test_isocalendar(self):
        # Check examples from
        # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
        week_mondays = [
            ((2003, 12, 22), (2003, 52, 1)),
            ((2003, 12, 29), (2004, 1, 1)),
            ((2004, 1, 5), (2004, 2, 1)),
            ((2009, 12, 21), (2009, 52, 1)),
            ((2009, 12, 28), (2009, 53, 1)),
            ((2010, 1, 4), (2010, 1, 1)),
        ]

        test_cases = []
        for cal_date, iso_date in week_mondays:
            base_date = self.theclass(*cal_date)
            # Adds one test case for every day of the specified weeks
            for i in range(7):
                new_date = base_date + timedelta(i)
                new_iso = iso_date[0:2] + (iso_date[2] + i,)
                test_cases.append((new_date, new_iso))

        for d, exp_iso in test_cases:
            self.assertEqual(d.isocalendar(), exp_iso)

            # Check that the tuple contents are accessible by field name
            t = d.isocalendar()
            self.assertEqual((t.year, t.week, t.weekday), exp_iso)

    def test_iso_long_years(self):
        # Calculate long ISO years and compare to table from
        # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
        ISO_LONG_YEARS_TABLE = """
              4   32   60   88
              9   37   65   93
             15   43   71   99
             20   48   76
             26   54   82
            105  133  161  189
            111  139  167  195
            116  144  172
            122  150  178
            128  156  184
            201  229  257  285
            207  235  263  291
            212  240  268  296
            218  246  274
            224  252  280
            303  331  359  387
            308  336  364  392
            314  342  370  398
            320  348  376
            325  353  381
        """
        iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
        L = []
        for i in range(400):
            d = self.theclass(2000 + i, 12, 31).isocalendar()
            d1 = self.theclass(1600 + i, 12, 31).isocalendar()
            self.assertEqual((d.week, d.weekday), (d1.week, d1.weekday))
            if d.week == 53:
                L.append(i)
        self.assertEqual(L, iso_long_years)

    def test_isoformat(self):
        t = self.theclass(2, 3, 2)
        self.assertEqual(t.isoformat(), "0002-03-02")

    def test_ctime(self):
        t = self.theclass(2002, 3, 2)
        self.assertEqual(t.ctime(), "Sat Mar  2 00:00:00 2002")

    def test_resolution_info(self):
        self.assertTrue(self.theclass.max > self.theclass.min)

    """
    def test_extreme_timedelta(self):
        big = self.theclass.max - self.theclass.min
        # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
        n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
        # n == 315537897599999999 ~= 2**58.13
        justasbig = timedelta(0, 0, n)
        self.assertEqual(big, justasbig)
        self.assertEqual(self.theclass.min + big, self.theclass.max)
        self.assertEqual(self.theclass.max - big, self.theclass.min)
    """

    def test_timetuple(self):
        from time import struct_time

        for i in range(7):
            # January 2, 1956 is a Monday (0)
            d = self.theclass(1956, 1, 2 + i)
            t = d.timetuple()
            self.assertEqual(t, struct_time(1956, 1, 2 + i, 0, 0, 0, i, 2 + i, -1))
            # February 1, 1956 is a Wednesday (2)
            d = self.theclass(1956, 2, 1 + i)
            t = d.timetuple()
            self.assertEqual(
                t, struct_time(1956, 2, 1 + i, 0, 0, 0, (2 + i) % 7, 32 + i, -1)
            )
            # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
            # of the year.
            d = self.theclass(1956, 3, 1 + i)
            t = d.timetuple()
            self.assertEqual(
                t, struct_time(1956, 3, 1 + i, 0, 0, 0, (3 + i) % 7, 61 + i, -1)
            )
            self.assertEqual(t.tm_year, 1956)
            self.assertEqual(t.tm_mon, 3)
            self.assertEqual(t.tm_mday, 1 + i)
            self.assertEqual(t.tm_hour, 0)
            self.assertEqual(t.tm_min, 0)
            self.assertEqual(t.tm_sec, 0)
            self.assertEqual(t.tm_wday, (3 + i) % 7)
            self.assertEqual(t.tm_yday, 61 + i)
            self.assertEqual(t.tm_isdst, -1)

    def test_compare(self):
        t1 = self.theclass(2, 3, 4)
        t2 = self.theclass(2, 3, 4)
        self.assertEqual(t1, t2)
        self.assertTrue(t1 <= t2)
        self.assertTrue(t1 >= t2)
        self.assertFalse(t1 != t2)
        self.assertFalse(t1 < t2)
        self.assertFalse(t1 > t2)

        for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
            t2 = self.theclass(*args)  # this is larger than t1
            self.assertTrue(t1 < t2)
            self.assertTrue(t2 > t1)
            self.assertTrue(t1 <= t2)
            self.assertTrue(t2 >= t1)
            self.assertTrue(t1 != t2)
            self.assertTrue(t2 != t1)
            self.assertFalse(t1 == t2)
            self.assertFalse(t2 == t1)
            self.assertFalse(t1 > t2)
            self.assertFalse(t2 < t1)
            self.assertFalse(t1 >= t2)
            self.assertFalse(t2 <= t1)

        """
        for badarg in OTHERSTUFF:
            self.assertEqual(t1 == badarg, False)
            self.assertEqual(t1 != badarg, True)
            self.assertEqual(badarg == t1, False)
            self.assertEqual(badarg != t1, True)

            self.assertRaises(TypeError, lambda: t1 < badarg)
            self.assertRaises(TypeError, lambda: t1 > badarg)
            self.assertRaises(TypeError, lambda: t1 >= badarg)
            self.assertRaises(TypeError, lambda: badarg <= t1)
            self.assertRaises(TypeError, lambda: badarg < t1)
            self.assertRaises(TypeError, lambda: badarg > t1)
            self.assertRaises(TypeError, lambda: badarg >= t1)
        """

    def test_bool(self):
        # All dates are considered true.
        # self.assertTrue(self.theclass.min)
        # self.assertTrue(self.theclass.max)
        self.assertTrue(self.theclass(1, 1, 1))

    def test_replace(self):
        cls = self.theclass
        args = (1, 2, 3)
        base = cls(*args)
        self.assertEqual(base, base.replace())
        self.assertEqual(base.replace(year=2), cls(2, 2, 3))
        self.assertEqual(base.replace(month=3), cls(1, 3, 3))
        self.assertEqual(base.replace(day=4), cls(1, 2, 4))

        # Out of bounds.
        base = cls(2000, 2, 29)
        self.assertRaises(ValueError, base.replace, year=2001)

    def test_fromisoformat(self):
        # Test that isoformat() is reversible
        base_dates = [
            (1, 1, 1),
            (1000, 2, 14),
            (1900, 1, 1),
            (2000, 2, 29),
            (2004, 11, 12),
            (2004, 4, 3),
            (2017, 5, 30),
        ]

        for dt_tuple in base_dates:
            dt = self.theclass(*dt_tuple)
            dt_str = dt.isoformat()
            dt_rt = self.theclass.fromisoformat(dt.isoformat())
            self.assertEqual(dt, dt_rt)

    def test_fromisoformat_fails(self):
        # Test that fromisoformat() fails on invalid values
        bad_strs = [
            "",  # Empty string
            "\ud800",  # bpo-34454: Surrogate code point
            "009-03-04",  # Not 10 characters
            "123456789",  # Not a date
            "200a-12-04",  # Invalid character in year
            "2009-1a-04",  # Invalid character in month
            "2009-12-0a",  # Invalid character in day
            "2009-01-32",  # Invalid day
            "2009-02-29",  # Invalid leap day
            "20090228",  # Valid ISO8601 output not from isoformat()
            "2009\ud80002\ud80028",  # Separators are surrogate codepoints
        ]

        for bad_str in bad_strs:
            self.assertRaises(ValueError, self.theclass.fromisoformat, bad_str)

    def test_fromisocalendar(self):
        # For each test case, assert that fromisocalendar is the
        # inverse of the isocalendar function
        dates = [
            (2016, 4, 3),
            (2005, 1, 2),  # (2004, 53, 7)
            (2008, 12, 30),  # (2009, 1, 2)
            (2010, 1, 2),  # (2009, 53, 6)
            (2009, 12, 31),  # (2009, 53, 4)
            (1900, 1, 1),  # Unusual non-leap year (year % 100 == 0)
            (1900, 12, 31),
            (2000, 1, 1),  # Unusual leap year (year % 400 == 0)
            (2000, 12, 31),
            (2004, 1, 1),  # Leap year
            (2004, 12, 31),
            (1, 1, 1),
            (9999, 12, 31),
            (MINYEAR, 1, 1),
            (MAXYEAR, 12, 31),
        ]

        for datecomps in dates:
            dobj = self.theclass(*datecomps)
            isocal = dobj.isocalendar()

            d_roundtrip = self.theclass.fromisocalendar(*isocal)

            self.assertEqual(dobj, d_roundtrip)

    def test_fromisocalendar_value_errors(self):
        isocals = [
            (2019, 0, 1),
            (2019, -1, 1),
            (2019, 54, 1),
            (2019, 1, 0),
            (2019, 1, -1),
            (2019, 1, 8),
            (2019, 53, 1),
            (10000, 1, 1),
            (0, 1, 1),
            (9999999, 1, 1),
            (2 << 32, 1, 1),
            (2019, 2 << 32, 1),
            (2019, 1, 2 << 32),
        ]

        for isocal in isocals:
            self.assertRaises(ValueError, self.theclass.fromisocalendar, *isocal)


class TestDateTime(Static[TestDate[theclass]]):
    theclass: type

    def test_basic_attributes(self):
        dt = self.theclass(2002, 3, 1, 12, 0)
        self.assertEqual(dt.year, 2002)
        self.assertEqual(dt.month, 3)
        self.assertEqual(dt.day, 1)
        self.assertEqual(dt.hour, 12)
        self.assertEqual(dt.minute, 0)
        self.assertEqual(dt.second, 0)
        self.assertEqual(dt.microsecond, 0)

    def test_basic_attributes_nonzero(self):
        # Make sure all attributes are non-zero so bugs in
        # bit-shifting access show up.
        dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
        self.assertEqual(dt.year, 2002)
        self.assertEqual(dt.month, 3)
        self.assertEqual(dt.day, 1)
        self.assertEqual(dt.hour, 12)
        self.assertEqual(dt.minute, 59)
        self.assertEqual(dt.second, 59)
        self.assertEqual(dt.microsecond, 8000)

    def test_isoformat(self):
        t = self.theclass(1, 2, 3, 4, 5, 1, 123)
        self.assertEqual(t.isoformat(), "0001-02-03T04:05:01.000123")
        self.assertEqual(t.isoformat("T"), "0001-02-03T04:05:01.000123")
        self.assertEqual(t.isoformat(" "), "0001-02-03 04:05:01.000123")
        self.assertEqual(t.isoformat("\x00"), "0001-02-03\x0004:05:01.000123")
        # bpo-34482: Check that surrogates are handled properly.
        self.assertEqual(t.isoformat("\ud800"), "0001-02-03\ud80004:05:01.000123")
        self.assertEqual(t.isoformat(timespec="hours"), "0001-02-03T04")
        self.assertEqual(t.isoformat(timespec="minutes"), "0001-02-03T04:05")
        self.assertEqual(t.isoformat(timespec="seconds"), "0001-02-03T04:05:01")
        self.assertEqual(
            t.isoformat(timespec="milliseconds"), "0001-02-03T04:05:01.000"
        )
        self.assertEqual(
            t.isoformat(timespec="microseconds"), "0001-02-03T04:05:01.000123"
        )
        self.assertEqual(t.isoformat(timespec="auto"), "0001-02-03T04:05:01.000123")
        self.assertEqual(t.isoformat(sep=" ", timespec="minutes"), "0001-02-03 04:05")
        # str is ISO format with the separator forced to a blank.
        self.assertEqual(str(t), "0001-02-03 04:05:01.000123")

        # t = self.theclass(1, 2, 3, 4, 5, 1, 999500, tzinfo=timezone.utc)
        # self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.999+00:00")

        t = self.theclass(1, 2, 3, 4, 5, 1, 999500)
        self.assertEqual(
            t.isoformat(timespec="milliseconds"), "0001-02-03T04:05:01.999"
        )

        t = self.theclass(1, 2, 3, 4, 5, 1)
        self.assertEqual(t.isoformat(timespec="auto"), "0001-02-03T04:05:01")
        self.assertEqual(
            t.isoformat(timespec="milliseconds"), "0001-02-03T04:05:01.000"
        )
        self.assertEqual(
            t.isoformat(timespec="microseconds"), "0001-02-03T04:05:01.000000"
        )

        t = self.theclass(2, 3, 2)
        self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
        self.assertEqual(t.isoformat("T"), "0002-03-02T00:00:00")
        self.assertEqual(t.isoformat(" "), "0002-03-02 00:00:00")
        # str is ISO format with the separator forced to a blank.
        self.assertEqual(str(t), "0002-03-02 00:00:00")
        # ISO format with timezone
        # tz = FixedOffset(timedelta(seconds=16), 'XXX')
        # t = self.theclass(2, 3, 2, tzinfo=tz)
        # self.assertEqual(t.isoformat(), "0002-03-02T00:00:00+00:00:16")

    """
    def test_isoformat_timezone(self):
        tzoffsets = [
            ('05:00', timedelta(hours=5)),
            ('02:00', timedelta(hours=2)),
            ('06:27', timedelta(hours=6, minutes=27)),
            ('12:32:30', timedelta(hours=12, minutes=32, seconds=30)),
            ('02:04:09.123456', timedelta(hours=2, minutes=4, seconds=9, microseconds=123456))
        ]

        tzinfos = [
            ('', None),
            ('+00:00', timezone.utc),
            ('+00:00', timezone(timedelta(0))),
        ]

        tzinfos += [
            (prefix + expected, timezone(sign * td))
            for expected, td in tzoffsets
            for prefix, sign in [('-', -1), ('+', 1)]
        ]

        dt_base = self.theclass(2016, 4, 1, 12, 37, 9)
        exp_base = '2016-04-01T12:37:09'

        for exp_tz, tzi in tzinfos:
            dt = dt_base.replace(tzinfo=tzi)
            exp = exp_base + exp_tz
            with self.subTest(tzi=tzi):
                assert dt.isoformat() == exp
    """

    """
    def test_more_ctime(self):
        # Test fields that TestDate doesn't touch.
        import time

        t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
        self.assertEqual(t.ctime(), "Sat Mar  2 18:03:05 2002")
        # Oops!  The next line fails on Win2K under MSVC 6, so it's commented
        # out.  The difference is that t.ctime() produces " 2" for the day,
        # but platform ctime() produces "02" for the day.  According to
        # C99, t.ctime() is correct here.
        # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))

        # So test a case where that difference doesn't matter.
        t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
        self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
    """

    def test_tz_independent_comparing(self):
        dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
        dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
        dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
        self.assertEqual(dt1, dt3)
        self.assertTrue(dt2 > dt3)

        # Make sure comparison doesn't forget microseconds, and isn't done
        # via comparing a float timestamp (an IEEE double doesn't have enough
        # precision to span microsecond resolution across years 1 through 9999,
        # so comparing via timestamp necessarily calls some distinct values
        # equal).
        dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
        us = timedelta(microseconds=1)
        dt2 = dt1 + us
        self.assertEqual(dt2 - dt1, us)
        self.assertTrue(dt1 < dt2)

    def test_bad_constructor_arguments(self):
        # bad years
        self.theclass(MINYEAR, 1, 1)  # no exception
        self.theclass(MAXYEAR, 1, 1)  # no exception

        make_dt1 = lambda a, b, c: self.theclass(a, b, c)
        make_dt2 = lambda a, b, c, d: self.theclass(a, b, c, d)
        make_dt3 = lambda a, b, c, d, e: self.theclass(a, b, c, d, e)
        make_dt4 = lambda a, b, c, d, e, f: self.theclass(a, b, c, d, e, f)
        make_dt5 = lambda a, b, c, d, e, f, g: self.theclass(a, b, c, d, e, f, g)

        self.assertRaises(ValueError, make_dt1, MINYEAR - 1, 1, 1)
        self.assertRaises(ValueError, make_dt1, MAXYEAR + 1, 1, 1)
        # bad months
        self.theclass(2000, 1, 1)  # no exception
        self.theclass(2000, 12, 1)  # no exception
        self.assertRaises(ValueError, make_dt1, 2000, 0, 1)
        self.assertRaises(ValueError, make_dt1, 2000, 13, 1)
        # bad days
        self.theclass(2000, 2, 29)  # no exception
        self.theclass(2004, 2, 29)  # no exception
        self.theclass(2400, 2, 29)  # no exception
        self.assertRaises(ValueError, make_dt1, 2000, 2, 30)
        self.assertRaises(ValueError, make_dt1, 2001, 2, 29)
        self.assertRaises(ValueError, make_dt1, 2100, 2, 29)
        self.assertRaises(ValueError, make_dt1, 1900, 2, 29)
        self.assertRaises(ValueError, make_dt1, 2000, 1, 0)
        self.assertRaises(ValueError, make_dt1, 2000, 1, 32)
        # bad hours
        self.theclass(2000, 1, 31, 0)  # no exception
        self.theclass(2000, 1, 31, 23)  # no exception
        self.assertRaises(ValueError, make_dt2, 2000, 1, 31, -1)
        self.assertRaises(ValueError, make_dt2, 2000, 1, 31, 24)
        # bad minutes
        self.theclass(2000, 1, 31, 23, 0)  # no exception
        self.theclass(2000, 1, 31, 23, 59)  # no exception
        self.assertRaises(ValueError, make_dt3, 2000, 1, 31, 23, -1)
        self.assertRaises(ValueError, make_dt3, 2000, 1, 31, 23, 60)
        # bad seconds
        self.theclass(2000, 1, 31, 23, 59, 0)  # no exception
        self.theclass(2000, 1, 31, 23, 59, 59)  # no exception
        self.assertRaises(ValueError, make_dt4, 2000, 1, 31, 23, 59, -1)
        self.assertRaises(ValueError, make_dt4, 2000, 1, 31, 23, 59, 60)
        # bad microseconds
        self.theclass(2000, 1, 31, 23, 59, 59, 0)  # no exception
        self.theclass(2000, 1, 31, 23, 59, 59, 999999)  # no exception
        self.assertRaises(ValueError, make_dt5, 2000, 1, 31, 23, 59, 59, -1)
        self.assertRaises(ValueError, make_dt5, 2000, 1, 31, 23, 59, 59, 1000000)
        """
        # bad fold
        self.assertRaises(ValueError, self.theclass,
                          2000, 1, 31, fold=-1)
        self.assertRaises(ValueError, self.theclass,
                          2000, 1, 31, fold=2)
        # Positional fold:
        self.assertRaises(TypeError, self.theclass,
                          2000, 1, 31, 23, 59, 59, 0, None, 1)
        """

    def test_hash_equality(self):
        d = self.theclass(2000, 12, 31, 23, 30, 17)
        e = self.theclass(2000, 12, 31, 23, 30, 17)
        self.assertEqual(d, e)
        self.assertEqual(hash(d), hash(e))

        dic = {d: 1}
        dic[e] = 2
        self.assertEqual(len(dic), 1)
        self.assertEqual(dic[d], 2)
        self.assertEqual(dic[e], 2)

        d = self.theclass(2001, 1, 1, 0, 5, 17)
        e = self.theclass(2001, 1, 1, 0, 5, 17)
        self.assertEqual(d, e)
        self.assertEqual(hash(d), hash(e))

        dic = {d: 1}
        dic[e] = 2
        self.assertEqual(len(dic), 1)
        self.assertEqual(dic[d], 2)
        self.assertEqual(dic[e], 2)

    def test_computations(self):
        a = self.theclass(2002, 1, 31)
        b = self.theclass(1956, 1, 31)
        diff = a - b
        self.assertEqual(diff.days, 46 * 365 + len(range(1956, 2002, 4)))
        self.assertEqual(diff.seconds, 0)
        self.assertEqual(diff.microseconds, 0)
        a = self.theclass(2002, 3, 2, 17, 6)
        millisec = timedelta(0, 0, 1000)
        hour = timedelta(0, 3600)
        day = timedelta(1)
        week = timedelta(7)
        self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
        self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
        self.assertEqual(a + 10 * hour, self.theclass(2002, 3, 3, 3, 6))
        self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
        self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
        self.assertEqual(a - hour, a + -hour)
        self.assertEqual(a - 20 * hour, self.theclass(2002, 3, 1, 21, 6))
        self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
        self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
        self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
        self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
        self.assertEqual(a + 52 * week, self.theclass(2003, 3, 1, 17, 6))
        self.assertEqual(a - 52 * week, self.theclass(2001, 3, 3, 17, 6))
        self.assertEqual((a + week) - a, week)
        self.assertEqual((a + day) - a, day)
        self.assertEqual((a + hour) - a, hour)
        self.assertEqual((a + millisec) - a, millisec)
        self.assertEqual((a - week) - a, -week)
        self.assertEqual((a - day) - a, -day)
        self.assertEqual((a - hour) - a, -hour)
        self.assertEqual((a - millisec) - a, -millisec)
        self.assertEqual(a - (a + week), -week)
        self.assertEqual(a - (a + day), -day)
        self.assertEqual(a - (a + hour), -hour)
        self.assertEqual(a - (a + millisec), -millisec)
        self.assertEqual(a - (a - week), week)
        self.assertEqual(a - (a - day), day)
        self.assertEqual(a - (a - hour), hour)
        self.assertEqual(a - (a - millisec), millisec)
        self.assertEqual(
            a + (week + day + hour + millisec),
            self.theclass(2002, 3, 10, 18, 6, 0, 1000),
        )
        self.assertEqual(
            a + (week + day + hour + millisec), (((a + week) + day) + hour) + millisec
        )
        self.assertEqual(
            a - (week + day + hour + millisec),
            self.theclass(2002, 2, 22, 16, 5, 59, 999000),
        )
        self.assertEqual(
            a - (week + day + hour + millisec), (((a - week) - day) - hour) - millisec
        )
        """
        # Add/sub ints or floats should be illegal
        for i in 1, 1.0:
            self.assertRaises(TypeError, lambda: a+i)
            self.assertRaises(TypeError, lambda: a-i)
            self.assertRaises(TypeError, lambda: i+a)
            self.assertRaises(TypeError, lambda: i-a)

        # delta - datetime is senseless.
        self.assertRaises(TypeError, lambda: day - a)
        # mixing datetime and (delta or datetime) via * or // is senseless
        self.assertRaises(TypeError, lambda: day * a)
        self.assertRaises(TypeError, lambda: a * day)
        self.assertRaises(TypeError, lambda: day // a)
        self.assertRaises(TypeError, lambda: a // day)
        self.assertRaises(TypeError, lambda: a * a)
        self.assertRaises(TypeError, lambda: a // a)
        # datetime + datetime is senseless
        self.assertRaises(TypeError, lambda: a + a)
        """

    def test_more_compare(self):
        # The test_compare() inherited from TestDate covers the error cases.
        # We just want to test lexicographic ordering on the members datetime
        # has that date lacks.
        args = (2000, 11, 29, 20, 58, 16, 999998)
        newargsx = [
            (2001, 11, 29, 20, 58, 16, 999998),
            (2000, 12, 29, 20, 58, 16, 999998),
            (2000, 11, 30, 20, 58, 16, 999998),
            (2000, 11, 29, 21, 58, 16, 999998),
            (2000, 11, 29, 20, 59, 16, 999998),
            (2000, 11, 29, 20, 58, 17, 999998),
            (2000, 11, 29, 20, 58, 16, 999999),
        ]
        t1 = self.theclass(*args)
        t2 = self.theclass(*args)
        self.assertEqual(t1, t2)
        self.assertTrue(t1 <= t2)
        self.assertTrue(t1 >= t2)
        self.assertFalse(t1 != t2)
        self.assertFalse(t1 < t2)
        self.assertFalse(t1 > t2)

        for i in range(len(args)):
            newargs = newargsx[i]
            t2 = self.theclass(*newargs)  # this is larger than t1
            self.assertTrue(t1 < t2)
            self.assertTrue(t2 > t1)
            self.assertTrue(t1 <= t2)
            self.assertTrue(t2 >= t1)
            self.assertTrue(t1 != t2)
            self.assertTrue(t2 != t1)
            self.assertFalse(t1 == t2)
            self.assertFalse(t2 == t1)
            self.assertFalse(t1 > t2)
            self.assertFalse(t2 < t1)
            self.assertFalse(t1 >= t2)
            self.assertFalse(t2 <= t1)

    # A helper for timestamp constructor tests.
    def verify_field_equality(self, expected, got):
        self.assertEqual(expected.tm_year, got.year)
        self.assertEqual(expected.tm_mon, got.month)
        self.assertEqual(expected.tm_mday, got.day)
        self.assertEqual(expected.tm_hour, got.hour)
        self.assertEqual(expected.tm_min, got.minute)
        self.assertEqual(expected.tm_sec, got.second)

    def test_fromtimestamp(self):
        import time

        ts = time.time()
        expected = time.localtime(int(ts))
        got = self.theclass.fromtimestamp(ts)
        self.verify_field_equality(expected, got)

    """
    def test_utcfromtimestamp(self):
        import time

        ts = time.time()
        expected = time.gmtime(int(ts))
        got = self.theclass.utcfromtimestamp(ts)
        self.verify_field_equality(expected, got)
    """

    def test_timestamp_naive(self):
        """
        t = self.theclass(1970, 1, 1)
        self.assertEqual(t.timestamp(), 18000.0)
        t = self.theclass(1970, 1, 1, 1, 2, 3, 4)
        self.assertEqual(t.timestamp(),
                         18000.0 + 3600 + 2*60 + 3 + 4*1e-6)
        """

        t = self.theclass(1970, 1, 1)
        self.assertEqual(t.timestamp(), 0)
        t = self.theclass(1970, 1, 1, 1, 2, 3, 4)
        self.assertEqual(t.timestamp(), 3600 + 2 * 60 + 3 + 4 * 1e-6)

        """
        # Missing hour
        t0 = self.theclass(2012, 3, 11, 2, 30)
        t1 = t0.replace(fold=1)
        self.assertEqual(self.theclass.fromtimestamp(t1.timestamp()),
                         t0 - timedelta(hours=1))
        self.assertEqual(self.theclass.fromtimestamp(t0.timestamp()),
                         t1 + timedelta(hours=1))
        # Ambiguous hour defaults to DST
        t = self.theclass(2012, 11, 4, 1, 30)
        self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t)

        # Timestamp may raise an overflow error on some platforms
        # XXX: Do we care to support the first and last year?
        for t in [self.theclass(2,1,1), self.theclass(9998,12,12)]:
            s = None
            try:
                s = t.timestamp()
            except OverflowError:
                continue

            self.assertEqual(self.theclass.fromtimestamp(s), t)
        """

    """
    def test_timestamp_aware(self):
        t = self.theclass(1970, 1, 1, tzinfo=timezone.utc)
        self.assertEqual(t.timestamp(), 0.0)
        t = self.theclass(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc)
        self.assertEqual(t.timestamp(),
                         3600 + 2*60 + 3 + 4*1e-6)
        t = self.theclass(1970, 1, 1, 1, 2, 3, 4,
                          tzinfo=timezone(timedelta(hours=-5), 'EST'))
        self.assertEqual(t.timestamp(),
                         18000 + 3600 + 2*60 + 3 + 4*1e-6)
    """
    """
    def test_microsecond_rounding(self):
        for fts in (self.theclass.fromtimestamp,
                    self.theclass.utcfromtimestamp):
            zero = fts(0)
            self.assertEqual(zero.second, 0)
            self.assertEqual(zero.microsecond, 0)
            one = fts(1e-6)
            minus_one = fts(-1e-6)

            self.assertEqual(minus_one.second, 59)
            self.assertEqual(minus_one.microsecond, 999999)

            t = fts(-1e-8)
            self.assertEqual(t, zero)
            t = fts(-9e-7)
            self.assertEqual(t, minus_one)
            t = fts(-1e-7)
            self.assertEqual(t, zero)
            t = fts(-1/2**7)
            self.assertEqual(t.second, 59)
            self.assertEqual(t.microsecond, 992188)

            t = fts(1e-7)
            self.assertEqual(t, zero)
            t = fts(9e-7)
            self.assertEqual(t, one)
            t = fts(0.99999949)
            self.assertEqual(t.second, 0)
            self.assertEqual(t.microsecond, 999999)
            t = fts(0.9999999)
            self.assertEqual(t.second, 1)
            self.assertEqual(t.microsecond, 0)
            t = fts(1/2**7)
            self.assertEqual(t.second, 0)
            self.assertEqual(t.microsecond, 7812)
    """
    """
    def test_timestamp_limits(self):
        # minimum timestamp
        min_dt = self.theclass.min.replace(tzinfo=timezone.utc)
        min_ts = min_dt.timestamp()
        try:
            # date 0001-01-01 00:00:00+00:00: timestamp=-62135596800
            self.assertEqual(self.theclass.fromtimestamp(min_ts, tz=timezone.utc),
                             min_dt)
        except (OverflowError, OSError) as exc:
            # the date 0001-01-01 doesn't fit into 32-bit time_t,
            # or platform doesn't support such very old date
            self.skipTest(str(exc))

        # maximum timestamp: set seconds to zero to avoid rounding issues
        max_dt = self.theclass.max.replace(tzinfo=timezone.utc,
                                           second=0, microsecond=0)
        max_ts = max_dt.timestamp()
        # date 9999-12-31 23:59:00+00:00: timestamp 253402300740
        self.assertEqual(self.theclass.fromtimestamp(max_ts, tz=timezone.utc),
                         max_dt)

        # number of seconds greater than 1 year: make sure that the new date
        # is not valid in datetime.datetime limits
        delta = 3600 * 24 * 400

        # too small
        ts = min_ts - delta
        # converting a Python int to C time_t can raise a OverflowError,
        # especially on 32-bit platforms.
        self.assertRaises(ValueError, self.theclass.fromtimestamp, ts)
        self.assertRaises(ValueError, self.theclass.utcfromtimestamp, ts)

        # too big
        ts = max_dt.timestamp() + delta
        self.assertRaises(ValueError, self.theclass.fromtimestamp, ts)
        self.assertRaises(ValueError, self.theclass.utcfromtimestamp, ts)
    """

    def test_negative_float_fromtimestamp(self):
        # The result is tz-dependent; at least test that this doesn't
        # fail (like it did before bug 1646728 was fixed).
        self.theclass.fromtimestamp(-1.05)

    """
    def test_negative_float_utcfromtimestamp(self):
        d = self.theclass.utcfromtimestamp(-1.05)
        self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
    """

    def test_utcnow(self):
        import time

        # Call it a success if utcnow() and utcfromtimestamp() are within
        # a second of each other.
        from_timestamp = None
        from_now = None
        tolerance = timedelta(seconds=1)
        for dummy in range(3):
            from_now = self.theclass.utcnow()
            from_timestamp = self.theclass.utcfromtimestamp(time.time())
            if abs(from_timestamp.__val__() - from_now.__val__()) <= tolerance:
                break
            # Else try again a few times.
        self.assertLessEqual(abs(from_timestamp.__val__() - from_now.__val__()), tolerance)

    def test_extract(self):
        dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
        self.assertEqual(dt.date(), date(2002, 3, 4))
        self.assertEqual(dt.time(), time(18, 45, 3, 1234))

    def test_combine(self):
        d = date(2002, 3, 4)
        t = time(18, 45, 3, 1234)
        expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
        combine = self.theclass.combine
        dt = combine(d, t)
        self.assertEqual(dt, expected)

        dt = combine(time=t, date=d)
        self.assertEqual(dt, expected)

        self.assertEqual(d, dt.date())
        self.assertEqual(t, dt.time())
        self.assertEqual(dt, combine(dt.date(), dt.time()))

        """
        self.assertRaises(TypeError, combine) # need an arg
        self.assertRaises(TypeError, combine, d) # need two args
        self.assertRaises(TypeError, combine, t, d) # args reversed
        self.assertRaises(TypeError, combine, d, t, 1) # wrong tzinfo type
        self.assertRaises(TypeError, combine, d, t, 1, 2)  # too many args
        self.assertRaises(TypeError, combine, "date", "time") # wrong types
        self.assertRaises(TypeError, combine, d, "time") # wrong type
        self.assertRaises(TypeError, combine, "date", t) # wrong type

        # tzinfo= argument
        dt = combine(d, t, timezone.utc)
        self.assertIs(dt.tzinfo, timezone.utc)
        dt = combine(d, t, tzinfo=timezone.utc)
        self.assertIs(dt.tzinfo, timezone.utc)
        t = time()
        dt = combine(dt, t)
        self.assertEqual(dt.date(), d)
        self.assertEqual(dt.time(), t)
        """

    def test_replace(self):
        cls = self.theclass
        args = (1, 2, 3, 4, 5, 6, 7)
        base = cls(*args)
        self.assertEqual(base, base.replace())

        self.assertEqual(base.replace(year=2), cls(2, 2, 3, 4, 5, 6, 7))
        self.assertEqual(base.replace(month=3), cls(1, 3, 3, 4, 5, 6, 7))
        self.assertEqual(base.replace(day=4), cls(1, 2, 4, 4, 5, 6, 7))
        self.assertEqual(base.replace(hour=5), cls(1, 2, 3, 5, 5, 6, 7))
        self.assertEqual(base.replace(minute=6), cls(1, 2, 3, 4, 6, 6, 7))
        self.assertEqual(base.replace(second=7), cls(1, 2, 3, 4, 5, 7, 7))
        self.assertEqual(base.replace(microsecond=8), cls(1, 2, 3, 4, 5, 6, 8))
        # Out of bounds.
        base = cls(2000, 2, 29)
        self.assertRaises(ValueError, lambda: base.replace(year=2001))

    """
    def test_astimezone(self):
        dt = self.theclass.now()
        f = FixedOffset(44, "0044")
        dt_utc = dt.replace(tzinfo=timezone(timedelta(hours=-4), 'EDT'))
        self.assertEqual(dt.astimezone(), dt_utc) # naive
        self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
        self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
        dt_f = dt.replace(tzinfo=f) + timedelta(hours=4, minutes=44)
        self.assertEqual(dt.astimezone(f), dt_f) # naive
        self.assertEqual(dt.astimezone(tz=f), dt_f) # naive

        class Bogus(tzinfo):
            def utcoffset(self, dt): return None
            def dst(self, dt): return timedelta(0)
        bog = Bogus()
        self.assertRaises(ValueError, dt.astimezone, bog)   # naive
        self.assertEqual(dt.replace(tzinfo=bog).astimezone(f), dt_f)

        class AlsoBogus(tzinfo):
            def utcoffset(self, dt): return timedelta(0)
            def dst(self, dt): return None
        alsobog = AlsoBogus()
        self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive

        class Broken(tzinfo):
            def utcoffset(self, dt): return 1
            def dst(self, dt): return 1
        broken = Broken()
        dt_broken = dt.replace(tzinfo=broken)
        with self.assertRaises(TypeError):
            dt_broken.astimezone()
    """

    def test_fromisoformat_datetime(self):
        # Test that isoformat() is reversible
        base_dates = [(1, 1, 1), (1900, 1, 1), (2004, 11, 12), (2017, 5, 30)]

        base_times = [
            (0, 0, 0, 0),
            (0, 0, 0, 241000),
            (0, 0, 0, 234567),
            (12, 30, 45, 234567),
        ]

        separators = [" ", "T"]

        dts = [
            self.theclass(*date_tuple, *time_tuple)
            for date_tuple in base_dates
            for time_tuple in base_times
        ]

        for dt in dts:
            for sep in separators:
                dtstr = dt.isoformat(sep=sep)
                dt_rt = self.theclass.fromisoformat(dtstr)
                self.assertEqual(dt, dt_rt)

    def test_fromisoformat_separators(self):
        separators = [" ", "T"]

        for sep in separators:
            dt = self.theclass(2018, 1, 31, 23, 59, 47, 124789)
            dtstr = dt.isoformat(sep=sep)

            dt_rt = self.theclass.fromisoformat(dtstr)
            self.assertEqual(dt, dt_rt)

    def test_fromisoformat_ambiguous(self):
        # Test strings like 2018-01-31+12:15 (where +12:15 is not a time zone)
        separators = ["+", "-"]
        for sep in separators:
            dt = self.theclass(2018, 1, 31, 12, 15)
            dtstr = dt.isoformat(sep=sep)

            dt_rt = self.theclass.fromisoformat(dtstr)
            self.assertEqual(dt, dt_rt)

    def test_fromisoformat_timespecs(self):
        datetime_bases = [(2009, 12, 4, 8, 17, 45, 123456), (2009, 12, 4, 8, 17, 45, 0)]

        timespecs = ["hours", "minutes", "seconds", "milliseconds", "microseconds"]

        for dt_tuple in datetime_bases:
            dt = self.theclass(*(dt_tuple[0:4]))
            dtstr = dt.isoformat(timespec="hours")
            dt_rt = self.theclass.fromisoformat(dtstr)
            self.assertEqual(dt, dt_rt)

            dt = self.theclass(*(dt_tuple[0:5]))
            dtstr = dt.isoformat(timespec="minutes")
            dt_rt = self.theclass.fromisoformat(dtstr)
            self.assertEqual(dt, dt_rt)

            dt = self.theclass(*(dt_tuple[0:6]))
            dtstr = dt.isoformat(timespec="seconds")
            dt_rt = self.theclass.fromisoformat(dtstr)
            self.assertEqual(dt, dt_rt)

            new_microseconds = 1000 * (dt_tuple[6] // 1000)
            dt_tuple2 = (*dt_tuple[0:6], new_microseconds)
            dt = self.theclass(*(dt_tuple2[0:7]))
            dtstr = dt.isoformat(timespec="milliseconds")
            dt_rt = self.theclass.fromisoformat(dtstr)
            self.assertEqual(dt, dt_rt)

            dt = self.theclass(*(dt_tuple[0:8]))
            dtstr = dt.isoformat(timespec="microseconds")
            dt_rt = self.theclass.fromisoformat(dtstr)
            self.assertEqual(dt, dt_rt)

    def test_fromisoformat_fails_datetime(self):
        # Test that fromisoformat() fails on invalid values
        bad_strs = [
            "",  # Empty string
            "\ud800",  # bpo-34454: Surrogate code point
            "2009.04-19T03",  # Wrong first separator
            "2009-04.19T03",  # Wrong second separator
            "2009-04-19T0a",  # Invalid hours
            "2009-04-19T03:1a:45",  # Invalid minutes
            "2009-04-19T03:15:4a",  # Invalid seconds
            "2009-04-19T03;15:45",  # Bad first time separator
            "2009-04-19T03:15;45",  # Bad second time separator
            "2009-04-19T03:15:4500:00",  # Bad time zone separator
            "2009-04-19T03:15:45.2345",  # Too many digits for milliseconds
            "2009-04-19T03:15:45.1234567",  # Too many digits for microseconds
            "2009-04-19T03:15:45.123456+24:30",  # Invalid time zone offset
            "2009-04-19T03:15:45.123456-24:30",  # Invalid negative offset
            "2009-04-10ᛇᛇᛇᛇᛇ12:15",  # Too many unicode separators
            "2009-04\ud80010T12:15",  # Surrogate char in date
            "2009-04-10T12\ud80015",  # Surrogate char in time
            "2009-04-19T1",  # Incomplete hours
            "2009-04-19T12:3",  # Incomplete minutes
            "2009-04-19T12:30:4",  # Incomplete seconds
            "2009-04-19T12:",  # Ends with time separator
            "2009-04-19T12:30:",  # Ends with time separator
            "2009-04-19T12:30:45.",  # Ends with time separator
            "2009-04-19T12:30:45.123456+",  # Ends with timzone separator
            "2009-04-19T12:30:45.123456-",  # Ends with timzone separator
            "2009-04-19T12:30:45.123456-05:00a",  # Extra text
            "2009-04-19T12:30:45.123-05:00a",  # Extra text
            "2009-04-19T12:30:45-05:00a",  # Extra text
        ]

        for bad_str in bad_strs:
            self.assertRaises(ValueError, self.theclass.fromisoformat, bad_str)


class TestTime(Static[TestCase]):
    theclass: type = time

    def test_basic_attributes(self):
        t = self.theclass(12, 0)
        self.assertEqual(t.hour, 12)
        self.assertEqual(t.minute, 0)
        self.assertEqual(t.second, 0)
        self.assertEqual(t.microsecond, 0)

    def test_basic_attributes_nonzero(self):
        # Make sure all attributes are non-zero so bugs in
        # bit-shifting access show up.
        t = self.theclass(12, 59, 59, 8000)
        self.assertEqual(t.hour, 12)
        self.assertEqual(t.minute, 59)
        self.assertEqual(t.second, 59)
        self.assertEqual(t.microsecond, 8000)

    def test_comparing(self):
        args = (1, 2, 3, 4)
        newargsx = [(2, 2, 3, 4), (1, 3, 3, 4), (1, 2, 4, 4), (1, 2, 3, 5)]
        t1 = self.theclass(*args)
        t2 = self.theclass(*args)
        self.assertEqual(t1, t2)
        self.assertTrue(t1 <= t2)
        self.assertTrue(t1 >= t2)
        self.assertFalse(t1 != t2)
        self.assertFalse(t1 < t2)
        self.assertFalse(t1 > t2)

        for i in range(len(args)):
            newargs = newargsx[i]
            t2 = self.theclass(*newargs)  # this is larger than t1
            self.assertTrue(t1 < t2)
            self.assertTrue(t2 > t1)
            self.assertTrue(t1 <= t2)
            self.assertTrue(t2 >= t1)
            self.assertTrue(t1 != t2)
            self.assertTrue(t2 != t1)
            self.assertFalse(t1 == t2)
            self.assertFalse(t2 == t1)
            self.assertFalse(t1 > t2)
            self.assertFalse(t2 < t1)
            self.assertFalse(t1 >= t2)
            self.assertFalse(t2 <= t1)

        """
        for badarg in OTHERSTUFF:
            self.assertEqual(t1 == badarg, False)
            self.assertEqual(t1 != badarg, True)
            self.assertEqual(badarg == t1, False)
            self.assertEqual(badarg != t1, True)

            self.assertRaises(TypeError, lambda: t1 <= badarg)
            self.assertRaises(TypeError, lambda: t1 < badarg)
            self.assertRaises(TypeError, lambda: t1 > badarg)
            self.assertRaises(TypeError, lambda: t1 >= badarg)
            self.assertRaises(TypeError, lambda: badarg <= t1)
            self.assertRaises(TypeError, lambda: badarg < t1)
            self.assertRaises(TypeError, lambda: badarg > t1)
            self.assertRaises(TypeError, lambda: badarg >= t1)
        """

    def test_bad_constructor_arguments(self):
        # bad hours
        self.theclass(0, 0)  # no exception
        self.theclass(23, 0)  # no exception

        make_time1 = lambda a, b: self.theclass(a, b)
        make_time2 = lambda a, b, c: self.theclass(a, b, c)
        make_time3 = lambda a, b, c, d: self.theclass(a, b, c, d)

        self.assertRaises(ValueError, make_time1, -1, 0)
        self.assertRaises(ValueError, make_time1, 24, 0)
        # bad minutes
        self.theclass(23, 0)  # no exception
        self.theclass(23, 59)  # no exception
        self.assertRaises(ValueError, make_time1, 23, -1)
        self.assertRaises(ValueError, make_time1, 23, 60)
        # bad seconds
        self.theclass(23, 59, 0)  # no exception
        self.theclass(23, 59, 59)  # no exception
        self.assertRaises(ValueError, make_time2, 23, 59, -1)
        self.assertRaises(ValueError, make_time2, 23, 59, 60)
        # bad microseconds
        self.theclass(23, 59, 59, 0)  # no exception
        self.theclass(23, 59, 59, 999999)  # no exception
        self.assertRaises(ValueError, make_time3, 23, 59, 59, -1)
        self.assertRaises(ValueError, make_time3, 23, 59, 59, 1000000)

    def test_hash_equality(self):
        d = self.theclass(23, 30, 17)
        e = self.theclass(23, 30, 17)
        self.assertEqual(d, e)
        self.assertEqual(hash(d), hash(e))

        dic = {d: 1}
        dic[e] = 2
        self.assertEqual(len(dic), 1)
        self.assertEqual(dic[d], 2)
        self.assertEqual(dic[e], 2)

        d = self.theclass(0, 5, 17)
        e = self.theclass(0, 5, 17)
        self.assertEqual(d, e)
        self.assertEqual(hash(d), hash(e))

        dic = {d: 1}
        dic[e] = 2
        self.assertEqual(len(dic), 1)
        self.assertEqual(dic[d], 2)
        self.assertEqual(dic[e], 2)

    def test_isoformat(self):
        t = self.theclass(4, 5, 1, 123)
        self.assertEqual(t.isoformat(), "04:05:01.000123")
        self.assertEqual(t.isoformat(), str(t))

        t = self.theclass()
        self.assertEqual(t.isoformat(), "00:00:00")
        self.assertEqual(t.isoformat(), str(t))

        t = self.theclass(microsecond=1)
        self.assertEqual(t.isoformat(), "00:00:00.000001")
        self.assertEqual(t.isoformat(), str(t))

        t = self.theclass(microsecond=10)
        self.assertEqual(t.isoformat(), "00:00:00.000010")
        self.assertEqual(t.isoformat(), str(t))

        t = self.theclass(microsecond=100)
        self.assertEqual(t.isoformat(), "00:00:00.000100")
        self.assertEqual(t.isoformat(), str(t))

        t = self.theclass(microsecond=1000)
        self.assertEqual(t.isoformat(), "00:00:00.001000")
        self.assertEqual(t.isoformat(), str(t))

        t = self.theclass(microsecond=10000)
        self.assertEqual(t.isoformat(), "00:00:00.010000")
        self.assertEqual(t.isoformat(), str(t))

        t = self.theclass(microsecond=100000)
        self.assertEqual(t.isoformat(), "00:00:00.100000")
        self.assertEqual(t.isoformat(), str(t))

        t = self.theclass(hour=12, minute=34, second=56, microsecond=123456)
        self.assertEqual(t.isoformat(timespec="hours"), "12")
        self.assertEqual(t.isoformat(timespec="minutes"), "12:34")
        self.assertEqual(t.isoformat(timespec="seconds"), "12:34:56")
        self.assertEqual(t.isoformat(timespec="milliseconds"), "12:34:56.123")
        self.assertEqual(t.isoformat(timespec="microseconds"), "12:34:56.123456")
        self.assertEqual(t.isoformat(timespec="auto"), "12:34:56.123456")

        t = self.theclass(hour=12, minute=34, second=56, microsecond=999500)
        self.assertEqual(t.isoformat(timespec="milliseconds"), "12:34:56.999")

        t = self.theclass(hour=12, minute=34, second=56, microsecond=0)
        self.assertEqual(t.isoformat(timespec="milliseconds"), "12:34:56.000")
        self.assertEqual(t.isoformat(timespec="microseconds"), "12:34:56.000000")
        self.assertEqual(t.isoformat(timespec="auto"), "12:34:56")

    def test_str(self):
        self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
        self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
        self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
        self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
        self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")

    def test_repr(self):
        self.assertEqual(
            repr(self.theclass(1, 2, 3, 4)),
            "time(hour=1, minute=2, second=3, microsecond=4)",
        )
        self.assertEqual(
            repr(self.theclass(10, 2, 3, 4000)),
            "time(hour=10, minute=2, second=3, microsecond=4000)",
        )
        self.assertEqual(
            repr(self.theclass(0, 2, 3, 400000)),
            "time(hour=0, minute=2, second=3, microsecond=400000)",
        )
        self.assertEqual(
            repr(self.theclass(12, 2, 3, 0)), "time(hour=12, minute=2, second=3)"
        )
        self.assertEqual(repr(self.theclass(23, 15, 0, 0)), "time(hour=23, minute=15)")

    def test_resolution_info(self):
        self.assertTrue(self.theclass.max > self.theclass.min)

    def test_bool(self):
        # time is always True.
        cls = self.theclass
        self.assertTrue(cls(1))
        self.assertTrue(cls(0, 1))
        self.assertTrue(cls(0, 0, 1))
        self.assertTrue(cls(0, 0, 0, 1))
        self.assertTrue(cls(0))
        self.assertTrue(cls())

    def test_replace(self):
        cls = self.theclass
        args = (1, 2, 3, 4)
        base = cls(*args)
        self.assertEqual(base, base.replace())

        self.assertEqual(base.replace(hour=5), cls(5, 2, 3, 4))
        self.assertEqual(base.replace(minute=6), cls(1, 6, 3, 4))
        self.assertEqual(base.replace(second=7), cls(1, 2, 7, 4))
        self.assertEqual(base.replace(microsecond=8), cls(1, 2, 3, 8))

        # Out of bounds.
        base = cls(1)
        self.assertRaises(ValueError, lambda: base.replace(hour=24))
        self.assertRaises(
            ValueError, lambda: base.replace(minute=-2)
        )  # minute=-1 indicates default; changed to -2 for test
        self.assertRaises(ValueError, lambda: base.replace(second=100))
        self.assertRaises(ValueError, lambda: base.replace(microsecond=1000000))


case_td = TestTimeDelta()
case_td.test_computations()
case_td.test_constructor()
case_td.test_resolution_info()
case_td.test_basic_attributes()
case_td.test_total_seconds()
case_td.test_carries()
case_td.test_hash_equality()
case_td.test_compare()
case_td.test_str()
case_td.test_repr()
case_td.test_microsecond_rounding()
case_td.test_massive_normalization()
case_td.test_bool()
case_td.test_division()
case_td.test_remainder()
case_td.test_divmod()

case_do = TestDateOnly()
case_do.test_delta_non_days_ignored()

case_dx = TestDate[date]()
case_dx.test_basic_attributes()
case_dx.test_ordinal_conversions()
# case_dx.test_extreme_ordinals()
case_dx.test_bad_constructor_arguments()
case_dx.test_hash_equality()
case_dx.test_computations()
case_dx.test_fromtimestamp()
case_dx.test_today()
case_dx.test_weekday()
case_dx.test_isocalendar()
case_dx.test_iso_long_years()
case_dx.test_isoformat()
case_dx.test_ctime()
case_dx.test_timetuple()
case_dx.test_compare()
case_dx.test_replace()
case_dx.test_fromisoformat()
case_dx.test_fromisoformat_fails()
case_dx.test_fromisocalendar()
case_dx.test_fromisocalendar_value_errors()

case_dt = TestDateTime[datetime]()
case_dt.test_ordinal_conversions()
case_dt.test_today()
case_dt.test_weekday()
case_dt.test_isocalendar()
case_dt.test_iso_long_years()
case_dt.test_ctime()
case_dt.test_timetuple()
case_dt.test_compare()
case_dt.test_fromisoformat()
case_dt.test_fromisoformat_fails()
case_dt.test_fromisocalendar_value_errors()
# ---
case_dt.test_basic_attributes()
case_dt.test_basic_attributes_nonzero()
case_dt.test_isoformat()
# case_dt.test_more_ctime()
case_dt.test_tz_independent_comparing()
case_dt.test_bad_constructor_arguments()
case_dt.test_hash_equality()
case_dt.test_computations()
case_dt.test_more_compare()
case_dt.test_fromtimestamp()
# case_dt.test_utcfromtimestamp()
case_dt.test_timestamp_naive()
# case_dt.test_microsecond_rounding()
# case_dt.test_timestamp_limits()
case_dt.test_negative_float_fromtimestamp()
# case_dt.test_negative_float_utcfromtimestamp()
case_dt.test_utcnow()
case_dt.test_extract()
case_dt.test_combine()
case_dt.test_replace()
case_dt.test_fromisoformat_datetime()
case_dt.test_fromisoformat_separators()
case_dt.test_fromisoformat_ambiguous()
case_dt.test_fromisoformat_timespecs()
case_dt.test_fromisoformat_fails_datetime()

case_tx = TestTime()
case_tx.test_basic_attributes()
case_tx.test_basic_attributes_nonzero()
case_tx.test_comparing()
case_tx.test_bad_constructor_arguments()
case_tx.test_hash_equality()
case_tx.test_isoformat()
case_tx.test_str()
case_tx.test_repr()
case_tx.test_resolution_info()
case_tx.test_bool()
case_tx.test_replace()

@test
def test_constants():
    assert str(timedelta.min) == '-106751992 days, 19:59:05.224192'  # note: diff w/ Python
    assert str(timedelta.max) == '106751991 days, 4:00:54.775807'    # note: diff w/ Python
    assert str(timedelta.resolution) == '0:00:00.000001'

    assert str(date.min) == '0001-01-01'
    assert str(date.max) == '9999-12-31'
    assert str(date.resolution) == '1 day, 0:00:00'

    assert str(time.min) == '00:00:00'
    assert str(time.max) == '23:59:59.999999'
    assert str(time.resolution) == '0:00:00.000001'

    assert str(datetime.min) == '0001-01-01 00:00:00'
    assert str(datetime.max) == '9999-12-31 23:59:59.999999'
    assert str(datetime.resolution) == '0:00:00.000001'
test_constants()
