mirror of https://github.com/exaloop/codon.git
1995 lines
74 KiB
Python
1995 lines
74 KiB
Python
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()
|