Home | History | Annotate | Download | only in test
      1 """Test date/time type.
      2 
      3 See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
      4 """
      5 from __future__ import division
      6 import sys
      7 import pickle
      8 import cPickle
      9 import unittest
     10 
     11 from test import test_support
     12 
     13 from datetime import MINYEAR, MAXYEAR
     14 from datetime import timedelta
     15 from datetime import tzinfo
     16 from datetime import time
     17 from datetime import date, datetime
     18 
     19 pickle_choices = [(pickler, unpickler, proto)
     20                   for pickler in pickle, cPickle
     21                   for unpickler in pickle, cPickle
     22                   for proto in range(3)]
     23 assert len(pickle_choices) == 2*2*3
     24 
     25 # An arbitrary collection of objects of non-datetime types, for testing
     26 # mixed-type comparisons.
     27 OTHERSTUFF = (10, 10L, 34.5, "abc", {}, [], ())
     28 
     29 
     30 #############################################################################
     31 # module tests
     32 
     33 class TestModule(unittest.TestCase):
     34 
     35     def test_constants(self):
     36         import datetime
     37         self.assertEqual(datetime.MINYEAR, 1)
     38         self.assertEqual(datetime.MAXYEAR, 9999)
     39 
     40 #############################################################################
     41 # tzinfo tests
     42 
     43 class FixedOffset(tzinfo):
     44     def __init__(self, offset, name, dstoffset=42):
     45         if isinstance(offset, int):
     46             offset = timedelta(minutes=offset)
     47         if isinstance(dstoffset, int):
     48             dstoffset = timedelta(minutes=dstoffset)
     49         self.__offset = offset
     50         self.__name = name
     51         self.__dstoffset = dstoffset
     52     def __repr__(self):
     53         return self.__name.lower()
     54     def utcoffset(self, dt):
     55         return self.__offset
     56     def tzname(self, dt):
     57         return self.__name
     58     def dst(self, dt):
     59         return self.__dstoffset
     60 
     61 class PicklableFixedOffset(FixedOffset):
     62     def __init__(self, offset=None, name=None, dstoffset=None):
     63         FixedOffset.__init__(self, offset, name, dstoffset)
     64 
     65 class TestTZInfo(unittest.TestCase):
     66 
     67     def test_non_abstractness(self):
     68         # In order to allow subclasses to get pickled, the C implementation
     69         # wasn't able to get away with having __init__ raise
     70         # NotImplementedError.
     71         useless = tzinfo()
     72         dt = datetime.max
     73         self.assertRaises(NotImplementedError, useless.tzname, dt)
     74         self.assertRaises(NotImplementedError, useless.utcoffset, dt)
     75         self.assertRaises(NotImplementedError, useless.dst, dt)
     76 
     77     def test_subclass_must_override(self):
     78         class NotEnough(tzinfo):
     79             def __init__(self, offset, name):
     80                 self.__offset = offset
     81                 self.__name = name
     82         self.assertTrue(issubclass(NotEnough, tzinfo))
     83         ne = NotEnough(3, "NotByALongShot")
     84         self.assertIsInstance(ne, tzinfo)
     85 
     86         dt = datetime.now()
     87         self.assertRaises(NotImplementedError, ne.tzname, dt)
     88         self.assertRaises(NotImplementedError, ne.utcoffset, dt)
     89         self.assertRaises(NotImplementedError, ne.dst, dt)
     90 
     91     def test_normal(self):
     92         fo = FixedOffset(3, "Three")
     93         self.assertIsInstance(fo, tzinfo)
     94         for dt in datetime.now(), None:
     95             self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
     96             self.assertEqual(fo.tzname(dt), "Three")
     97             self.assertEqual(fo.dst(dt), timedelta(minutes=42))
     98 
     99     def test_pickling_base(self):
    100         # There's no point to pickling tzinfo objects on their own (they
    101         # carry no data), but they need to be picklable anyway else
    102         # concrete subclasses can't be pickled.
    103         orig = tzinfo.__new__(tzinfo)
    104         self.assertTrue(type(orig) is tzinfo)
    105         for pickler, unpickler, proto in pickle_choices:
    106             green = pickler.dumps(orig, proto)
    107             derived = unpickler.loads(green)
    108             self.assertTrue(type(derived) is tzinfo)
    109 
    110     def test_pickling_subclass(self):
    111         # Make sure we can pickle/unpickle an instance of a subclass.
    112         offset = timedelta(minutes=-300)
    113         orig = PicklableFixedOffset(offset, 'cookie')
    114         self.assertIsInstance(orig, tzinfo)
    115         self.assertTrue(type(orig) is PicklableFixedOffset)
    116         self.assertEqual(orig.utcoffset(None), offset)
    117         self.assertEqual(orig.tzname(None), 'cookie')
    118         for pickler, unpickler, proto in pickle_choices:
    119             green = pickler.dumps(orig, proto)
    120             derived = unpickler.loads(green)
    121             self.assertIsInstance(derived, tzinfo)
    122             self.assertTrue(type(derived) is PicklableFixedOffset)
    123             self.assertEqual(derived.utcoffset(None), offset)
    124             self.assertEqual(derived.tzname(None), 'cookie')
    125 
    126 #############################################################################
    127 # Base clase for testing a particular aspect of timedelta, time, date and
    128 # datetime comparisons.
    129 
    130 class HarmlessMixedComparison:
    131     # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
    132 
    133     # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
    134     # legit constructor.
    135 
    136     def test_harmless_mixed_comparison(self):
    137         me = self.theclass(1, 1, 1)
    138 
    139         self.assertFalse(me == ())
    140         self.assertTrue(me != ())
    141         self.assertFalse(() == me)
    142         self.assertTrue(() != me)
    143 
    144         self.assertIn(me, [1, 20L, [], me])
    145         self.assertIn([], [me, 1, 20L, []])
    146 
    147     def test_harmful_mixed_comparison(self):
    148         me = self.theclass(1, 1, 1)
    149 
    150         self.assertRaises(TypeError, lambda: me < ())
    151         self.assertRaises(TypeError, lambda: me <= ())
    152         self.assertRaises(TypeError, lambda: me > ())
    153         self.assertRaises(TypeError, lambda: me >= ())
    154 
    155         self.assertRaises(TypeError, lambda: () < me)
    156         self.assertRaises(TypeError, lambda: () <= me)
    157         self.assertRaises(TypeError, lambda: () > me)
    158         self.assertRaises(TypeError, lambda: () >= me)
    159 
    160         self.assertRaises(TypeError, cmp, (), me)
    161         self.assertRaises(TypeError, cmp, me, ())
    162 
    163 #############################################################################
    164 # timedelta tests
    165 
    166 class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
    167 
    168     theclass = timedelta
    169 
    170     def test_constructor(self):
    171         eq = self.assertEqual
    172         td = timedelta
    173 
    174         # Check keyword args to constructor
    175         eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
    176                     milliseconds=0, microseconds=0))
    177         eq(td(1), td(days=1))
    178         eq(td(0, 1), td(seconds=1))
    179         eq(td(0, 0, 1), td(microseconds=1))
    180         eq(td(weeks=1), td(days=7))
    181         eq(td(days=1), td(hours=24))
    182         eq(td(hours=1), td(minutes=60))
    183         eq(td(minutes=1), td(seconds=60))
    184         eq(td(seconds=1), td(milliseconds=1000))
    185         eq(td(milliseconds=1), td(microseconds=1000))
    186 
    187         # Check float args to constructor
    188         eq(td(weeks=1.0/7), td(days=1))
    189         eq(td(days=1.0/24), td(hours=1))
    190         eq(td(hours=1.0/60), td(minutes=1))
    191         eq(td(minutes=1.0/60), td(seconds=1))
    192         eq(td(seconds=0.001), td(milliseconds=1))
    193         eq(td(milliseconds=0.001), td(microseconds=1))
    194 
    195     def test_computations(self):
    196         eq = self.assertEqual
    197         td = timedelta
    198 
    199         a = td(7) # One week
    200         b = td(0, 60) # One minute
    201         c = td(0, 0, 1000) # One millisecond
    202         eq(a+b+c, td(7, 60, 1000))
    203         eq(a-b, td(6, 24*3600 - 60))
    204         eq(-a, td(-7))
    205         eq(+a, td(7))
    206         eq(-b, td(-1, 24*3600 - 60))
    207         eq(-c, td(-1, 24*3600 - 1, 999000))
    208         eq(abs(a), a)
    209         eq(abs(-a), a)
    210         eq(td(6, 24*3600), a)
    211         eq(td(0, 0, 60*1000000), b)
    212         eq(a*10, td(70))
    213         eq(a*10, 10*a)
    214         eq(a*10L, 10*a)
    215         eq(b*10, td(0, 600))
    216         eq(10*b, td(0, 600))
    217         eq(b*10L, td(0, 600))
    218         eq(c*10, td(0, 0, 10000))
    219         eq(10*c, td(0, 0, 10000))
    220         eq(c*10L, td(0, 0, 10000))
    221         eq(a*-1, -a)
    222         eq(b*-2, -b-b)
    223         eq(c*-2, -c+-c)
    224         eq(b*(60*24), (b*60)*24)
    225         eq(b*(60*24), (60*b)*24)
    226         eq(c*1000, td(0, 1))
    227         eq(1000*c, td(0, 1))
    228         eq(a//7, td(1))
    229         eq(b//10, td(0, 6))
    230         eq(c//1000, td(0, 0, 1))
    231         eq(a//10, td(0, 7*24*360))
    232         eq(a//3600000, td(0, 0, 7*24*1000))
    233 
    234         # Issue #11576
    235         eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998),
    236            td(0, 0, 1))
    237         eq(td(999999999, 1, 1) - td(999999999, 1, 0),
    238            td(0, 0, 1))
    239 
    240 
    241     def test_disallowed_computations(self):
    242         a = timedelta(42)
    243 
    244         # Add/sub ints, longs, floats should be illegal
    245         for i in 1, 1L, 1.0:
    246             self.assertRaises(TypeError, lambda: a+i)
    247             self.assertRaises(TypeError, lambda: a-i)
    248             self.assertRaises(TypeError, lambda: i+a)
    249             self.assertRaises(TypeError, lambda: i-a)
    250 
    251         # Mul/div by float isn't supported.
    252         x = 2.3
    253         self.assertRaises(TypeError, lambda: a*x)
    254         self.assertRaises(TypeError, lambda: x*a)
    255         self.assertRaises(TypeError, lambda: a/x)
    256         self.assertRaises(TypeError, lambda: x/a)
    257         self.assertRaises(TypeError, lambda: a // x)
    258         self.assertRaises(TypeError, lambda: x // a)
    259 
    260         # Division of int by timedelta doesn't make sense.
    261         # Division by zero doesn't make sense.
    262         for zero in 0, 0L:
    263             self.assertRaises(TypeError, lambda: zero // a)
    264             self.assertRaises(ZeroDivisionError, lambda: a // zero)
    265 
    266     def test_basic_attributes(self):
    267         days, seconds, us = 1, 7, 31
    268         td = timedelta(days, seconds, us)
    269         self.assertEqual(td.days, days)
    270         self.assertEqual(td.seconds, seconds)
    271         self.assertEqual(td.microseconds, us)
    272 
    273     def test_total_seconds(self):
    274         td = timedelta(days=365)
    275         self.assertEqual(td.total_seconds(), 31536000.0)
    276         for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
    277             td = timedelta(seconds=total_seconds)
    278             self.assertEqual(td.total_seconds(), total_seconds)
    279         # Issue8644: Test that td.total_seconds() has the same
    280         # accuracy as td / timedelta(seconds=1).
    281         for ms in [-1, -2, -123]:
    282             td = timedelta(microseconds=ms)
    283             self.assertEqual(td.total_seconds(),
    284                              ((24*3600*td.days + td.seconds)*10**6
    285                               + td.microseconds)/10**6)
    286 
    287     def test_carries(self):
    288         t1 = timedelta(days=100,
    289                        weeks=-7,
    290                        hours=-24*(100-49),
    291                        minutes=-3,
    292                        seconds=12,
    293                        microseconds=(3*60 - 12) * 1e6 + 1)
    294         t2 = timedelta(microseconds=1)
    295         self.assertEqual(t1, t2)
    296 
    297     def test_hash_equality(self):
    298         t1 = timedelta(days=100,
    299                        weeks=-7,
    300                        hours=-24*(100-49),
    301                        minutes=-3,
    302                        seconds=12,
    303                        microseconds=(3*60 - 12) * 1000000)
    304         t2 = timedelta()
    305         self.assertEqual(hash(t1), hash(t2))
    306 
    307         t1 += timedelta(weeks=7)
    308         t2 += timedelta(days=7*7)
    309         self.assertEqual(t1, t2)
    310         self.assertEqual(hash(t1), hash(t2))
    311 
    312         d = {t1: 1}
    313         d[t2] = 2
    314         self.assertEqual(len(d), 1)
    315         self.assertEqual(d[t1], 2)
    316 
    317     def test_pickling(self):
    318         args = 12, 34, 56
    319         orig = timedelta(*args)
    320         for pickler, unpickler, proto in pickle_choices:
    321             green = pickler.dumps(orig, proto)
    322             derived = unpickler.loads(green)
    323             self.assertEqual(orig, derived)
    324 
    325     def test_compare(self):
    326         t1 = timedelta(2, 3, 4)
    327         t2 = timedelta(2, 3, 4)
    328         self.assertTrue(t1 == t2)
    329         self.assertTrue(t1 <= t2)
    330         self.assertTrue(t1 >= t2)
    331         self.assertTrue(not t1 != t2)
    332         self.assertTrue(not t1 < t2)
    333         self.assertTrue(not t1 > t2)
    334         self.assertEqual(cmp(t1, t2), 0)
    335         self.assertEqual(cmp(t2, t1), 0)
    336 
    337         for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
    338             t2 = timedelta(*args)   # this is larger than t1
    339             self.assertTrue(t1 < t2)
    340             self.assertTrue(t2 > t1)
    341             self.assertTrue(t1 <= t2)
    342             self.assertTrue(t2 >= t1)
    343             self.assertTrue(t1 != t2)
    344             self.assertTrue(t2 != t1)
    345             self.assertTrue(not t1 == t2)
    346             self.assertTrue(not t2 == t1)
    347             self.assertTrue(not t1 > t2)
    348             self.assertTrue(not t2 < t1)
    349             self.assertTrue(not t1 >= t2)
    350             self.assertTrue(not t2 <= t1)
    351             self.assertEqual(cmp(t1, t2), -1)
    352             self.assertEqual(cmp(t2, t1), 1)
    353 
    354         for badarg in OTHERSTUFF:
    355             self.assertEqual(t1 == badarg, False)
    356             self.assertEqual(t1 != badarg, True)
    357             self.assertEqual(badarg == t1, False)
    358             self.assertEqual(badarg != t1, True)
    359 
    360             self.assertRaises(TypeError, lambda: t1 <= badarg)
    361             self.assertRaises(TypeError, lambda: t1 < badarg)
    362             self.assertRaises(TypeError, lambda: t1 > badarg)
    363             self.assertRaises(TypeError, lambda: t1 >= badarg)
    364             self.assertRaises(TypeError, lambda: badarg <= t1)
    365             self.assertRaises(TypeError, lambda: badarg < t1)
    366             self.assertRaises(TypeError, lambda: badarg > t1)
    367             self.assertRaises(TypeError, lambda: badarg >= t1)
    368 
    369     def test_str(self):
    370         td = timedelta
    371         eq = self.assertEqual
    372 
    373         eq(str(td(1)), "1 day, 0:00:00")
    374         eq(str(td(-1)), "-1 day, 0:00:00")
    375         eq(str(td(2)), "2 days, 0:00:00")
    376         eq(str(td(-2)), "-2 days, 0:00:00")
    377 
    378         eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
    379         eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
    380         eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
    381            "-210 days, 23:12:34")
    382 
    383         eq(str(td(milliseconds=1)), "0:00:00.001000")
    384         eq(str(td(microseconds=3)), "0:00:00.000003")
    385 
    386         eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
    387                    microseconds=999999)),
    388            "999999999 days, 23:59:59.999999")
    389 
    390     def test_roundtrip(self):
    391         for td in (timedelta(days=999999999, hours=23, minutes=59,
    392                              seconds=59, microseconds=999999),
    393                    timedelta(days=-999999999),
    394                    timedelta(days=1, seconds=2, microseconds=3)):
    395 
    396             # Verify td -> string -> td identity.
    397             s = repr(td)
    398             self.assertTrue(s.startswith('datetime.'))
    399             s = s[9:]
    400             td2 = eval(s)
    401             self.assertEqual(td, td2)
    402 
    403             # Verify identity via reconstructing from pieces.
    404             td2 = timedelta(td.days, td.seconds, td.microseconds)
    405             self.assertEqual(td, td2)
    406 
    407     def test_resolution_info(self):
    408         self.assertIsInstance(timedelta.min, timedelta)
    409         self.assertIsInstance(timedelta.max, timedelta)
    410         self.assertIsInstance(timedelta.resolution, timedelta)
    411         self.assertTrue(timedelta.max > timedelta.min)
    412         self.assertEqual(timedelta.min, timedelta(-999999999))
    413         self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
    414         self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
    415 
    416     def test_overflow(self):
    417         tiny = timedelta.resolution
    418 
    419         td = timedelta.min + tiny
    420         td -= tiny  # no problem
    421         self.assertRaises(OverflowError, td.__sub__, tiny)
    422         self.assertRaises(OverflowError, td.__add__, -tiny)
    423 
    424         td = timedelta.max - tiny
    425         td += tiny  # no problem
    426         self.assertRaises(OverflowError, td.__add__, tiny)
    427         self.assertRaises(OverflowError, td.__sub__, -tiny)
    428 
    429         self.assertRaises(OverflowError, lambda: -timedelta.max)
    430 
    431     def test_microsecond_rounding(self):
    432         td = timedelta
    433         eq = self.assertEqual
    434 
    435         # Single-field rounding.
    436         eq(td(milliseconds=0.4/1000), td(0))    # rounds to 0
    437         eq(td(milliseconds=-0.4/1000), td(0))    # rounds to 0
    438         eq(td(milliseconds=0.6/1000), td(microseconds=1))
    439         eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
    440 
    441         # Rounding due to contributions from more than one field.
    442         us_per_hour = 3600e6
    443         us_per_day = us_per_hour * 24
    444         eq(td(days=.4/us_per_day), td(0))
    445         eq(td(hours=.2/us_per_hour), td(0))
    446         eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
    447 
    448         eq(td(days=-.4/us_per_day), td(0))
    449         eq(td(hours=-.2/us_per_hour), td(0))
    450         eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
    451 
    452     def test_massive_normalization(self):
    453         td = timedelta(microseconds=-1)
    454         self.assertEqual((td.days, td.seconds, td.microseconds),
    455                          (-1, 24*3600-1, 999999))
    456 
    457     def test_bool(self):
    458         self.assertTrue(timedelta(1))
    459         self.assertTrue(timedelta(0, 1))
    460         self.assertTrue(timedelta(0, 0, 1))
    461         self.assertTrue(timedelta(microseconds=1))
    462         self.assertTrue(not timedelta(0))
    463 
    464     def test_subclass_timedelta(self):
    465 
    466         class T(timedelta):
    467             @staticmethod
    468             def from_td(td):
    469                 return T(td.days, td.seconds, td.microseconds)
    470 
    471             def as_hours(self):
    472                 sum = (self.days * 24 +
    473                        self.seconds / 3600.0 +
    474                        self.microseconds / 3600e6)
    475                 return round(sum)
    476 
    477         t1 = T(days=1)
    478         self.assertTrue(type(t1) is T)
    479         self.assertEqual(t1.as_hours(), 24)
    480 
    481         t2 = T(days=-1, seconds=-3600)
    482         self.assertTrue(type(t2) is T)
    483         self.assertEqual(t2.as_hours(), -25)
    484 
    485         t3 = t1 + t2
    486         self.assertTrue(type(t3) is timedelta)
    487         t4 = T.from_td(t3)
    488         self.assertTrue(type(t4) is T)
    489         self.assertEqual(t3.days, t4.days)
    490         self.assertEqual(t3.seconds, t4.seconds)
    491         self.assertEqual(t3.microseconds, t4.microseconds)
    492         self.assertEqual(str(t3), str(t4))
    493         self.assertEqual(t4.as_hours(), -1)
    494 
    495 #############################################################################
    496 # date tests
    497 
    498 class TestDateOnly(unittest.TestCase):
    499     # Tests here won't pass if also run on datetime objects, so don't
    500     # subclass this to test datetimes too.
    501 
    502     def test_delta_non_days_ignored(self):
    503         dt = date(2000, 1, 2)
    504         delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
    505                           microseconds=5)
    506         days = timedelta(delta.days)
    507         self.assertEqual(days, timedelta(1))
    508 
    509         dt2 = dt + delta
    510         self.assertEqual(dt2, dt + days)
    511 
    512         dt2 = delta + dt
    513         self.assertEqual(dt2, dt + days)
    514 
    515         dt2 = dt - delta
    516         self.assertEqual(dt2, dt - days)
    517 
    518         delta = -delta
    519         days = timedelta(delta.days)
    520         self.assertEqual(days, timedelta(-2))
    521 
    522         dt2 = dt + delta
    523         self.assertEqual(dt2, dt + days)
    524 
    525         dt2 = delta + dt
    526         self.assertEqual(dt2, dt + days)
    527 
    528         dt2 = dt - delta
    529         self.assertEqual(dt2, dt - days)
    530 
    531 class SubclassDate(date):
    532     sub_var = 1
    533 
    534 class TestDate(HarmlessMixedComparison, unittest.TestCase):
    535     # Tests here should pass for both dates and datetimes, except for a
    536     # few tests that TestDateTime overrides.
    537 
    538     theclass = date
    539 
    540     def test_basic_attributes(self):
    541         dt = self.theclass(2002, 3, 1)
    542         self.assertEqual(dt.year, 2002)
    543         self.assertEqual(dt.month, 3)
    544         self.assertEqual(dt.day, 1)
    545 
    546     def test_roundtrip(self):
    547         for dt in (self.theclass(1, 2, 3),
    548                    self.theclass.today()):
    549             # Verify dt -> string -> date identity.
    550             s = repr(dt)
    551             self.assertTrue(s.startswith('datetime.'))
    552             s = s[9:]
    553             dt2 = eval(s)
    554             self.assertEqual(dt, dt2)
    555 
    556             # Verify identity via reconstructing from pieces.
    557             dt2 = self.theclass(dt.year, dt.month, dt.day)
    558             self.assertEqual(dt, dt2)
    559 
    560     def test_ordinal_conversions(self):
    561         # Check some fixed values.
    562         for y, m, d, n in [(1, 1, 1, 1),      # calendar origin
    563                            (1, 12, 31, 365),
    564                            (2, 1, 1, 366),
    565                            # first example from "Calendrical Calculations"
    566                            (1945, 11, 12, 710347)]:
    567             d = self.theclass(y, m, d)
    568             self.assertEqual(n, d.toordinal())
    569             fromord = self.theclass.fromordinal(n)
    570             self.assertEqual(d, fromord)
    571             if hasattr(fromord, "hour"):
    572             # if we're checking something fancier than a date, verify
    573             # the extra fields have been zeroed out
    574                 self.assertEqual(fromord.hour, 0)
    575                 self.assertEqual(fromord.minute, 0)
    576                 self.assertEqual(fromord.second, 0)
    577                 self.assertEqual(fromord.microsecond, 0)
    578 
    579         # Check first and last days of year spottily across the whole
    580         # range of years supported.
    581         for year in xrange(MINYEAR, MAXYEAR+1, 7):
    582             # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
    583             d = self.theclass(year, 1, 1)
    584             n = d.toordinal()
    585             d2 = self.theclass.fromordinal(n)
    586             self.assertEqual(d, d2)
    587             # Verify that moving back a day gets to the end of year-1.
    588             if year > 1:
    589                 d = self.theclass.fromordinal(n-1)
    590                 d2 = self.theclass(year-1, 12, 31)
    591                 self.assertEqual(d, d2)
    592                 self.assertEqual(d2.toordinal(), n-1)
    593 
    594         # Test every day in a leap-year and a non-leap year.
    595         dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    596         for year, isleap in (2000, True), (2002, False):
    597             n = self.theclass(year, 1, 1).toordinal()
    598             for month, maxday in zip(range(1, 13), dim):
    599                 if month == 2 and isleap:
    600                     maxday += 1
    601                 for day in range(1, maxday+1):
    602                     d = self.theclass(year, month, day)
    603                     self.assertEqual(d.toordinal(), n)
    604                     self.assertEqual(d, self.theclass.fromordinal(n))
    605                     n += 1
    606 
    607     def test_extreme_ordinals(self):
    608         a = self.theclass.min
    609         a = self.theclass(a.year, a.month, a.day)  # get rid of time parts
    610         aord = a.toordinal()
    611         b = a.fromordinal(aord)
    612         self.assertEqual(a, b)
    613 
    614         self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
    615 
    616         b = a + timedelta(days=1)
    617         self.assertEqual(b.toordinal(), aord + 1)
    618         self.assertEqual(b, self.theclass.fromordinal(aord + 1))
    619 
    620         a = self.theclass.max
    621         a = self.theclass(a.year, a.month, a.day)  # get rid of time parts
    622         aord = a.toordinal()
    623         b = a.fromordinal(aord)
    624         self.assertEqual(a, b)
    625 
    626         self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
    627 
    628         b = a - timedelta(days=1)
    629         self.assertEqual(b.toordinal(), aord - 1)
    630         self.assertEqual(b, self.theclass.fromordinal(aord - 1))
    631 
    632     def test_bad_constructor_arguments(self):
    633         # bad years
    634         self.theclass(MINYEAR, 1, 1)  # no exception
    635         self.theclass(MAXYEAR, 1, 1)  # no exception
    636         self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
    637         self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
    638         # bad months
    639         self.theclass(2000, 1, 1)    # no exception
    640         self.theclass(2000, 12, 1)   # no exception
    641         self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
    642         self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
    643         # bad days
    644         self.theclass(2000, 2, 29)   # no exception
    645         self.theclass(2004, 2, 29)   # no exception
    646         self.theclass(2400, 2, 29)   # no exception
    647         self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
    648         self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
    649         self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
    650         self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
    651         self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
    652         self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
    653 
    654     def test_hash_equality(self):
    655         d = self.theclass(2000, 12, 31)
    656         # same thing
    657         e = self.theclass(2000, 12, 31)
    658         self.assertEqual(d, e)
    659         self.assertEqual(hash(d), hash(e))
    660 
    661         dic = {d: 1}
    662         dic[e] = 2
    663         self.assertEqual(len(dic), 1)
    664         self.assertEqual(dic[d], 2)
    665         self.assertEqual(dic[e], 2)
    666 
    667         d = self.theclass(2001,  1,  1)
    668         # same thing
    669         e = self.theclass(2001,  1,  1)
    670         self.assertEqual(d, e)
    671         self.assertEqual(hash(d), hash(e))
    672 
    673         dic = {d: 1}
    674         dic[e] = 2
    675         self.assertEqual(len(dic), 1)
    676         self.assertEqual(dic[d], 2)
    677         self.assertEqual(dic[e], 2)
    678 
    679     def test_computations(self):
    680         a = self.theclass(2002, 1, 31)
    681         b = self.theclass(1956, 1, 31)
    682 
    683         diff = a-b
    684         self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
    685         self.assertEqual(diff.seconds, 0)
    686         self.assertEqual(diff.microseconds, 0)
    687 
    688         day = timedelta(1)
    689         week = timedelta(7)
    690         a = self.theclass(2002, 3, 2)
    691         self.assertEqual(a + day, self.theclass(2002, 3, 3))
    692         self.assertEqual(day + a, self.theclass(2002, 3, 3))
    693         self.assertEqual(a - day, self.theclass(2002, 3, 1))
    694         self.assertEqual(-day + a, self.theclass(2002, 3, 1))
    695         self.assertEqual(a + week, self.theclass(2002, 3, 9))
    696         self.assertEqual(a - week, self.theclass(2002, 2, 23))
    697         self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
    698         self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
    699         self.assertEqual((a + week) - a, week)
    700         self.assertEqual((a + day) - a, day)
    701         self.assertEqual((a - week) - a, -week)
    702         self.assertEqual((a - day) - a, -day)
    703         self.assertEqual(a - (a + week), -week)
    704         self.assertEqual(a - (a + day), -day)
    705         self.assertEqual(a - (a - week), week)
    706         self.assertEqual(a - (a - day), day)
    707 
    708         # Add/sub ints, longs, floats should be illegal
    709         for i in 1, 1L, 1.0:
    710             self.assertRaises(TypeError, lambda: a+i)
    711             self.assertRaises(TypeError, lambda: a-i)
    712             self.assertRaises(TypeError, lambda: i+a)
    713             self.assertRaises(TypeError, lambda: i-a)
    714 
    715         # delta - date is senseless.
    716         self.assertRaises(TypeError, lambda: day - a)
    717         # mixing date and (delta or date) via * or // is senseless
    718         self.assertRaises(TypeError, lambda: day * a)
    719         self.assertRaises(TypeError, lambda: a * day)
    720         self.assertRaises(TypeError, lambda: day // a)
    721         self.assertRaises(TypeError, lambda: a // day)
    722         self.assertRaises(TypeError, lambda: a * a)
    723         self.assertRaises(TypeError, lambda: a // a)
    724         # date + date is senseless
    725         self.assertRaises(TypeError, lambda: a + a)
    726 
    727     def test_overflow(self):
    728         tiny = self.theclass.resolution
    729 
    730         for delta in [tiny, timedelta(1), timedelta(2)]:
    731             dt = self.theclass.min + delta
    732             dt -= delta  # no problem
    733             self.assertRaises(OverflowError, dt.__sub__, delta)
    734             self.assertRaises(OverflowError, dt.__add__, -delta)
    735 
    736             dt = self.theclass.max - delta
    737             dt += delta  # no problem
    738             self.assertRaises(OverflowError, dt.__add__, delta)
    739             self.assertRaises(OverflowError, dt.__sub__, -delta)
    740 
    741     def test_fromtimestamp(self):
    742         import time
    743 
    744         # Try an arbitrary fixed value.
    745         year, month, day = 1999, 9, 19
    746         ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
    747         d = self.theclass.fromtimestamp(ts)
    748         self.assertEqual(d.year, year)
    749         self.assertEqual(d.month, month)
    750         self.assertEqual(d.day, day)
    751 
    752     def test_insane_fromtimestamp(self):
    753         # It's possible that some platform maps time_t to double,
    754         # and that this test will fail there.  This test should
    755         # exempt such platforms (provided they return reasonable
    756         # results!).
    757         for insane in -1e200, 1e200:
    758             self.assertRaises(ValueError, self.theclass.fromtimestamp,
    759                               insane)
    760 
    761     def test_today(self):
    762         import time
    763 
    764         # We claim that today() is like fromtimestamp(time.time()), so
    765         # prove it.
    766         for dummy in range(3):
    767             today = self.theclass.today()
    768             ts = time.time()
    769             todayagain = self.theclass.fromtimestamp(ts)
    770             if today == todayagain:
    771                 break
    772             # There are several legit reasons that could fail:
    773             # 1. It recently became midnight, between the today() and the
    774             #    time() calls.
    775             # 2. The platform time() has such fine resolution that we'll
    776             #    never get the same value twice.
    777             # 3. The platform time() has poor resolution, and we just
    778             #    happened to call today() right before a resolution quantum
    779             #    boundary.
    780             # 4. The system clock got fiddled between calls.
    781             # In any case, wait a little while and try again.
    782             time.sleep(0.1)
    783 
    784         # It worked or it didn't.  If it didn't, assume it's reason #2, and
    785         # let the test pass if they're within half a second of each other.
    786         self.assertTrue(today == todayagain or
    787                         abs(todayagain - today) < timedelta(seconds=0.5))
    788 
    789     def test_weekday(self):
    790         for i in range(7):
    791             # March 4, 2002 is a Monday
    792             self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
    793             self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
    794             # January 2, 1956 is a Monday
    795             self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
    796             self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
    797 
    798     def test_isocalendar(self):
    799         # Check examples from
    800         # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
    801         for i in range(7):
    802             d = self.theclass(2003, 12, 22+i)
    803             self.assertEqual(d.isocalendar(), (2003, 52, i+1))
    804             d = self.theclass(2003, 12, 29) + timedelta(i)
    805             self.assertEqual(d.isocalendar(), (2004, 1, i+1))
    806             d = self.theclass(2004, 1, 5+i)
    807             self.assertEqual(d.isocalendar(), (2004, 2, i+1))
    808             d = self.theclass(2009, 12, 21+i)
    809             self.assertEqual(d.isocalendar(), (2009, 52, i+1))
    810             d = self.theclass(2009, 12, 28) + timedelta(i)
    811             self.assertEqual(d.isocalendar(), (2009, 53, i+1))
    812             d = self.theclass(2010, 1, 4+i)
    813             self.assertEqual(d.isocalendar(), (2010, 1, i+1))
    814 
    815     def test_iso_long_years(self):
    816         # Calculate long ISO years and compare to table from
    817         # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
    818         ISO_LONG_YEARS_TABLE = """
    819               4   32   60   88
    820               9   37   65   93
    821              15   43   71   99
    822              20   48   76
    823              26   54   82
    824 
    825             105  133  161  189
    826             111  139  167  195
    827             116  144  172
    828             122  150  178
    829             128  156  184
    830 
    831             201  229  257  285
    832             207  235  263  291
    833             212  240  268  296
    834             218  246  274
    835             224  252  280
    836 
    837             303  331  359  387
    838             308  336  364  392
    839             314  342  370  398
    840             320  348  376
    841             325  353  381
    842         """
    843         iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
    844         iso_long_years.sort()
    845         L = []
    846         for i in range(400):
    847             d = self.theclass(2000+i, 12, 31)
    848             d1 = self.theclass(1600+i, 12, 31)
    849             self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
    850             if d.isocalendar()[1] == 53:
    851                 L.append(i)
    852         self.assertEqual(L, iso_long_years)
    853 
    854     def test_isoformat(self):
    855         t = self.theclass(2, 3, 2)
    856         self.assertEqual(t.isoformat(), "0002-03-02")
    857 
    858     def test_ctime(self):
    859         t = self.theclass(2002, 3, 2)
    860         self.assertEqual(t.ctime(), "Sat Mar  2 00:00:00 2002")
    861 
    862     def test_strftime(self):
    863         t = self.theclass(2005, 3, 2)
    864         self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
    865         self.assertEqual(t.strftime(""), "") # SF bug #761337
    866         self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
    867 
    868         self.assertRaises(TypeError, t.strftime) # needs an arg
    869         self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
    870         self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
    871 
    872         # test that unicode input is allowed (issue 2782)
    873         self.assertEqual(t.strftime(u"%m"), "03")
    874 
    875         # A naive object replaces %z and %Z w/ empty strings.
    876         self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
    877 
    878         #make sure that invalid format specifiers are handled correctly
    879         #self.assertRaises(ValueError, t.strftime, "%e")
    880         #self.assertRaises(ValueError, t.strftime, "%")
    881         #self.assertRaises(ValueError, t.strftime, "%#")
    882 
    883         #oh well, some systems just ignore those invalid ones.
    884         #at least, excercise them to make sure that no crashes
    885         #are generated
    886         for f in ["%e", "%", "%#"]:
    887             try:
    888                 t.strftime(f)
    889             except ValueError:
    890                 pass
    891 
    892         #check that this standard extension works
    893         t.strftime("%f")
    894 
    895 
    896     def test_format(self):
    897         dt = self.theclass(2007, 9, 10)
    898         self.assertEqual(dt.__format__(''), str(dt))
    899 
    900         # check that a derived class's __str__() gets called
    901         class A(self.theclass):
    902             def __str__(self):
    903                 return 'A'
    904         a = A(2007, 9, 10)
    905         self.assertEqual(a.__format__(''), 'A')
    906 
    907         # check that a derived class's strftime gets called
    908         class B(self.theclass):
    909             def strftime(self, format_spec):
    910                 return 'B'
    911         b = B(2007, 9, 10)
    912         self.assertEqual(b.__format__(''), str(dt))
    913 
    914         for fmt in ["m:%m d:%d y:%y",
    915                     "m:%m d:%d y:%y H:%H M:%M S:%S",
    916                     "%z %Z",
    917                     ]:
    918             self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
    919             self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
    920             self.assertEqual(b.__format__(fmt), 'B')
    921 
    922     def test_resolution_info(self):
    923         self.assertIsInstance(self.theclass.min, self.theclass)
    924         self.assertIsInstance(self.theclass.max, self.theclass)
    925         self.assertIsInstance(self.theclass.resolution, timedelta)
    926         self.assertTrue(self.theclass.max > self.theclass.min)
    927 
    928     def test_extreme_timedelta(self):
    929         big = self.theclass.max - self.theclass.min
    930         # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
    931         n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
    932         # n == 315537897599999999 ~= 2**58.13
    933         justasbig = timedelta(0, 0, n)
    934         self.assertEqual(big, justasbig)
    935         self.assertEqual(self.theclass.min + big, self.theclass.max)
    936         self.assertEqual(self.theclass.max - big, self.theclass.min)
    937 
    938     def test_timetuple(self):
    939         for i in range(7):
    940             # January 2, 1956 is a Monday (0)
    941             d = self.theclass(1956, 1, 2+i)
    942             t = d.timetuple()
    943             self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
    944             # February 1, 1956 is a Wednesday (2)
    945             d = self.theclass(1956, 2, 1+i)
    946             t = d.timetuple()
    947             self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
    948             # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
    949             # of the year.
    950             d = self.theclass(1956, 3, 1+i)
    951             t = d.timetuple()
    952             self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
    953             self.assertEqual(t.tm_year, 1956)
    954             self.assertEqual(t.tm_mon, 3)
    955             self.assertEqual(t.tm_mday, 1+i)
    956             self.assertEqual(t.tm_hour, 0)
    957             self.assertEqual(t.tm_min, 0)
    958             self.assertEqual(t.tm_sec, 0)
    959             self.assertEqual(t.tm_wday, (3+i)%7)
    960             self.assertEqual(t.tm_yday, 61+i)
    961             self.assertEqual(t.tm_isdst, -1)
    962 
    963     def test_pickling(self):
    964         args = 6, 7, 23
    965         orig = self.theclass(*args)
    966         for pickler, unpickler, proto in pickle_choices:
    967             green = pickler.dumps(orig, proto)
    968             derived = unpickler.loads(green)
    969             self.assertEqual(orig, derived)
    970 
    971     def test_compare(self):
    972         t1 = self.theclass(2, 3, 4)
    973         t2 = self.theclass(2, 3, 4)
    974         self.assertTrue(t1 == t2)
    975         self.assertTrue(t1 <= t2)
    976         self.assertTrue(t1 >= t2)
    977         self.assertTrue(not t1 != t2)
    978         self.assertTrue(not t1 < t2)
    979         self.assertTrue(not t1 > t2)
    980         self.assertEqual(cmp(t1, t2), 0)
    981         self.assertEqual(cmp(t2, t1), 0)
    982 
    983         for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
    984             t2 = self.theclass(*args)   # this is larger than t1
    985             self.assertTrue(t1 < t2)
    986             self.assertTrue(t2 > t1)
    987             self.assertTrue(t1 <= t2)
    988             self.assertTrue(t2 >= t1)
    989             self.assertTrue(t1 != t2)
    990             self.assertTrue(t2 != t1)
    991             self.assertTrue(not t1 == t2)
    992             self.assertTrue(not t2 == t1)
    993             self.assertTrue(not t1 > t2)
    994             self.assertTrue(not t2 < t1)
    995             self.assertTrue(not t1 >= t2)
    996             self.assertTrue(not t2 <= t1)
    997             self.assertEqual(cmp(t1, t2), -1)
    998             self.assertEqual(cmp(t2, t1), 1)
    999 
   1000         for badarg in OTHERSTUFF:
   1001             self.assertEqual(t1 == badarg, False)
   1002             self.assertEqual(t1 != badarg, True)
   1003             self.assertEqual(badarg == t1, False)
   1004             self.assertEqual(badarg != t1, True)
   1005 
   1006             self.assertRaises(TypeError, lambda: t1 < badarg)
   1007             self.assertRaises(TypeError, lambda: t1 > badarg)
   1008             self.assertRaises(TypeError, lambda: t1 >= badarg)
   1009             self.assertRaises(TypeError, lambda: badarg <= t1)
   1010             self.assertRaises(TypeError, lambda: badarg < t1)
   1011             self.assertRaises(TypeError, lambda: badarg > t1)
   1012             self.assertRaises(TypeError, lambda: badarg >= t1)
   1013 
   1014     def test_mixed_compare(self):
   1015         our = self.theclass(2000, 4, 5)
   1016         self.assertRaises(TypeError, cmp, our, 1)
   1017         self.assertRaises(TypeError, cmp, 1, our)
   1018 
   1019         class AnotherDateTimeClass(object):
   1020             def __cmp__(self, other):
   1021                 # Return "equal" so calling this can't be confused with
   1022                 # compare-by-address (which never says "equal" for distinct
   1023                 # objects).
   1024                 return 0
   1025             __hash__ = None # Silence Py3k warning
   1026 
   1027         # This still errors, because date and datetime comparison raise
   1028         # TypeError instead of NotImplemented when they don't know what to
   1029         # do, in order to stop comparison from falling back to the default
   1030         # compare-by-address.
   1031         their = AnotherDateTimeClass()
   1032         self.assertRaises(TypeError, cmp, our, their)
   1033         # Oops:  The next stab raises TypeError in the C implementation,
   1034         # but not in the Python implementation of datetime.  The difference
   1035         # is due to that the Python implementation defines __cmp__ but
   1036         # the C implementation defines tp_richcompare.  This is more pain
   1037         # to fix than it's worth, so commenting out the test.
   1038         # self.assertEqual(cmp(their, our), 0)
   1039 
   1040         # But date and datetime comparison return NotImplemented instead if the
   1041         # other object has a timetuple attr.  This gives the other object a
   1042         # chance to do the comparison.
   1043         class Comparable(AnotherDateTimeClass):
   1044             def timetuple(self):
   1045                 return ()
   1046 
   1047         their = Comparable()
   1048         self.assertEqual(cmp(our, their), 0)
   1049         self.assertEqual(cmp(their, our), 0)
   1050         self.assertTrue(our == their)
   1051         self.assertTrue(their == our)
   1052 
   1053     def test_bool(self):
   1054         # All dates are considered true.
   1055         self.assertTrue(self.theclass.min)
   1056         self.assertTrue(self.theclass.max)
   1057 
   1058     def test_strftime_out_of_range(self):
   1059         # For nasty technical reasons, we can't handle years before 1900.
   1060         cls = self.theclass
   1061         self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
   1062         for y in 1, 49, 51, 99, 100, 1000, 1899:
   1063             self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
   1064 
   1065     def test_replace(self):
   1066         cls = self.theclass
   1067         args = [1, 2, 3]
   1068         base = cls(*args)
   1069         self.assertEqual(base, base.replace())
   1070 
   1071         i = 0
   1072         for name, newval in (("year", 2),
   1073                              ("month", 3),
   1074                              ("day", 4)):
   1075             newargs = args[:]
   1076             newargs[i] = newval
   1077             expected = cls(*newargs)
   1078             got = base.replace(**{name: newval})
   1079             self.assertEqual(expected, got)
   1080             i += 1
   1081 
   1082         # Out of bounds.
   1083         base = cls(2000, 2, 29)
   1084         self.assertRaises(ValueError, base.replace, year=2001)
   1085 
   1086     def test_subclass_date(self):
   1087 
   1088         class C(self.theclass):
   1089             theAnswer = 42
   1090 
   1091             def __new__(cls, *args, **kws):
   1092                 temp = kws.copy()
   1093                 extra = temp.pop('extra')
   1094                 result = self.theclass.__new__(cls, *args, **temp)
   1095                 result.extra = extra
   1096                 return result
   1097 
   1098             def newmeth(self, start):
   1099                 return start + self.year + self.month
   1100 
   1101         args = 2003, 4, 14
   1102 
   1103         dt1 = self.theclass(*args)
   1104         dt2 = C(*args, **{'extra': 7})
   1105 
   1106         self.assertEqual(dt2.__class__, C)
   1107         self.assertEqual(dt2.theAnswer, 42)
   1108         self.assertEqual(dt2.extra, 7)
   1109         self.assertEqual(dt1.toordinal(), dt2.toordinal())
   1110         self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
   1111 
   1112     def test_pickling_subclass_date(self):
   1113 
   1114         args = 6, 7, 23
   1115         orig = SubclassDate(*args)
   1116         for pickler, unpickler, proto in pickle_choices:
   1117             green = pickler.dumps(orig, proto)
   1118             derived = unpickler.loads(green)
   1119             self.assertEqual(orig, derived)
   1120 
   1121     def test_backdoor_resistance(self):
   1122         # For fast unpickling, the constructor accepts a pickle string.
   1123         # This is a low-overhead backdoor.  A user can (by intent or
   1124         # mistake) pass a string directly, which (if it's the right length)
   1125         # will get treated like a pickle, and bypass the normal sanity
   1126         # checks in the constructor.  This can create insane objects.
   1127         # The constructor doesn't want to burn the time to validate all
   1128         # fields, but does check the month field.  This stops, e.g.,
   1129         # datetime.datetime('1995-03-25') from yielding an insane object.
   1130         base = '1995-03-25'
   1131         if not issubclass(self.theclass, datetime):
   1132             base = base[:4]
   1133         for month_byte in '9', chr(0), chr(13), '\xff':
   1134             self.assertRaises(TypeError, self.theclass,
   1135                                          base[:2] + month_byte + base[3:])
   1136         for ord_byte in range(1, 13):
   1137             # This shouldn't blow up because of the month byte alone.  If
   1138             # the implementation changes to do more-careful checking, it may
   1139             # blow up because other fields are insane.
   1140             self.theclass(base[:2] + chr(ord_byte) + base[3:])
   1141 
   1142 #############################################################################
   1143 # datetime tests
   1144 
   1145 class SubclassDatetime(datetime):
   1146     sub_var = 1
   1147 
   1148 class TestDateTime(TestDate):
   1149 
   1150     theclass = datetime
   1151 
   1152     def test_basic_attributes(self):
   1153         dt = self.theclass(2002, 3, 1, 12, 0)
   1154         self.assertEqual(dt.year, 2002)
   1155         self.assertEqual(dt.month, 3)
   1156         self.assertEqual(dt.day, 1)
   1157         self.assertEqual(dt.hour, 12)
   1158         self.assertEqual(dt.minute, 0)
   1159         self.assertEqual(dt.second, 0)
   1160         self.assertEqual(dt.microsecond, 0)
   1161 
   1162     def test_basic_attributes_nonzero(self):
   1163         # Make sure all attributes are non-zero so bugs in
   1164         # bit-shifting access show up.
   1165         dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
   1166         self.assertEqual(dt.year, 2002)
   1167         self.assertEqual(dt.month, 3)
   1168         self.assertEqual(dt.day, 1)
   1169         self.assertEqual(dt.hour, 12)
   1170         self.assertEqual(dt.minute, 59)
   1171         self.assertEqual(dt.second, 59)
   1172         self.assertEqual(dt.microsecond, 8000)
   1173 
   1174     def test_roundtrip(self):
   1175         for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
   1176                    self.theclass.now()):
   1177             # Verify dt -> string -> datetime identity.
   1178             s = repr(dt)
   1179             self.assertTrue(s.startswith('datetime.'))
   1180             s = s[9:]
   1181             dt2 = eval(s)
   1182             self.assertEqual(dt, dt2)
   1183 
   1184             # Verify identity via reconstructing from pieces.
   1185             dt2 = self.theclass(dt.year, dt.month, dt.day,
   1186                                 dt.hour, dt.minute, dt.second,
   1187                                 dt.microsecond)
   1188             self.assertEqual(dt, dt2)
   1189 
   1190     def test_isoformat(self):
   1191         t = self.theclass(2, 3, 2, 4, 5, 1, 123)
   1192         self.assertEqual(t.isoformat(),    "0002-03-02T04:05:01.000123")
   1193         self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
   1194         self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
   1195         self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
   1196         # str is ISO format with the separator forced to a blank.
   1197         self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
   1198 
   1199         t = self.theclass(2, 3, 2)
   1200         self.assertEqual(t.isoformat(),    "0002-03-02T00:00:00")
   1201         self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
   1202         self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
   1203         # str is ISO format with the separator forced to a blank.
   1204         self.assertEqual(str(t), "0002-03-02 00:00:00")
   1205 
   1206     def test_format(self):
   1207         dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
   1208         self.assertEqual(dt.__format__(''), str(dt))
   1209 
   1210         # check that a derived class's __str__() gets called
   1211         class A(self.theclass):
   1212             def __str__(self):
   1213                 return 'A'
   1214         a = A(2007, 9, 10, 4, 5, 1, 123)
   1215         self.assertEqual(a.__format__(''), 'A')
   1216 
   1217         # check that a derived class's strftime gets called
   1218         class B(self.theclass):
   1219             def strftime(self, format_spec):
   1220                 return 'B'
   1221         b = B(2007, 9, 10, 4, 5, 1, 123)
   1222         self.assertEqual(b.__format__(''), str(dt))
   1223 
   1224         for fmt in ["m:%m d:%d y:%y",
   1225                     "m:%m d:%d y:%y H:%H M:%M S:%S",
   1226                     "%z %Z",
   1227                     ]:
   1228             self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
   1229             self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
   1230             self.assertEqual(b.__format__(fmt), 'B')
   1231 
   1232     def test_more_ctime(self):
   1233         # Test fields that TestDate doesn't touch.
   1234         import time
   1235 
   1236         t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
   1237         self.assertEqual(t.ctime(), "Sat Mar  2 18:03:05 2002")
   1238         # Oops!  The next line fails on Win2K under MSVC 6, so it's commented
   1239         # out.  The difference is that t.ctime() produces " 2" for the day,
   1240         # but platform ctime() produces "02" for the day.  According to
   1241         # C99, t.ctime() is correct here.
   1242         # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
   1243 
   1244         # So test a case where that difference doesn't matter.
   1245         t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
   1246         self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
   1247 
   1248     def test_tz_independent_comparing(self):
   1249         dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
   1250         dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
   1251         dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
   1252         self.assertEqual(dt1, dt3)
   1253         self.assertTrue(dt2 > dt3)
   1254 
   1255         # Make sure comparison doesn't forget microseconds, and isn't done
   1256         # via comparing a float timestamp (an IEEE double doesn't have enough
   1257         # precision to span microsecond resolution across years 1 thru 9999,
   1258         # so comparing via timestamp necessarily calls some distinct values
   1259         # equal).
   1260         dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
   1261         us = timedelta(microseconds=1)
   1262         dt2 = dt1 + us
   1263         self.assertEqual(dt2 - dt1, us)
   1264         self.assertTrue(dt1 < dt2)
   1265 
   1266     def test_strftime_with_bad_tzname_replace(self):
   1267         # verify ok if tzinfo.tzname().replace() returns a non-string
   1268         class MyTzInfo(FixedOffset):
   1269             def tzname(self, dt):
   1270                 class MyStr(str):
   1271                     def replace(self, *args):
   1272                         return None
   1273                 return MyStr('name')
   1274         t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
   1275         self.assertRaises(TypeError, t.strftime, '%Z')
   1276 
   1277     def test_bad_constructor_arguments(self):
   1278         # bad years
   1279         self.theclass(MINYEAR, 1, 1)  # no exception
   1280         self.theclass(MAXYEAR, 1, 1)  # no exception
   1281         self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
   1282         self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
   1283         # bad months
   1284         self.theclass(2000, 1, 1)    # no exception
   1285         self.theclass(2000, 12, 1)   # no exception
   1286         self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
   1287         self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
   1288         # bad days
   1289         self.theclass(2000, 2, 29)   # no exception
   1290         self.theclass(2004, 2, 29)   # no exception
   1291         self.theclass(2400, 2, 29)   # no exception
   1292         self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
   1293         self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
   1294         self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
   1295         self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
   1296         self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
   1297         self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
   1298         # bad hours
   1299         self.theclass(2000, 1, 31, 0)    # no exception
   1300         self.theclass(2000, 1, 31, 23)   # no exception
   1301         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
   1302         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
   1303         # bad minutes
   1304         self.theclass(2000, 1, 31, 23, 0)    # no exception
   1305         self.theclass(2000, 1, 31, 23, 59)   # no exception
   1306         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
   1307         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
   1308         # bad seconds
   1309         self.theclass(2000, 1, 31, 23, 59, 0)    # no exception
   1310         self.theclass(2000, 1, 31, 23, 59, 59)   # no exception
   1311         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
   1312         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
   1313         # bad microseconds
   1314         self.theclass(2000, 1, 31, 23, 59, 59, 0)    # no exception
   1315         self.theclass(2000, 1, 31, 23, 59, 59, 999999)   # no exception
   1316         self.assertRaises(ValueError, self.theclass,
   1317                           2000, 1, 31, 23, 59, 59, -1)
   1318         self.assertRaises(ValueError, self.theclass,
   1319                           2000, 1, 31, 23, 59, 59,
   1320                           1000000)
   1321 
   1322     def test_hash_equality(self):
   1323         d = self.theclass(2000, 12, 31, 23, 30, 17)
   1324         e = self.theclass(2000, 12, 31, 23, 30, 17)
   1325         self.assertEqual(d, e)
   1326         self.assertEqual(hash(d), hash(e))
   1327 
   1328         dic = {d: 1}
   1329         dic[e] = 2
   1330         self.assertEqual(len(dic), 1)
   1331         self.assertEqual(dic[d], 2)
   1332         self.assertEqual(dic[e], 2)
   1333 
   1334         d = self.theclass(2001,  1,  1,  0,  5, 17)
   1335         e = self.theclass(2001,  1,  1,  0,  5, 17)
   1336         self.assertEqual(d, e)
   1337         self.assertEqual(hash(d), hash(e))
   1338 
   1339         dic = {d: 1}
   1340         dic[e] = 2
   1341         self.assertEqual(len(dic), 1)
   1342         self.assertEqual(dic[d], 2)
   1343         self.assertEqual(dic[e], 2)
   1344 
   1345     def test_computations(self):
   1346         a = self.theclass(2002, 1, 31)
   1347         b = self.theclass(1956, 1, 31)
   1348         diff = a-b
   1349         self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
   1350         self.assertEqual(diff.seconds, 0)
   1351         self.assertEqual(diff.microseconds, 0)
   1352         a = self.theclass(2002, 3, 2, 17, 6)
   1353         millisec = timedelta(0, 0, 1000)
   1354         hour = timedelta(0, 3600)
   1355         day = timedelta(1)
   1356         week = timedelta(7)
   1357         self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
   1358         self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
   1359         self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
   1360         self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
   1361         self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
   1362         self.assertEqual(a - hour, a + -hour)
   1363         self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
   1364         self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
   1365         self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
   1366         self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
   1367         self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
   1368         self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
   1369         self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
   1370         self.assertEqual((a + week) - a, week)
   1371         self.assertEqual((a + day) - a, day)
   1372         self.assertEqual((a + hour) - a, hour)
   1373         self.assertEqual((a + millisec) - a, millisec)
   1374         self.assertEqual((a - week) - a, -week)
   1375         self.assertEqual((a - day) - a, -day)
   1376         self.assertEqual((a - hour) - a, -hour)
   1377         self.assertEqual((a - millisec) - a, -millisec)
   1378         self.assertEqual(a - (a + week), -week)
   1379         self.assertEqual(a - (a + day), -day)
   1380         self.assertEqual(a - (a + hour), -hour)
   1381         self.assertEqual(a - (a + millisec), -millisec)
   1382         self.assertEqual(a - (a - week), week)
   1383         self.assertEqual(a - (a - day), day)
   1384         self.assertEqual(a - (a - hour), hour)
   1385         self.assertEqual(a - (a - millisec), millisec)
   1386         self.assertEqual(a + (week + day + hour + millisec),
   1387                          self.theclass(2002, 3, 10, 18, 6, 0, 1000))
   1388         self.assertEqual(a + (week + day + hour + millisec),
   1389                          (((a + week) + day) + hour) + millisec)
   1390         self.assertEqual(a - (week + day + hour + millisec),
   1391                          self.theclass(2002, 2, 22, 16, 5, 59, 999000))
   1392         self.assertEqual(a - (week + day + hour + millisec),
   1393                          (((a - week) - day) - hour) - millisec)
   1394         # Add/sub ints, longs, floats should be illegal
   1395         for i in 1, 1L, 1.0:
   1396             self.assertRaises(TypeError, lambda: a+i)
   1397             self.assertRaises(TypeError, lambda: a-i)
   1398             self.assertRaises(TypeError, lambda: i+a)
   1399             self.assertRaises(TypeError, lambda: i-a)
   1400 
   1401         # delta - datetime is senseless.
   1402         self.assertRaises(TypeError, lambda: day - a)
   1403         # mixing datetime and (delta or datetime) via * or // is senseless
   1404         self.assertRaises(TypeError, lambda: day * a)
   1405         self.assertRaises(TypeError, lambda: a * day)
   1406         self.assertRaises(TypeError, lambda: day // a)
   1407         self.assertRaises(TypeError, lambda: a // day)
   1408         self.assertRaises(TypeError, lambda: a * a)
   1409         self.assertRaises(TypeError, lambda: a // a)
   1410         # datetime + datetime is senseless
   1411         self.assertRaises(TypeError, lambda: a + a)
   1412 
   1413     def test_pickling(self):
   1414         args = 6, 7, 23, 20, 59, 1, 64**2
   1415         orig = self.theclass(*args)
   1416         for pickler, unpickler, proto in pickle_choices:
   1417             green = pickler.dumps(orig, proto)
   1418             derived = unpickler.loads(green)
   1419             self.assertEqual(orig, derived)
   1420 
   1421     def test_more_pickling(self):
   1422         a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
   1423         s = pickle.dumps(a)
   1424         b = pickle.loads(s)
   1425         self.assertEqual(b.year, 2003)
   1426         self.assertEqual(b.month, 2)
   1427         self.assertEqual(b.day, 7)
   1428 
   1429     def test_pickling_subclass_datetime(self):
   1430         args = 6, 7, 23, 20, 59, 1, 64**2
   1431         orig = SubclassDatetime(*args)
   1432         for pickler, unpickler, proto in pickle_choices:
   1433             green = pickler.dumps(orig, proto)
   1434             derived = unpickler.loads(green)
   1435             self.assertEqual(orig, derived)
   1436 
   1437     def test_more_compare(self):
   1438         # The test_compare() inherited from TestDate covers the error cases.
   1439         # We just want to test lexicographic ordering on the members datetime
   1440         # has that date lacks.
   1441         args = [2000, 11, 29, 20, 58, 16, 999998]
   1442         t1 = self.theclass(*args)
   1443         t2 = self.theclass(*args)
   1444         self.assertTrue(t1 == t2)
   1445         self.assertTrue(t1 <= t2)
   1446         self.assertTrue(t1 >= t2)
   1447         self.assertTrue(not t1 != t2)
   1448         self.assertTrue(not t1 < t2)
   1449         self.assertTrue(not t1 > t2)
   1450         self.assertEqual(cmp(t1, t2), 0)
   1451         self.assertEqual(cmp(t2, t1), 0)
   1452 
   1453         for i in range(len(args)):
   1454             newargs = args[:]
   1455             newargs[i] = args[i] + 1
   1456             t2 = self.theclass(*newargs)   # this is larger than t1
   1457             self.assertTrue(t1 < t2)
   1458             self.assertTrue(t2 > t1)
   1459             self.assertTrue(t1 <= t2)
   1460             self.assertTrue(t2 >= t1)
   1461             self.assertTrue(t1 != t2)
   1462             self.assertTrue(t2 != t1)
   1463             self.assertTrue(not t1 == t2)
   1464             self.assertTrue(not t2 == t1)
   1465             self.assertTrue(not t1 > t2)
   1466             self.assertTrue(not t2 < t1)
   1467             self.assertTrue(not t1 >= t2)
   1468             self.assertTrue(not t2 <= t1)
   1469             self.assertEqual(cmp(t1, t2), -1)
   1470             self.assertEqual(cmp(t2, t1), 1)
   1471 
   1472 
   1473     # A helper for timestamp constructor tests.
   1474     def verify_field_equality(self, expected, got):
   1475         self.assertEqual(expected.tm_year, got.year)
   1476         self.assertEqual(expected.tm_mon, got.month)
   1477         self.assertEqual(expected.tm_mday, got.day)
   1478         self.assertEqual(expected.tm_hour, got.hour)
   1479         self.assertEqual(expected.tm_min, got.minute)
   1480         self.assertEqual(expected.tm_sec, got.second)
   1481 
   1482     def test_fromtimestamp(self):
   1483         import time
   1484 
   1485         ts = time.time()
   1486         expected = time.localtime(ts)
   1487         got = self.theclass.fromtimestamp(ts)
   1488         self.verify_field_equality(expected, got)
   1489 
   1490     def test_utcfromtimestamp(self):
   1491         import time
   1492 
   1493         ts = time.time()
   1494         expected = time.gmtime(ts)
   1495         got = self.theclass.utcfromtimestamp(ts)
   1496         self.verify_field_equality(expected, got)
   1497 
   1498     def test_microsecond_rounding(self):
   1499         # Test whether fromtimestamp "rounds up" floats that are less
   1500         # than one microsecond smaller than an integer.
   1501         self.assertEqual(self.theclass.fromtimestamp(0.9999999),
   1502                          self.theclass.fromtimestamp(1))
   1503 
   1504     def test_insane_fromtimestamp(self):
   1505         # It's possible that some platform maps time_t to double,
   1506         # and that this test will fail there.  This test should
   1507         # exempt such platforms (provided they return reasonable
   1508         # results!).
   1509         for insane in -1e200, 1e200:
   1510             self.assertRaises(ValueError, self.theclass.fromtimestamp,
   1511                               insane)
   1512 
   1513     def test_insane_utcfromtimestamp(self):
   1514         # It's possible that some platform maps time_t to double,
   1515         # and that this test will fail there.  This test should
   1516         # exempt such platforms (provided they return reasonable
   1517         # results!).
   1518         for insane in -1e200, 1e200:
   1519             self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
   1520                               insane)
   1521     @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
   1522     def test_negative_float_fromtimestamp(self):
   1523         # The result is tz-dependent; at least test that this doesn't
   1524         # fail (like it did before bug 1646728 was fixed).
   1525         self.theclass.fromtimestamp(-1.05)
   1526 
   1527     @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
   1528     def test_negative_float_utcfromtimestamp(self):
   1529         d = self.theclass.utcfromtimestamp(-1.05)
   1530         self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
   1531 
   1532     def test_utcnow(self):
   1533         import time
   1534 
   1535         # Call it a success if utcnow() and utcfromtimestamp() are within
   1536         # a second of each other.
   1537         tolerance = timedelta(seconds=1)
   1538         for dummy in range(3):
   1539             from_now = self.theclass.utcnow()
   1540             from_timestamp = self.theclass.utcfromtimestamp(time.time())
   1541             if abs(from_timestamp - from_now) <= tolerance:
   1542                 break
   1543             # Else try again a few times.
   1544         self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
   1545 
   1546     def test_strptime(self):
   1547         import _strptime
   1548 
   1549         string = '2004-12-01 13:02:47.197'
   1550         format = '%Y-%m-%d %H:%M:%S.%f'
   1551         result, frac = _strptime._strptime(string, format)
   1552         expected = self.theclass(*(result[0:6]+(frac,)))
   1553         got = self.theclass.strptime(string, format)
   1554         self.assertEqual(expected, got)
   1555 
   1556     def test_more_timetuple(self):
   1557         # This tests fields beyond those tested by the TestDate.test_timetuple.
   1558         t = self.theclass(2004, 12, 31, 6, 22, 33)
   1559         self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
   1560         self.assertEqual(t.timetuple(),
   1561                          (t.year, t.month, t.day,
   1562                           t.hour, t.minute, t.second,
   1563                           t.weekday(),
   1564                           t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
   1565                           -1))
   1566         tt = t.timetuple()
   1567         self.assertEqual(tt.tm_year, t.year)
   1568         self.assertEqual(tt.tm_mon, t.month)
   1569         self.assertEqual(tt.tm_mday, t.day)
   1570         self.assertEqual(tt.tm_hour, t.hour)
   1571         self.assertEqual(tt.tm_min, t.minute)
   1572         self.assertEqual(tt.tm_sec, t.second)
   1573         self.assertEqual(tt.tm_wday, t.weekday())
   1574         self.assertEqual(tt.tm_yday, t.toordinal() -
   1575                                      date(t.year, 1, 1).toordinal() + 1)
   1576         self.assertEqual(tt.tm_isdst, -1)
   1577 
   1578     def test_more_strftime(self):
   1579         # This tests fields beyond those tested by the TestDate.test_strftime.
   1580         t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
   1581         self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
   1582                                     "12 31 04 000047 33 22 06 366")
   1583 
   1584     def test_extract(self):
   1585         dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
   1586         self.assertEqual(dt.date(), date(2002, 3, 4))
   1587         self.assertEqual(dt.time(), time(18, 45, 3, 1234))
   1588 
   1589     def test_combine(self):
   1590         d = date(2002, 3, 4)
   1591         t = time(18, 45, 3, 1234)
   1592         expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
   1593         combine = self.theclass.combine
   1594         dt = combine(d, t)
   1595         self.assertEqual(dt, expected)
   1596 
   1597         dt = combine(time=t, date=d)
   1598         self.assertEqual(dt, expected)
   1599 
   1600         self.assertEqual(d, dt.date())
   1601         self.assertEqual(t, dt.time())
   1602         self.assertEqual(dt, combine(dt.date(), dt.time()))
   1603 
   1604         self.assertRaises(TypeError, combine) # need an arg
   1605         self.assertRaises(TypeError, combine, d) # need two args
   1606         self.assertRaises(TypeError, combine, t, d) # args reversed
   1607         self.assertRaises(TypeError, combine, d, t, 1) # too many args
   1608         self.assertRaises(TypeError, combine, "date", "time") # wrong types
   1609 
   1610     def test_replace(self):
   1611         cls = self.theclass
   1612         args = [1, 2, 3, 4, 5, 6, 7]
   1613         base = cls(*args)
   1614         self.assertEqual(base, base.replace())
   1615 
   1616         i = 0
   1617         for name, newval in (("year", 2),
   1618                              ("month", 3),
   1619                              ("day", 4),
   1620                              ("hour", 5),
   1621                              ("minute", 6),
   1622                              ("second", 7),
   1623                              ("microsecond", 8)):
   1624             newargs = args[:]
   1625             newargs[i] = newval
   1626             expected = cls(*newargs)
   1627             got = base.replace(**{name: newval})
   1628             self.assertEqual(expected, got)
   1629             i += 1
   1630 
   1631         # Out of bounds.
   1632         base = cls(2000, 2, 29)
   1633         self.assertRaises(ValueError, base.replace, year=2001)
   1634 
   1635     def test_astimezone(self):
   1636         # Pretty boring!  The TZ test is more interesting here.  astimezone()
   1637         # simply can't be applied to a naive object.
   1638         dt = self.theclass.now()
   1639         f = FixedOffset(44, "")
   1640         self.assertRaises(TypeError, dt.astimezone) # not enough args
   1641         self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
   1642         self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
   1643         self.assertRaises(ValueError, dt.astimezone, f) # naive
   1644         self.assertRaises(ValueError, dt.astimezone, tz=f)  # naive
   1645 
   1646         class Bogus(tzinfo):
   1647             def utcoffset(self, dt): return None
   1648             def dst(self, dt): return timedelta(0)
   1649         bog = Bogus()
   1650         self.assertRaises(ValueError, dt.astimezone, bog)   # naive
   1651 
   1652         class AlsoBogus(tzinfo):
   1653             def utcoffset(self, dt): return timedelta(0)
   1654             def dst(self, dt): return None
   1655         alsobog = AlsoBogus()
   1656         self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
   1657 
   1658     def test_subclass_datetime(self):
   1659 
   1660         class C(self.theclass):
   1661             theAnswer = 42
   1662 
   1663             def __new__(cls, *args, **kws):
   1664                 temp = kws.copy()
   1665                 extra = temp.pop('extra')
   1666                 result = self.theclass.__new__(cls, *args, **temp)
   1667                 result.extra = extra
   1668                 return result
   1669 
   1670             def newmeth(self, start):
   1671                 return start + self.year + self.month + self.second
   1672 
   1673         args = 2003, 4, 14, 12, 13, 41
   1674 
   1675         dt1 = self.theclass(*args)
   1676         dt2 = C(*args, **{'extra': 7})
   1677 
   1678         self.assertEqual(dt2.__class__, C)
   1679         self.assertEqual(dt2.theAnswer, 42)
   1680         self.assertEqual(dt2.extra, 7)
   1681         self.assertEqual(dt1.toordinal(), dt2.toordinal())
   1682         self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
   1683                                           dt1.second - 7)
   1684 
   1685 class SubclassTime(time):
   1686     sub_var = 1
   1687 
   1688 class TestTime(HarmlessMixedComparison, unittest.TestCase):
   1689 
   1690     theclass = time
   1691 
   1692     def test_basic_attributes(self):
   1693         t = self.theclass(12, 0)
   1694         self.assertEqual(t.hour, 12)
   1695         self.assertEqual(t.minute, 0)
   1696         self.assertEqual(t.second, 0)
   1697         self.assertEqual(t.microsecond, 0)
   1698 
   1699     def test_basic_attributes_nonzero(self):
   1700         # Make sure all attributes are non-zero so bugs in
   1701         # bit-shifting access show up.
   1702         t = self.theclass(12, 59, 59, 8000)
   1703         self.assertEqual(t.hour, 12)
   1704         self.assertEqual(t.minute, 59)
   1705         self.assertEqual(t.second, 59)
   1706         self.assertEqual(t.microsecond, 8000)
   1707 
   1708     def test_roundtrip(self):
   1709         t = self.theclass(1, 2, 3, 4)
   1710 
   1711         # Verify t -> string -> time identity.
   1712         s = repr(t)
   1713         self.assertTrue(s.startswith('datetime.'))
   1714         s = s[9:]
   1715         t2 = eval(s)
   1716         self.assertEqual(t, t2)
   1717 
   1718         # Verify identity via reconstructing from pieces.
   1719         t2 = self.theclass(t.hour, t.minute, t.second,
   1720                            t.microsecond)
   1721         self.assertEqual(t, t2)
   1722 
   1723     def test_comparing(self):
   1724         args = [1, 2, 3, 4]
   1725         t1 = self.theclass(*args)
   1726         t2 = self.theclass(*args)
   1727         self.assertTrue(t1 == t2)
   1728         self.assertTrue(t1 <= t2)
   1729         self.assertTrue(t1 >= t2)
   1730         self.assertTrue(not t1 != t2)
   1731         self.assertTrue(not t1 < t2)
   1732         self.assertTrue(not t1 > t2)
   1733         self.assertEqual(cmp(t1, t2), 0)
   1734         self.assertEqual(cmp(t2, t1), 0)
   1735 
   1736         for i in range(len(args)):
   1737             newargs = args[:]
   1738             newargs[i] = args[i] + 1
   1739             t2 = self.theclass(*newargs)   # this is larger than t1
   1740             self.assertTrue(t1 < t2)
   1741             self.assertTrue(t2 > t1)
   1742             self.assertTrue(t1 <= t2)
   1743             self.assertTrue(t2 >= t1)
   1744             self.assertTrue(t1 != t2)
   1745             self.assertTrue(t2 != t1)
   1746             self.assertTrue(not t1 == t2)
   1747             self.assertTrue(not t2 == t1)
   1748             self.assertTrue(not t1 > t2)
   1749             self.assertTrue(not t2 < t1)
   1750             self.assertTrue(not t1 >= t2)
   1751             self.assertTrue(not t2 <= t1)
   1752             self.assertEqual(cmp(t1, t2), -1)
   1753             self.assertEqual(cmp(t2, t1), 1)
   1754 
   1755         for badarg in OTHERSTUFF:
   1756             self.assertEqual(t1 == badarg, False)
   1757             self.assertEqual(t1 != badarg, True)
   1758             self.assertEqual(badarg == t1, False)
   1759             self.assertEqual(badarg != t1, True)
   1760 
   1761             self.assertRaises(TypeError, lambda: t1 <= badarg)
   1762             self.assertRaises(TypeError, lambda: t1 < badarg)
   1763             self.assertRaises(TypeError, lambda: t1 > badarg)
   1764             self.assertRaises(TypeError, lambda: t1 >= badarg)
   1765             self.assertRaises(TypeError, lambda: badarg <= t1)
   1766             self.assertRaises(TypeError, lambda: badarg < t1)
   1767             self.assertRaises(TypeError, lambda: badarg > t1)
   1768             self.assertRaises(TypeError, lambda: badarg >= t1)
   1769 
   1770     def test_bad_constructor_arguments(self):
   1771         # bad hours
   1772         self.theclass(0, 0)    # no exception
   1773         self.theclass(23, 0)   # no exception
   1774         self.assertRaises(ValueError, self.theclass, -1, 0)
   1775         self.assertRaises(ValueError, self.theclass, 24, 0)
   1776         # bad minutes
   1777         self.theclass(23, 0)    # no exception
   1778         self.theclass(23, 59)   # no exception
   1779         self.assertRaises(ValueError, self.theclass, 23, -1)
   1780         self.assertRaises(ValueError, self.theclass, 23, 60)
   1781         # bad seconds
   1782         self.theclass(23, 59, 0)    # no exception
   1783         self.theclass(23, 59, 59)   # no exception
   1784         self.assertRaises(ValueError, self.theclass, 23, 59, -1)
   1785         self.assertRaises(ValueError, self.theclass, 23, 59, 60)
   1786         # bad microseconds
   1787         self.theclass(23, 59, 59, 0)        # no exception
   1788         self.theclass(23, 59, 59, 999999)   # no exception
   1789         self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
   1790         self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
   1791 
   1792     def test_hash_equality(self):
   1793         d = self.theclass(23, 30, 17)
   1794         e = self.theclass(23, 30, 17)
   1795         self.assertEqual(d, e)
   1796         self.assertEqual(hash(d), hash(e))
   1797 
   1798         dic = {d: 1}
   1799         dic[e] = 2
   1800         self.assertEqual(len(dic), 1)
   1801         self.assertEqual(dic[d], 2)
   1802         self.assertEqual(dic[e], 2)
   1803 
   1804         d = self.theclass(0,  5, 17)
   1805         e = self.theclass(0,  5, 17)
   1806         self.assertEqual(d, e)
   1807         self.assertEqual(hash(d), hash(e))
   1808 
   1809         dic = {d: 1}
   1810         dic[e] = 2
   1811         self.assertEqual(len(dic), 1)
   1812         self.assertEqual(dic[d], 2)
   1813         self.assertEqual(dic[e], 2)
   1814 
   1815     def test_isoformat(self):
   1816         t = self.theclass(4, 5, 1, 123)
   1817         self.assertEqual(t.isoformat(), "04:05:01.000123")
   1818         self.assertEqual(t.isoformat(), str(t))
   1819 
   1820         t = self.theclass()
   1821         self.assertEqual(t.isoformat(), "00:00:00")
   1822         self.assertEqual(t.isoformat(), str(t))
   1823 
   1824         t = self.theclass(microsecond=1)
   1825         self.assertEqual(t.isoformat(), "00:00:00.000001")
   1826         self.assertEqual(t.isoformat(), str(t))
   1827 
   1828         t = self.theclass(microsecond=10)
   1829         self.assertEqual(t.isoformat(), "00:00:00.000010")
   1830         self.assertEqual(t.isoformat(), str(t))
   1831 
   1832         t = self.theclass(microsecond=100)
   1833         self.assertEqual(t.isoformat(), "00:00:00.000100")
   1834         self.assertEqual(t.isoformat(), str(t))
   1835 
   1836         t = self.theclass(microsecond=1000)
   1837         self.assertEqual(t.isoformat(), "00:00:00.001000")
   1838         self.assertEqual(t.isoformat(), str(t))
   1839 
   1840         t = self.theclass(microsecond=10000)
   1841         self.assertEqual(t.isoformat(), "00:00:00.010000")
   1842         self.assertEqual(t.isoformat(), str(t))
   1843 
   1844         t = self.theclass(microsecond=100000)
   1845         self.assertEqual(t.isoformat(), "00:00:00.100000")
   1846         self.assertEqual(t.isoformat(), str(t))
   1847 
   1848     def test_1653736(self):
   1849         # verify it doesn't accept extra keyword arguments
   1850         t = self.theclass(second=1)
   1851         self.assertRaises(TypeError, t.isoformat, foo=3)
   1852 
   1853     def test_strftime(self):
   1854         t = self.theclass(1, 2, 3, 4)
   1855         self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
   1856         # A naive object replaces %z and %Z with empty strings.
   1857         self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
   1858 
   1859     def test_format(self):
   1860         t = self.theclass(1, 2, 3, 4)
   1861         self.assertEqual(t.__format__(''), str(t))
   1862 
   1863         # check that a derived class's __str__() gets called
   1864         class A(self.theclass):
   1865             def __str__(self):
   1866                 return 'A'
   1867         a = A(1, 2, 3, 4)
   1868         self.assertEqual(a.__format__(''), 'A')
   1869 
   1870         # check that a derived class's strftime gets called
   1871         class B(self.theclass):
   1872             def strftime(self, format_spec):
   1873                 return 'B'
   1874         b = B(1, 2, 3, 4)
   1875         self.assertEqual(b.__format__(''), str(t))
   1876 
   1877         for fmt in ['%H %M %S',
   1878                     ]:
   1879             self.assertEqual(t.__format__(fmt), t.strftime(fmt))
   1880             self.assertEqual(a.__format__(fmt), t.strftime(fmt))
   1881             self.assertEqual(b.__format__(fmt), 'B')
   1882 
   1883     def test_str(self):
   1884         self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
   1885         self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
   1886         self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
   1887         self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
   1888         self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
   1889 
   1890     def test_repr(self):
   1891         name = 'datetime.' + self.theclass.__name__
   1892         self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
   1893                          "%s(1, 2, 3, 4)" % name)
   1894         self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
   1895                          "%s(10, 2, 3, 4000)" % name)
   1896         self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
   1897                          "%s(0, 2, 3, 400000)" % name)
   1898         self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
   1899                          "%s(12, 2, 3)" % name)
   1900         self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
   1901                          "%s(23, 15)" % name)
   1902 
   1903     def test_resolution_info(self):
   1904         self.assertIsInstance(self.theclass.min, self.theclass)
   1905         self.assertIsInstance(self.theclass.max, self.theclass)
   1906         self.assertIsInstance(self.theclass.resolution, timedelta)
   1907         self.assertTrue(self.theclass.max > self.theclass.min)
   1908 
   1909     def test_pickling(self):
   1910         args = 20, 59, 16, 64**2
   1911         orig = self.theclass(*args)
   1912         for pickler, unpickler, proto in pickle_choices:
   1913             green = pickler.dumps(orig, proto)
   1914             derived = unpickler.loads(green)
   1915             self.assertEqual(orig, derived)
   1916 
   1917     def test_pickling_subclass_time(self):
   1918         args = 20, 59, 16, 64**2
   1919         orig = SubclassTime(*args)
   1920         for pickler, unpickler, proto in pickle_choices:
   1921             green = pickler.dumps(orig, proto)
   1922             derived = unpickler.loads(green)
   1923             self.assertEqual(orig, derived)
   1924 
   1925     def test_bool(self):
   1926         cls = self.theclass
   1927         self.assertTrue(cls(1))
   1928         self.assertTrue(cls(0, 1))
   1929         self.assertTrue(cls(0, 0, 1))
   1930         self.assertTrue(cls(0, 0, 0, 1))
   1931         self.assertTrue(not cls(0))
   1932         self.assertTrue(not cls())
   1933 
   1934     def test_replace(self):
   1935         cls = self.theclass
   1936         args = [1, 2, 3, 4]
   1937         base = cls(*args)
   1938         self.assertEqual(base, base.replace())
   1939 
   1940         i = 0
   1941         for name, newval in (("hour", 5),
   1942                              ("minute", 6),
   1943                              ("second", 7),
   1944                              ("microsecond", 8)):
   1945             newargs = args[:]
   1946             newargs[i] = newval
   1947             expected = cls(*newargs)
   1948             got = base.replace(**{name: newval})
   1949             self.assertEqual(expected, got)
   1950             i += 1
   1951 
   1952         # Out of bounds.
   1953         base = cls(1)
   1954         self.assertRaises(ValueError, base.replace, hour=24)
   1955         self.assertRaises(ValueError, base.replace, minute=-1)
   1956         self.assertRaises(ValueError, base.replace, second=100)
   1957         self.assertRaises(ValueError, base.replace, microsecond=1000000)
   1958 
   1959     def test_subclass_time(self):
   1960 
   1961         class C(self.theclass):
   1962             theAnswer = 42
   1963 
   1964             def __new__(cls, *args, **kws):
   1965                 temp = kws.copy()
   1966                 extra = temp.pop('extra')
   1967                 result = self.theclass.__new__(cls, *args, **temp)
   1968                 result.extra = extra
   1969                 return result
   1970 
   1971             def newmeth(self, start):
   1972                 return start + self.hour + self.second
   1973 
   1974         args = 4, 5, 6
   1975 
   1976         dt1 = self.theclass(*args)
   1977         dt2 = C(*args, **{'extra': 7})
   1978 
   1979         self.assertEqual(dt2.__class__, C)
   1980         self.assertEqual(dt2.theAnswer, 42)
   1981         self.assertEqual(dt2.extra, 7)
   1982         self.assertEqual(dt1.isoformat(), dt2.isoformat())
   1983         self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
   1984 
   1985     def test_backdoor_resistance(self):
   1986         # see TestDate.test_backdoor_resistance().
   1987         base = '2:59.0'
   1988         for hour_byte in ' ', '9', chr(24), '\xff':
   1989             self.assertRaises(TypeError, self.theclass,
   1990                                          hour_byte + base[1:])
   1991 
   1992 # A mixin for classes with a tzinfo= argument.  Subclasses must define
   1993 # theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
   1994 # must be legit (which is true for time and datetime).
   1995 class TZInfoBase:
   1996 
   1997     def test_argument_passing(self):
   1998         cls = self.theclass
   1999         # A datetime passes itself on, a time passes None.
   2000         class introspective(tzinfo):
   2001             def tzname(self, dt):    return dt and "real" or "none"
   2002             def utcoffset(self, dt):
   2003                 return timedelta(minutes = dt and 42 or -42)
   2004             dst = utcoffset
   2005 
   2006         obj = cls(1, 2, 3, tzinfo=introspective())
   2007 
   2008         expected = cls is time and "none" or "real"
   2009         self.assertEqual(obj.tzname(), expected)
   2010 
   2011         expected = timedelta(minutes=(cls is time and -42 or 42))
   2012         self.assertEqual(obj.utcoffset(), expected)
   2013         self.assertEqual(obj.dst(), expected)
   2014 
   2015     def test_bad_tzinfo_classes(self):
   2016         cls = self.theclass
   2017         self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
   2018 
   2019         class NiceTry(object):
   2020             def __init__(self): pass
   2021             def utcoffset(self, dt): pass
   2022         self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
   2023 
   2024         class BetterTry(tzinfo):
   2025             def __init__(self): pass
   2026             def utcoffset(self, dt): pass
   2027         b = BetterTry()
   2028         t = cls(1, 1, 1, tzinfo=b)
   2029         self.assertTrue(t.tzinfo is b)
   2030 
   2031     def test_utc_offset_out_of_bounds(self):
   2032         class Edgy(tzinfo):
   2033             def __init__(self, offset):
   2034                 self.offset = timedelta(minutes=offset)
   2035             def utcoffset(self, dt):
   2036                 return self.offset
   2037 
   2038         cls = self.theclass
   2039         for offset, legit in ((-1440, False),
   2040                               (-1439, True),
   2041                               (1439, True),
   2042                               (1440, False)):
   2043             if cls is time:
   2044                 t = cls(1, 2, 3, tzinfo=Edgy(offset))
   2045             elif cls is datetime:
   2046                 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
   2047             else:
   2048                 assert 0, "impossible"
   2049             if legit:
   2050                 aofs = abs(offset)
   2051                 h, m = divmod(aofs, 60)
   2052                 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
   2053                 if isinstance(t, datetime):
   2054                     t = t.timetz()
   2055                 self.assertEqual(str(t), "01:02:03" + tag)
   2056             else:
   2057                 self.assertRaises(ValueError, str, t)
   2058 
   2059     def test_tzinfo_classes(self):
   2060         cls = self.theclass
   2061         class C1(tzinfo):
   2062             def utcoffset(self, dt): return None
   2063             def dst(self, dt): return None
   2064             def tzname(self, dt): return None
   2065         for t in (cls(1, 1, 1),
   2066                   cls(1, 1, 1, tzinfo=None),
   2067                   cls(1, 1, 1, tzinfo=C1())):
   2068             self.assertTrue(t.utcoffset() is None)
   2069             self.assertTrue(t.dst() is None)
   2070             self.assertTrue(t.tzname() is None)
   2071 
   2072         class C3(tzinfo):
   2073             def utcoffset(self, dt): return timedelta(minutes=-1439)
   2074             def dst(self, dt): return timedelta(minutes=1439)
   2075             def tzname(self, dt): return "aname"
   2076         t = cls(1, 1, 1, tzinfo=C3())
   2077         self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
   2078         self.assertEqual(t.dst(), timedelta(minutes=1439))
   2079         self.assertEqual(t.tzname(), "aname")
   2080 
   2081         # Wrong types.
   2082         class C4(tzinfo):
   2083             def utcoffset(self, dt): return "aname"
   2084             def dst(self, dt): return 7
   2085             def tzname(self, dt): return 0
   2086         t = cls(1, 1, 1, tzinfo=C4())
   2087         self.assertRaises(TypeError, t.utcoffset)
   2088         self.assertRaises(TypeError, t.dst)
   2089         self.assertRaises(TypeError, t.tzname)
   2090 
   2091         # Offset out of range.
   2092         class C6(tzinfo):
   2093             def utcoffset(self, dt): return timedelta(hours=-24)
   2094             def dst(self, dt): return timedelta(hours=24)
   2095         t = cls(1, 1, 1, tzinfo=C6())
   2096         self.assertRaises(ValueError, t.utcoffset)
   2097         self.assertRaises(ValueError, t.dst)
   2098 
   2099         # Not a whole number of minutes.
   2100         class C7(tzinfo):
   2101             def utcoffset(self, dt): return timedelta(seconds=61)
   2102             def dst(self, dt): return timedelta(microseconds=-81)
   2103         t = cls(1, 1, 1, tzinfo=C7())
   2104         self.assertRaises(ValueError, t.utcoffset)
   2105         self.assertRaises(ValueError, t.dst)
   2106 
   2107     def test_aware_compare(self):
   2108         cls = self.theclass
   2109 
   2110         # Ensure that utcoffset() gets ignored if the comparands have
   2111         # the same tzinfo member.
   2112         class OperandDependentOffset(tzinfo):
   2113             def utcoffset(self, t):
   2114                 if t.minute < 10:
   2115                     # d0 and d1 equal after adjustment
   2116                     return timedelta(minutes=t.minute)
   2117                 else:
   2118                     # d2 off in the weeds
   2119                     return timedelta(minutes=59)
   2120 
   2121         base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
   2122         d0 = base.replace(minute=3)
   2123         d1 = base.replace(minute=9)
   2124         d2 = base.replace(minute=11)
   2125         for x in d0, d1, d2:
   2126             for y in d0, d1, d2:
   2127                 got = cmp(x, y)
   2128                 expected = cmp(x.minute, y.minute)
   2129                 self.assertEqual(got, expected)
   2130 
   2131         # However, if they're different members, uctoffset is not ignored.
   2132         # Note that a time can't actually have an operand-depedent offset,
   2133         # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
   2134         # so skip this test for time.
   2135         if cls is not time:
   2136             d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
   2137             d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
   2138             d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
   2139             for x in d0, d1, d2:
   2140                 for y in d0, d1, d2:
   2141                     got = cmp(x, y)
   2142                     if (x is d0 or x is d1) and (y is d0 or y is d1):
   2143                         expected = 0
   2144                     elif x is y is d2:
   2145                         expected = 0
   2146                     elif x is d2:
   2147                         expected = -1
   2148                     else:
   2149                         assert y is d2
   2150                         expected = 1
   2151                     self.assertEqual(got, expected)
   2152 
   2153 
   2154 # Testing time objects with a non-None tzinfo.
   2155 class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
   2156     theclass = time
   2157 
   2158     def test_empty(self):
   2159         t = self.theclass()
   2160         self.assertEqual(t.hour, 0)
   2161         self.assertEqual(t.minute, 0)
   2162         self.assertEqual(t.second, 0)
   2163         self.assertEqual(t.microsecond, 0)
   2164         self.assertTrue(t.tzinfo is None)
   2165 
   2166     def test_zones(self):
   2167         est = FixedOffset(-300, "EST", 1)
   2168         utc = FixedOffset(0, "UTC", -2)
   2169         met = FixedOffset(60, "MET", 3)
   2170         t1 = time( 7, 47, tzinfo=est)
   2171         t2 = time(12, 47, tzinfo=utc)
   2172         t3 = time(13, 47, tzinfo=met)
   2173         t4 = time(microsecond=40)
   2174         t5 = time(microsecond=40, tzinfo=utc)
   2175 
   2176         self.assertEqual(t1.tzinfo, est)
   2177         self.assertEqual(t2.tzinfo, utc)
   2178         self.assertEqual(t3.tzinfo, met)
   2179         self.assertTrue(t4.tzinfo is None)
   2180         self.assertEqual(t5.tzinfo, utc)
   2181 
   2182         self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
   2183         self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
   2184         self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
   2185         self.assertTrue(t4.utcoffset() is None)
   2186         self.assertRaises(TypeError, t1.utcoffset, "no args")
   2187 
   2188         self.assertEqual(t1.tzname(), "EST")
   2189         self.assertEqual(t2.tzname(), "UTC")
   2190         self.assertEqual(t3.tzname(), "MET")
   2191         self.assertTrue(t4.tzname() is None)
   2192         self.assertRaises(TypeError, t1.tzname, "no args")
   2193 
   2194         self.assertEqual(t1.dst(), timedelta(minutes=1))
   2195         self.assertEqual(t2.dst(), timedelta(minutes=-2))
   2196         self.assertEqual(t3.dst(), timedelta(minutes=3))
   2197         self.assertTrue(t4.dst() is None)
   2198         self.assertRaises(TypeError, t1.dst, "no args")
   2199 
   2200         self.assertEqual(hash(t1), hash(t2))
   2201         self.assertEqual(hash(t1), hash(t3))
   2202         self.assertEqual(hash(t2), hash(t3))
   2203 
   2204         self.assertEqual(t1, t2)
   2205         self.assertEqual(t1, t3)
   2206         self.assertEqual(t2, t3)
   2207         self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
   2208         self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
   2209         self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
   2210 
   2211         self.assertEqual(str(t1), "07:47:00-05:00")
   2212         self.assertEqual(str(t2), "12:47:00+00:00")
   2213         self.assertEqual(str(t3), "13:47:00+01:00")
   2214         self.assertEqual(str(t4), "00:00:00.000040")
   2215         self.assertEqual(str(t5), "00:00:00.000040+00:00")
   2216 
   2217         self.assertEqual(t1.isoformat(), "07:47:00-05:00")
   2218         self.assertEqual(t2.isoformat(), "12:47:00+00:00")
   2219         self.assertEqual(t3.isoformat(), "13:47:00+01:00")
   2220         self.assertEqual(t4.isoformat(), "00:00:00.000040")
   2221         self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
   2222 
   2223         d = 'datetime.time'
   2224         self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
   2225         self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
   2226         self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
   2227         self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
   2228         self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
   2229 
   2230         self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
   2231                                      "07:47:00 %Z=EST %z=-0500")
   2232         self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
   2233         self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
   2234 
   2235         yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
   2236         t1 = time(23, 59, tzinfo=yuck)
   2237         self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
   2238                                      "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
   2239 
   2240         # Check that an invalid tzname result raises an exception.
   2241         class Badtzname(tzinfo):
   2242             def tzname(self, dt): return 42
   2243         t = time(2, 3, 4, tzinfo=Badtzname())
   2244         self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
   2245         self.assertRaises(TypeError, t.strftime, "%Z")
   2246 
   2247     def test_hash_edge_cases(self):
   2248         # Offsets that overflow a basic time.
   2249         t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
   2250         t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
   2251         self.assertEqual(hash(t1), hash(t2))
   2252 
   2253         t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
   2254         t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
   2255         self.assertEqual(hash(t1), hash(t2))
   2256 
   2257     def test_pickling(self):
   2258         # Try one without a tzinfo.
   2259         args = 20, 59, 16, 64**2
   2260         orig = self.theclass(*args)
   2261         for pickler, unpickler, proto in pickle_choices:
   2262             green = pickler.dumps(orig, proto)
   2263             derived = unpickler.loads(green)
   2264             self.assertEqual(orig, derived)
   2265 
   2266         # Try one with a tzinfo.
   2267         tinfo = PicklableFixedOffset(-300, 'cookie')
   2268         orig = self.theclass(5, 6, 7, tzinfo=tinfo)
   2269         for pickler, unpickler, proto in pickle_choices:
   2270             green = pickler.dumps(orig, proto)
   2271             derived = unpickler.loads(green)
   2272             self.assertEqual(orig, derived)
   2273             self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
   2274             self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
   2275             self.assertEqual(derived.tzname(), 'cookie')
   2276 
   2277     def test_more_bool(self):
   2278         # Test cases with non-None tzinfo.
   2279         cls = self.theclass
   2280 
   2281         t = cls(0, tzinfo=FixedOffset(-300, ""))
   2282         self.assertTrue(t)
   2283 
   2284         t = cls(5, tzinfo=FixedOffset(-300, ""))
   2285         self.assertTrue(t)
   2286 
   2287         t = cls(5, tzinfo=FixedOffset(300, ""))
   2288         self.assertTrue(not t)
   2289 
   2290         t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
   2291         self.assertTrue(not t)
   2292 
   2293         # Mostly ensuring this doesn't overflow internally.
   2294         t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
   2295         self.assertTrue(t)
   2296 
   2297         # But this should yield a value error -- the utcoffset is bogus.
   2298         t = cls(0, tzinfo=FixedOffset(24*60, ""))
   2299         self.assertRaises(ValueError, lambda: bool(t))
   2300 
   2301         # Likewise.
   2302         t = cls(0, tzinfo=FixedOffset(-24*60, ""))
   2303         self.assertRaises(ValueError, lambda: bool(t))
   2304 
   2305     def test_replace(self):
   2306         cls = self.theclass
   2307         z100 = FixedOffset(100, "+100")
   2308         zm200 = FixedOffset(timedelta(minutes=-200), "-200")
   2309         args = [1, 2, 3, 4, z100]
   2310         base = cls(*args)
   2311         self.assertEqual(base, base.replace())
   2312 
   2313         i = 0
   2314         for name, newval in (("hour", 5),
   2315                              ("minute", 6),
   2316                              ("second", 7),
   2317                              ("microsecond", 8),
   2318                              ("tzinfo", zm200)):
   2319             newargs = args[:]
   2320             newargs[i] = newval
   2321             expected = cls(*newargs)
   2322             got = base.replace(**{name: newval})
   2323             self.assertEqual(expected, got)
   2324             i += 1
   2325 
   2326         # Ensure we can get rid of a tzinfo.
   2327         self.assertEqual(base.tzname(), "+100")
   2328         base2 = base.replace(tzinfo=None)
   2329         self.assertTrue(base2.tzinfo is None)
   2330         self.assertTrue(base2.tzname() is None)
   2331 
   2332         # Ensure we can add one.
   2333         base3 = base2.replace(tzinfo=z100)
   2334         self.assertEqual(base, base3)
   2335         self.assertTrue(base.tzinfo is base3.tzinfo)
   2336 
   2337         # Out of bounds.
   2338         base = cls(1)
   2339         self.assertRaises(ValueError, base.replace, hour=24)
   2340         self.assertRaises(ValueError, base.replace, minute=-1)
   2341         self.assertRaises(ValueError, base.replace, second=100)
   2342         self.assertRaises(ValueError, base.replace, microsecond=1000000)
   2343 
   2344     def test_mixed_compare(self):
   2345         t1 = time(1, 2, 3)
   2346         t2 = time(1, 2, 3)
   2347         self.assertEqual(t1, t2)
   2348         t2 = t2.replace(tzinfo=None)
   2349         self.assertEqual(t1, t2)
   2350         t2 = t2.replace(tzinfo=FixedOffset(None, ""))
   2351         self.assertEqual(t1, t2)
   2352         t2 = t2.replace(tzinfo=FixedOffset(0, ""))
   2353         self.assertRaises(TypeError, lambda: t1 == t2)
   2354 
   2355         # In time w/ identical tzinfo objects, utcoffset is ignored.
   2356         class Varies(tzinfo):
   2357             def __init__(self):
   2358                 self.offset = timedelta(minutes=22)
   2359             def utcoffset(self, t):
   2360                 self.offset += timedelta(minutes=1)
   2361                 return self.offset
   2362 
   2363         v = Varies()
   2364         t1 = t2.replace(tzinfo=v)
   2365         t2 = t2.replace(tzinfo=v)
   2366         self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
   2367         self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
   2368         self.assertEqual(t1, t2)
   2369 
   2370         # But if they're not identical, it isn't ignored.
   2371         t2 = t2.replace(tzinfo=Varies())
   2372         self.assertTrue(t1 < t2)  # t1's offset counter still going up
   2373 
   2374     def test_subclass_timetz(self):
   2375 
   2376         class C(self.theclass):
   2377             theAnswer = 42
   2378 
   2379             def __new__(cls, *args, **kws):
   2380                 temp = kws.copy()
   2381                 extra = temp.pop('extra')
   2382                 result = self.theclass.__new__(cls, *args, **temp)
   2383                 result.extra = extra
   2384                 return result
   2385 
   2386             def newmeth(self, start):
   2387                 return start + self.hour + self.second
   2388 
   2389         args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
   2390 
   2391         dt1 = self.theclass(*args)
   2392         dt2 = C(*args, **{'extra': 7})
   2393 
   2394         self.assertEqual(dt2.__class__, C)
   2395         self.assertEqual(dt2.theAnswer, 42)
   2396         self.assertEqual(dt2.extra, 7)
   2397         self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
   2398         self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
   2399 
   2400 
   2401 # Testing datetime objects with a non-None tzinfo.
   2402 
   2403 class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
   2404     theclass = datetime
   2405 
   2406     def test_trivial(self):
   2407         dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
   2408         self.assertEqual(dt.year, 1)
   2409         self.assertEqual(dt.month, 2)
   2410         self.assertEqual(dt.day, 3)
   2411         self.assertEqual(dt.hour, 4)
   2412         self.assertEqual(dt.minute, 5)
   2413         self.assertEqual(dt.second, 6)
   2414         self.assertEqual(dt.microsecond, 7)
   2415         self.assertEqual(dt.tzinfo, None)
   2416 
   2417     def test_even_more_compare(self):
   2418         # The test_compare() and test_more_compare() inherited from TestDate
   2419         # and TestDateTime covered non-tzinfo cases.
   2420 
   2421         # Smallest possible after UTC adjustment.
   2422         t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
   2423         # Largest possible after UTC adjustment.
   2424         t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
   2425                            tzinfo=FixedOffset(-1439, ""))
   2426 
   2427         # Make sure those compare correctly, and w/o overflow.
   2428         self.assertTrue(t1 < t2)
   2429         self.assertTrue(t1 != t2)
   2430         self.assertTrue(t2 > t1)
   2431 
   2432         self.assertTrue(t1 == t1)
   2433         self.assertTrue(t2 == t2)
   2434 
   2435         # Equal afer adjustment.
   2436         t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
   2437         t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
   2438         self.assertEqual(t1, t2)
   2439 
   2440         # Change t1 not to subtract a minute, and t1 should be larger.
   2441         t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
   2442         self.assertTrue(t1 > t2)
   2443 
   2444         # Change t1 to subtract 2 minutes, and t1 should be smaller.
   2445         t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
   2446         self.assertTrue(t1 < t2)
   2447 
   2448         # Back to the original t1, but make seconds resolve it.
   2449         t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
   2450                            second=1)
   2451         self.assertTrue(t1 > t2)
   2452 
   2453         # Likewise, but make microseconds resolve it.
   2454         t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
   2455                            microsecond=1)
   2456         self.assertTrue(t1 > t2)
   2457 
   2458         # Make t2 naive and it should fail.
   2459         t2 = self.theclass.min
   2460         self.assertRaises(TypeError, lambda: t1 == t2)
   2461         self.assertEqual(t2, t2)
   2462 
   2463         # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
   2464         class Naive(tzinfo):
   2465             def utcoffset(self, dt): return None
   2466         t2 = self.theclass(5, 6, 7, tzinfo=Naive())
   2467         self.assertRaises(TypeError, lambda: t1 == t2)
   2468         self.assertEqual(t2, t2)
   2469 
   2470         # OTOH, it's OK to compare two of these mixing the two ways of being
   2471         # naive.
   2472         t1 = self.theclass(5, 6, 7)
   2473         self.assertEqual(t1, t2)
   2474 
   2475         # Try a bogus uctoffset.
   2476         class Bogus(tzinfo):
   2477             def utcoffset(self, dt):
   2478                 return timedelta(minutes=1440) # out of bounds
   2479         t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
   2480         t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
   2481         self.assertRaises(ValueError, lambda: t1 == t2)
   2482 
   2483     def test_pickling(self):
   2484         # Try one without a tzinfo.
   2485         args = 6, 7, 23, 20, 59, 1, 64**2
   2486         orig = self.theclass(*args)
   2487         for pickler, unpickler, proto in pickle_choices:
   2488             green = pickler.dumps(orig, proto)
   2489             derived = unpickler.loads(green)
   2490             self.assertEqual(orig, derived)
   2491 
   2492         # Try one with a tzinfo.
   2493         tinfo = PicklableFixedOffset(-300, 'cookie')
   2494         orig = self.theclass(*args, **{'tzinfo': tinfo})
   2495         derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
   2496         for pickler, unpickler, proto in pickle_choices:
   2497             green = pickler.dumps(orig, proto)
   2498             derived = unpickler.loads(green)
   2499             self.assertEqual(orig, derived)
   2500             self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
   2501             self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
   2502             self.assertEqual(derived.tzname(), 'cookie')
   2503 
   2504     def test_extreme_hashes(self):
   2505         # If an attempt is made to hash these via subtracting the offset
   2506         # then hashing a datetime object, OverflowError results.  The
   2507         # Python implementation used to blow up here.
   2508         t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
   2509         hash(t)
   2510         t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
   2511                           tzinfo=FixedOffset(-1439, ""))
   2512         hash(t)
   2513 
   2514         # OTOH, an OOB offset should blow up.
   2515         t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
   2516         self.assertRaises(ValueError, hash, t)
   2517 
   2518     def test_zones(self):
   2519         est = FixedOffset(-300, "EST")
   2520         utc = FixedOffset(0, "UTC")
   2521         met = FixedOffset(60, "MET")
   2522         t1 = datetime(2002, 3, 19,  7, 47, tzinfo=est)
   2523         t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
   2524         t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
   2525         self.assertEqual(t1.tzinfo, est)
   2526         self.assertEqual(t2.tzinfo, utc)
   2527         self.assertEqual(t3.tzinfo, met)
   2528         self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
   2529         self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
   2530         self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
   2531         self.assertEqual(t1.tzname(), "EST")
   2532         self.assertEqual(t2.tzname(), "UTC")
   2533         self.assertEqual(t3.tzname(), "MET")
   2534         self.assertEqual(hash(t1), hash(t2))
   2535         self.assertEqual(hash(t1), hash(t3))
   2536         self.assertEqual(hash(t2), hash(t3))
   2537         self.assertEqual(t1, t2)
   2538         self.assertEqual(t1, t3)
   2539         self.assertEqual(t2, t3)
   2540         self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
   2541         self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
   2542         self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
   2543         d = 'datetime.datetime(2002, 3, 19, '
   2544         self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
   2545         self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
   2546         self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
   2547 
   2548     def test_combine(self):
   2549         met = FixedOffset(60, "MET")
   2550         d = date(2002, 3, 4)
   2551         tz = time(18, 45, 3, 1234, tzinfo=met)
   2552         dt = datetime.combine(d, tz)
   2553         self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
   2554                                         tzinfo=met))
   2555 
   2556     def test_extract(self):
   2557         met = FixedOffset(60, "MET")
   2558         dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
   2559         self.assertEqual(dt.date(), date(2002, 3, 4))
   2560         self.assertEqual(dt.time(), time(18, 45, 3, 1234))
   2561         self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
   2562 
   2563     def test_tz_aware_arithmetic(self):
   2564         import random
   2565 
   2566         now = self.theclass.now()
   2567         tz55 = FixedOffset(-330, "west 5:30")
   2568         timeaware = now.time().replace(tzinfo=tz55)
   2569         nowaware = self.theclass.combine(now.date(), timeaware)
   2570         self.assertTrue(nowaware.tzinfo is tz55)
   2571         self.assertEqual(nowaware.timetz(), timeaware)
   2572 
   2573         # Can't mix aware and non-aware.
   2574         self.assertRaises(TypeError, lambda: now - nowaware)
   2575         self.assertRaises(TypeError, lambda: nowaware - now)
   2576 
   2577         # And adding datetime's doesn't make sense, aware or not.
   2578         self.assertRaises(TypeError, lambda: now + nowaware)
   2579         self.assertRaises(TypeError, lambda: nowaware + now)
   2580         self.assertRaises(TypeError, lambda: nowaware + nowaware)
   2581 
   2582         # Subtracting should yield 0.
   2583         self.assertEqual(now - now, timedelta(0))
   2584         self.assertEqual(nowaware - nowaware, timedelta(0))
   2585 
   2586         # Adding a delta should preserve tzinfo.
   2587         delta = timedelta(weeks=1, minutes=12, microseconds=5678)
   2588         nowawareplus = nowaware + delta
   2589         self.assertTrue(nowaware.tzinfo is tz55)
   2590         nowawareplus2 = delta + nowaware
   2591         self.assertTrue(nowawareplus2.tzinfo is tz55)
   2592         self.assertEqual(nowawareplus, nowawareplus2)
   2593 
   2594         # that - delta should be what we started with, and that - what we
   2595         # started with should be delta.
   2596         diff = nowawareplus - delta
   2597         self.assertTrue(diff.tzinfo is tz55)
   2598         self.assertEqual(nowaware, diff)
   2599         self.assertRaises(TypeError, lambda: delta - nowawareplus)
   2600         self.assertEqual(nowawareplus - nowaware, delta)
   2601 
   2602         # Make up a random timezone.
   2603         tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
   2604         # Attach it to nowawareplus.
   2605         nowawareplus = nowawareplus.replace(tzinfo=tzr)
   2606         self.assertTrue(nowawareplus.tzinfo is tzr)
   2607         # Make sure the difference takes the timezone adjustments into account.
   2608         got = nowaware - nowawareplus
   2609         # Expected:  (nowaware base - nowaware offset) -
   2610         #            (nowawareplus base - nowawareplus offset) =
   2611         #            (nowaware base - nowawareplus base) +
   2612         #            (nowawareplus offset - nowaware offset) =
   2613         #            -delta + nowawareplus offset - nowaware offset
   2614         expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
   2615         self.assertEqual(got, expected)
   2616 
   2617         # Try max possible difference.
   2618         min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
   2619         max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
   2620                             tzinfo=FixedOffset(-1439, "max"))
   2621         maxdiff = max - min
   2622         self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
   2623                                   timedelta(minutes=2*1439))
   2624 
   2625     def test_tzinfo_now(self):
   2626         meth = self.theclass.now
   2627         # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
   2628         base = meth()
   2629         # Try with and without naming the keyword.
   2630         off42 = FixedOffset(42, "42")
   2631         another = meth(off42)
   2632         again = meth(tz=off42)
   2633         self.assertTrue(another.tzinfo is again.tzinfo)
   2634         self.assertEqual(another.utcoffset(), timedelta(minutes=42))
   2635         # Bad argument with and w/o naming the keyword.
   2636         self.assertRaises(TypeError, meth, 16)
   2637         self.assertRaises(TypeError, meth, tzinfo=16)
   2638         # Bad keyword name.
   2639         self.assertRaises(TypeError, meth, tinfo=off42)
   2640         # Too many args.
   2641         self.assertRaises(TypeError, meth, off42, off42)
   2642 
   2643         # We don't know which time zone we're in, and don't have a tzinfo
   2644         # class to represent it, so seeing whether a tz argument actually
   2645         # does a conversion is tricky.
   2646         weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
   2647         utc = FixedOffset(0, "utc", 0)
   2648         for dummy in range(3):
   2649             now = datetime.now(weirdtz)
   2650             self.assertTrue(now.tzinfo is weirdtz)
   2651             utcnow = datetime.utcnow().replace(tzinfo=utc)
   2652             now2 = utcnow.astimezone(weirdtz)
   2653             if abs(now - now2) < timedelta(seconds=30):
   2654                 break
   2655             # Else the code is broken, or more than 30 seconds passed between
   2656             # calls; assuming the latter, just try again.
   2657         else:
   2658             # Three strikes and we're out.
   2659             self.fail("utcnow(), now(tz), or astimezone() may be broken")
   2660 
   2661     def test_tzinfo_fromtimestamp(self):
   2662         import time
   2663         meth = self.theclass.fromtimestamp
   2664         ts = time.time()
   2665         # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
   2666         base = meth(ts)
   2667         # Try with and without naming the keyword.
   2668         off42 = FixedOffset(42, "42")
   2669         another = meth(ts, off42)
   2670         again = meth(ts, tz=off42)
   2671         self.assertTrue(another.tzinfo is again.tzinfo)
   2672         self.assertEqual(another.utcoffset(), timedelta(minutes=42))
   2673         # Bad argument with and w/o naming the keyword.
   2674         self.assertRaises(TypeError, meth, ts, 16)
   2675         self.assertRaises(TypeError, meth, ts, tzinfo=16)
   2676         # Bad keyword name.
   2677         self.assertRaises(TypeError, meth, ts, tinfo=off42)
   2678         # Too many args.
   2679         self.assertRaises(TypeError, meth, ts, off42, off42)
   2680         # Too few args.
   2681         self.assertRaises(TypeError, meth)
   2682 
   2683         # Try to make sure tz= actually does some conversion.
   2684         timestamp = 1000000000
   2685         utcdatetime = datetime.utcfromtimestamp(timestamp)
   2686         # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
   2687         # But on some flavor of Mac, it's nowhere near that.  So we can't have
   2688         # any idea here what time that actually is, we can only test that
   2689         # relative changes match.
   2690         utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
   2691         tz = FixedOffset(utcoffset, "tz", 0)
   2692         expected = utcdatetime + utcoffset
   2693         got = datetime.fromtimestamp(timestamp, tz)
   2694         self.assertEqual(expected, got.replace(tzinfo=None))
   2695 
   2696     def test_tzinfo_utcnow(self):
   2697         meth = self.theclass.utcnow
   2698         # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
   2699         base = meth()
   2700         # Try with and without naming the keyword; for whatever reason,
   2701         # utcnow() doesn't accept a tzinfo argument.
   2702         off42 = FixedOffset(42, "42")
   2703         self.assertRaises(TypeError, meth, off42)
   2704         self.assertRaises(TypeError, meth, tzinfo=off42)
   2705 
   2706     def test_tzinfo_utcfromtimestamp(self):
   2707         import time
   2708         meth = self.theclass.utcfromtimestamp
   2709         ts = time.time()
   2710         # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
   2711         base = meth(ts)
   2712         # Try with and without naming the keyword; for whatever reason,
   2713         # utcfromtimestamp() doesn't accept a tzinfo argument.
   2714         off42 = FixedOffset(42, "42")
   2715         self.assertRaises(TypeError, meth, ts, off42)
   2716         self.assertRaises(TypeError, meth, ts, tzinfo=off42)
   2717 
   2718     def test_tzinfo_timetuple(self):
   2719         # TestDateTime tested most of this.  datetime adds a twist to the
   2720         # DST flag.
   2721         class DST(tzinfo):
   2722             def __init__(self, dstvalue):
   2723                 if isinstance(dstvalue, int):
   2724                     dstvalue = timedelta(minutes=dstvalue)
   2725                 self.dstvalue = dstvalue
   2726             def dst(self, dt):
   2727                 return self.dstvalue
   2728 
   2729         cls = self.theclass
   2730         for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
   2731             d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
   2732             t = d.timetuple()
   2733             self.assertEqual(1, t.tm_year)
   2734             self.assertEqual(1, t.tm_mon)
   2735             self.assertEqual(1, t.tm_mday)
   2736             self.assertEqual(10, t.tm_hour)
   2737             self.assertEqual(20, t.tm_min)
   2738             self.assertEqual(30, t.tm_sec)
   2739             self.assertEqual(0, t.tm_wday)
   2740             self.assertEqual(1, t.tm_yday)
   2741             self.assertEqual(flag, t.tm_isdst)
   2742 
   2743         # dst() returns wrong type.
   2744         self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
   2745 
   2746         # dst() at the edge.
   2747         self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
   2748         self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
   2749 
   2750         # dst() out of range.
   2751         self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
   2752         self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
   2753 
   2754     def test_utctimetuple(self):
   2755         class DST(tzinfo):
   2756             def __init__(self, dstvalue):
   2757                 if isinstance(dstvalue, int):
   2758                     dstvalue = timedelta(minutes=dstvalue)
   2759                 self.dstvalue = dstvalue
   2760             def dst(self, dt):
   2761                 return self.dstvalue
   2762 
   2763         cls = self.theclass
   2764         # This can't work:  DST didn't implement utcoffset.
   2765         self.assertRaises(NotImplementedError,
   2766                           cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
   2767 
   2768         class UOFS(DST):
   2769             def __init__(self, uofs, dofs=None):
   2770                 DST.__init__(self, dofs)
   2771                 self.uofs = timedelta(minutes=uofs)
   2772             def utcoffset(self, dt):
   2773                 return self.uofs
   2774 
   2775         # Ensure tm_isdst is 0 regardless of what dst() says:  DST is never
   2776         # in effect for a UTC time.
   2777         for dstvalue in -33, 33, 0, None:
   2778             d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
   2779             t = d.utctimetuple()
   2780             self.assertEqual(d.year, t.tm_year)
   2781             self.assertEqual(d.month, t.tm_mon)
   2782             self.assertEqual(d.day, t.tm_mday)
   2783             self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
   2784             self.assertEqual(13, t.tm_min)
   2785             self.assertEqual(d.second, t.tm_sec)
   2786             self.assertEqual(d.weekday(), t.tm_wday)
   2787             self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
   2788                              t.tm_yday)
   2789             self.assertEqual(0, t.tm_isdst)
   2790 
   2791         # At the edges, UTC adjustment can normalize into years out-of-range
   2792         # for a datetime object.  Ensure that a correct timetuple is
   2793         # created anyway.
   2794         tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
   2795         # That goes back 1 minute less than a full day.
   2796         t = tiny.utctimetuple()
   2797         self.assertEqual(t.tm_year, MINYEAR-1)
   2798         self.assertEqual(t.tm_mon, 12)
   2799         self.assertEqual(t.tm_mday, 31)
   2800         self.assertEqual(t.tm_hour, 0)
   2801         self.assertEqual(t.tm_min, 1)
   2802         self.assertEqual(t.tm_sec, 37)
   2803         self.assertEqual(t.tm_yday, 366)    # "year 0" is a leap year
   2804         self.assertEqual(t.tm_isdst, 0)
   2805 
   2806         huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
   2807         # That goes forward 1 minute less than a full day.
   2808         t = huge.utctimetuple()
   2809         self.assertEqual(t.tm_year, MAXYEAR+1)
   2810         self.assertEqual(t.tm_mon, 1)
   2811         self.assertEqual(t.tm_mday, 1)
   2812         self.assertEqual(t.tm_hour, 23)
   2813         self.assertEqual(t.tm_min, 58)
   2814         self.assertEqual(t.tm_sec, 37)
   2815         self.assertEqual(t.tm_yday, 1)
   2816         self.assertEqual(t.tm_isdst, 0)
   2817 
   2818     def test_tzinfo_isoformat(self):
   2819         zero = FixedOffset(0, "+00:00")
   2820         plus = FixedOffset(220, "+03:40")
   2821         minus = FixedOffset(-231, "-03:51")
   2822         unknown = FixedOffset(None, "")
   2823 
   2824         cls = self.theclass
   2825         datestr = '0001-02-03'
   2826         for ofs in None, zero, plus, minus, unknown:
   2827             for us in 0, 987001:
   2828                 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
   2829                 timestr = '04:05:59' + (us and '.987001' or '')
   2830                 ofsstr = ofs is not None and d.tzname() or ''
   2831                 tailstr = timestr + ofsstr
   2832                 iso = d.isoformat()
   2833                 self.assertEqual(iso, datestr + 'T' + tailstr)
   2834                 self.assertEqual(iso, d.isoformat('T'))
   2835                 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
   2836                 self.assertEqual(str(d), datestr + ' ' + tailstr)
   2837 
   2838     def test_replace(self):
   2839         cls = self.theclass
   2840         z100 = FixedOffset(100, "+100")
   2841         zm200 = FixedOffset(timedelta(minutes=-200), "-200")
   2842         args = [1, 2, 3, 4, 5, 6, 7, z100]
   2843         base = cls(*args)
   2844         self.assertEqual(base, base.replace())
   2845 
   2846         i = 0
   2847         for name, newval in (("year", 2),
   2848                              ("month", 3),
   2849                              ("day", 4),
   2850                              ("hour", 5),
   2851                              ("minute", 6),
   2852                              ("second", 7),
   2853                              ("microsecond", 8),
   2854                              ("tzinfo", zm200)):
   2855             newargs = args[:]
   2856             newargs[i] = newval
   2857             expected = cls(*newargs)
   2858             got = base.replace(**{name: newval})
   2859             self.assertEqual(expected, got)
   2860             i += 1
   2861 
   2862         # Ensure we can get rid of a tzinfo.
   2863         self.assertEqual(base.tzname(), "+100")
   2864         base2 = base.replace(tzinfo=None)
   2865         self.assertTrue(base2.tzinfo is None)
   2866         self.assertTrue(base2.tzname() is None)
   2867 
   2868         # Ensure we can add one.
   2869         base3 = base2.replace(tzinfo=z100)
   2870         self.assertEqual(base, base3)
   2871         self.assertTrue(base.tzinfo is base3.tzinfo)
   2872 
   2873         # Out of bounds.
   2874         base = cls(2000, 2, 29)
   2875         self.assertRaises(ValueError, base.replace, year=2001)
   2876 
   2877     def test_more_astimezone(self):
   2878         # The inherited test_astimezone covered some trivial and error cases.
   2879         fnone = FixedOffset(None, "None")
   2880         f44m = FixedOffset(44, "44")
   2881         fm5h = FixedOffset(-timedelta(hours=5), "m300")
   2882 
   2883         dt = self.theclass.now(tz=f44m)
   2884         self.assertTrue(dt.tzinfo is f44m)
   2885         # Replacing with degenerate tzinfo raises an exception.
   2886         self.assertRaises(ValueError, dt.astimezone, fnone)
   2887         # Ditto with None tz.
   2888         self.assertRaises(TypeError, dt.astimezone, None)
   2889         # Replacing with same tzinfo makes no change.
   2890         x = dt.astimezone(dt.tzinfo)
   2891         self.assertTrue(x.tzinfo is f44m)
   2892         self.assertEqual(x.date(), dt.date())
   2893         self.assertEqual(x.time(), dt.time())
   2894 
   2895         # Replacing with different tzinfo does adjust.
   2896         got = dt.astimezone(fm5h)
   2897         self.assertTrue(got.tzinfo is fm5h)
   2898         self.assertEqual(got.utcoffset(), timedelta(hours=-5))
   2899         expected = dt - dt.utcoffset()  # in effect, convert to UTC
   2900         expected += fm5h.utcoffset(dt)  # and from there to local time
   2901         expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
   2902         self.assertEqual(got.date(), expected.date())
   2903         self.assertEqual(got.time(), expected.time())
   2904         self.assertEqual(got.timetz(), expected.timetz())
   2905         self.assertTrue(got.tzinfo is expected.tzinfo)
   2906         self.assertEqual(got, expected)
   2907 
   2908     def test_aware_subtract(self):
   2909         cls = self.theclass
   2910 
   2911         # Ensure that utcoffset() is ignored when the operands have the
   2912         # same tzinfo member.
   2913         class OperandDependentOffset(tzinfo):
   2914             def utcoffset(self, t):
   2915                 if t.minute < 10:
   2916                     # d0 and d1 equal after adjustment
   2917                     return timedelta(minutes=t.minute)
   2918                 else:
   2919                     # d2 off in the weeds
   2920                     return timedelta(minutes=59)
   2921 
   2922         base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
   2923         d0 = base.replace(minute=3)
   2924         d1 = base.replace(minute=9)
   2925         d2 = base.replace(minute=11)
   2926         for x in d0, d1, d2:
   2927             for y in d0, d1, d2:
   2928                 got = x - y
   2929                 expected = timedelta(minutes=x.minute - y.minute)
   2930                 self.assertEqual(got, expected)
   2931 
   2932         # OTOH, if the tzinfo members are distinct, utcoffsets aren't
   2933         # ignored.
   2934         base = cls(8, 9, 10, 11, 12, 13, 14)
   2935         d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
   2936         d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
   2937         d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
   2938         for x in d0, d1, d2:
   2939             for y in d0, d1, d2:
   2940                 got = x - y
   2941                 if (x is d0 or x is d1) and (y is d0 or y is d1):
   2942                     expected = timedelta(0)
   2943                 elif x is y is d2:
   2944                     expected = timedelta(0)
   2945                 elif x is d2:
   2946                     expected = timedelta(minutes=(11-59)-0)
   2947                 else:
   2948                     assert y is d2
   2949                     expected = timedelta(minutes=0-(11-59))
   2950                 self.assertEqual(got, expected)
   2951 
   2952     def test_mixed_compare(self):
   2953         t1 = datetime(1, 2, 3, 4, 5, 6, 7)
   2954         t2 = datetime(1, 2, 3, 4, 5, 6, 7)
   2955         self.assertEqual(t1, t2)
   2956         t2 = t2.replace(tzinfo=None)
   2957         self.assertEqual(t1, t2)
   2958         t2 = t2.replace(tzinfo=FixedOffset(None, ""))
   2959         self.assertEqual(t1, t2)
   2960         t2 = t2.replace(tzinfo=FixedOffset(0, ""))
   2961         self.assertRaises(TypeError, lambda: t1 == t2)
   2962 
   2963         # In datetime w/ identical tzinfo objects, utcoffset is ignored.
   2964         class Varies(tzinfo):
   2965             def __init__(self):
   2966                 self.offset = timedelta(minutes=22)
   2967             def utcoffset(self, t):
   2968                 self.offset += timedelta(minutes=1)
   2969                 return self.offset
   2970 
   2971         v = Varies()
   2972         t1 = t2.replace(tzinfo=v)
   2973         t2 = t2.replace(tzinfo=v)
   2974         self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
   2975         self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
   2976         self.assertEqual(t1, t2)
   2977 
   2978         # But if they're not identical, it isn't ignored.
   2979         t2 = t2.replace(tzinfo=Varies())
   2980         self.assertTrue(t1 < t2)  # t1's offset counter still going up
   2981 
   2982     def test_subclass_datetimetz(self):
   2983 
   2984         class C(self.theclass):
   2985             theAnswer = 42
   2986 
   2987             def __new__(cls, *args, **kws):
   2988                 temp = kws.copy()
   2989                 extra = temp.pop('extra')
   2990                 result = self.theclass.__new__(cls, *args, **temp)
   2991                 result.extra = extra
   2992                 return result
   2993 
   2994             def newmeth(self, start):
   2995                 return start + self.hour + self.year
   2996 
   2997         args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
   2998 
   2999         dt1 = self.theclass(*args)
   3000         dt2 = C(*args, **{'extra': 7})
   3001 
   3002         self.assertEqual(dt2.__class__, C)
   3003         self.assertEqual(dt2.theAnswer, 42)
   3004         self.assertEqual(dt2.extra, 7)
   3005         self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
   3006         self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
   3007 
   3008 # Pain to set up DST-aware tzinfo classes.
   3009 
   3010 def first_sunday_on_or_after(dt):
   3011     days_to_go = 6 - dt.weekday()
   3012     if days_to_go:
   3013         dt += timedelta(days_to_go)
   3014     return dt
   3015 
   3016 ZERO = timedelta(0)
   3017 HOUR = timedelta(hours=1)
   3018 DAY = timedelta(days=1)
   3019 # In the US, DST starts at 2am (standard time) on the first Sunday in April.
   3020 DSTSTART = datetime(1, 4, 1, 2)
   3021 # and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
   3022 # which is the first Sunday on or after Oct 25.  Because we view 1:MM as
   3023 # being standard time on that day, there is no spelling in local time of
   3024 # the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
   3025 DSTEND = datetime(1, 10, 25, 1)
   3026 
   3027 class USTimeZone(tzinfo):
   3028 
   3029     def __init__(self, hours, reprname, stdname, dstname):
   3030         self.stdoffset = timedelta(hours=hours)
   3031         self.reprname = reprname
   3032         self.stdname = stdname
   3033         self.dstname = dstname
   3034 
   3035     def __repr__(self):
   3036         return self.reprname
   3037 
   3038     def tzname(self, dt):
   3039         if self.dst(dt):
   3040             return self.dstname
   3041         else:
   3042             return self.stdname
   3043 
   3044     def utcoffset(self, dt):
   3045         return self.stdoffset + self.dst(dt)
   3046 
   3047     def dst(self, dt):
   3048         if dt is None or dt.tzinfo is None:
   3049             # An exception instead may be sensible here, in one or more of
   3050             # the cases.
   3051             return ZERO
   3052         assert dt.tzinfo is self
   3053 
   3054         # Find first Sunday in April.
   3055         start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
   3056         assert start.weekday() == 6 and start.month == 4 and start.day <= 7
   3057 
   3058         # Find last Sunday in October.
   3059         end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
   3060         assert end.weekday() == 6 and end.month == 10 and end.day >= 25
   3061 
   3062         # Can't compare naive to aware objects, so strip the timezone from
   3063         # dt first.
   3064         if start <= dt.replace(tzinfo=None) < end:
   3065             return HOUR
   3066         else:
   3067             return ZERO
   3068 
   3069 Eastern  = USTimeZone(-5, "Eastern",  "EST", "EDT")
   3070 Central  = USTimeZone(-6, "Central",  "CST", "CDT")
   3071 Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
   3072 Pacific  = USTimeZone(-8, "Pacific",  "PST", "PDT")
   3073 utc_real = FixedOffset(0, "UTC", 0)
   3074 # For better test coverage, we want another flavor of UTC that's west of
   3075 # the Eastern and Pacific timezones.
   3076 utc_fake = FixedOffset(-12*60, "UTCfake", 0)
   3077 
   3078 class TestTimezoneConversions(unittest.TestCase):
   3079     # The DST switch times for 2002, in std time.
   3080     dston = datetime(2002, 4, 7, 2)
   3081     dstoff = datetime(2002, 10, 27, 1)
   3082 
   3083     theclass = datetime
   3084 
   3085     # Check a time that's inside DST.
   3086     def checkinside(self, dt, tz, utc, dston, dstoff):
   3087         self.assertEqual(dt.dst(), HOUR)
   3088 
   3089         # Conversion to our own timezone is always an identity.
   3090         self.assertEqual(dt.astimezone(tz), dt)
   3091 
   3092         asutc = dt.astimezone(utc)
   3093         there_and_back = asutc.astimezone(tz)
   3094 
   3095         # Conversion to UTC and back isn't always an identity here,
   3096         # because there are redundant spellings (in local time) of
   3097         # UTC time when DST begins:  the clock jumps from 1:59:59
   3098         # to 3:00:00, and a local time of 2:MM:SS doesn't really
   3099         # make sense then.  The classes above treat 2:MM:SS as
   3100         # daylight time then (it's "after 2am"), really an alias
   3101         # for 1:MM:SS standard time.  The latter form is what
   3102         # conversion back from UTC produces.
   3103         if dt.date() == dston.date() and dt.hour == 2:
   3104             # We're in the redundant hour, and coming back from
   3105             # UTC gives the 1:MM:SS standard-time spelling.
   3106             self.assertEqual(there_and_back + HOUR, dt)
   3107             # Although during was considered to be in daylight
   3108             # time, there_and_back is not.
   3109             self.assertEqual(there_and_back.dst(), ZERO)
   3110             # They're the same times in UTC.
   3111             self.assertEqual(there_and_back.astimezone(utc),
   3112                              dt.astimezone(utc))
   3113         else:
   3114             # We're not in the redundant hour.
   3115             self.assertEqual(dt, there_and_back)
   3116 
   3117         # Because we have a redundant spelling when DST begins, there is
   3118         # (unfortunately) an hour when DST ends that can't be spelled at all in
   3119         # local time.  When DST ends, the clock jumps from 1:59 back to 1:00
   3120         # again.  The hour 1:MM DST has no spelling then:  1:MM is taken to be
   3121         # standard time.  1:MM DST == 0:MM EST, but 0:MM is taken to be
   3122         # daylight time.  The hour 1:MM daylight == 0:MM standard can't be
   3123         # expressed in local time.  Nevertheless, we want conversion back
   3124         # from UTC to mimic the local clock's "repeat an hour" behavior.
   3125         nexthour_utc = asutc + HOUR
   3126         nexthour_tz = nexthour_utc.astimezone(tz)
   3127         if dt.date() == dstoff.date() and dt.hour == 0:
   3128             # We're in the hour before the last DST hour.  The last DST hour
   3129             # is ineffable.  We want the conversion back to repeat 1:MM.
   3130             self.assertEqual(nexthour_tz, dt.replace(hour=1))
   3131             nexthour_utc += HOUR
   3132             nexthour_tz = nexthour_utc.astimezone(tz)
   3133             self.assertEqual(nexthour_tz, dt.replace(hour=1))
   3134         else:
   3135             self.assertEqual(nexthour_tz - dt, HOUR)
   3136 
   3137     # Check a time that's outside DST.
   3138     def checkoutside(self, dt, tz, utc):
   3139         self.assertEqual(dt.dst(), ZERO)
   3140 
   3141         # Conversion to our own timezone is always an identity.
   3142         self.assertEqual(dt.astimezone(tz), dt)
   3143 
   3144         # Converting to UTC and back is an identity too.
   3145         asutc = dt.astimezone(utc)
   3146         there_and_back = asutc.astimezone(tz)
   3147         self.assertEqual(dt, there_and_back)
   3148 
   3149     def convert_between_tz_and_utc(self, tz, utc):
   3150         dston = self.dston.replace(tzinfo=tz)
   3151         # Because 1:MM on the day DST ends is taken as being standard time,
   3152         # there is no spelling in tz for the last hour of daylight time.
   3153         # For purposes of the test, the last hour of DST is 0:MM, which is
   3154         # taken as being daylight time (and 1:MM is taken as being standard
   3155         # time).
   3156         dstoff = self.dstoff.replace(tzinfo=tz)
   3157         for delta in (timedelta(weeks=13),
   3158                       DAY,
   3159                       HOUR,
   3160                       timedelta(minutes=1),
   3161                       timedelta(microseconds=1)):
   3162 
   3163             self.checkinside(dston, tz, utc, dston, dstoff)
   3164             for during in dston + delta, dstoff - delta:
   3165                 self.checkinside(during, tz, utc, dston, dstoff)
   3166 
   3167             self.checkoutside(dstoff, tz, utc)
   3168             for outside in dston - delta, dstoff + delta:
   3169                 self.checkoutside(outside, tz, utc)
   3170 
   3171     def test_easy(self):
   3172         # Despite the name of this test, the endcases are excruciating.
   3173         self.convert_between_tz_and_utc(Eastern, utc_real)
   3174         self.convert_between_tz_and_utc(Pacific, utc_real)
   3175         self.convert_between_tz_and_utc(Eastern, utc_fake)
   3176         self.convert_between_tz_and_utc(Pacific, utc_fake)
   3177         # The next is really dancing near the edge.  It works because
   3178         # Pacific and Eastern are far enough apart that their "problem
   3179         # hours" don't overlap.
   3180         self.convert_between_tz_and_utc(Eastern, Pacific)
   3181         self.convert_between_tz_and_utc(Pacific, Eastern)
   3182         # OTOH, these fail!  Don't enable them.  The difficulty is that
   3183         # the edge case tests assume that every hour is representable in
   3184         # the "utc" class.  This is always true for a fixed-offset tzinfo
   3185         # class (lke utc_real and utc_fake), but not for Eastern or Central.
   3186         # For these adjacent DST-aware time zones, the range of time offsets
   3187         # tested ends up creating hours in the one that aren't representable
   3188         # in the other.  For the same reason, we would see failures in the
   3189         # Eastern vs Pacific tests too if we added 3*HOUR to the list of
   3190         # offset deltas in convert_between_tz_and_utc().
   3191         #
   3192         # self.convert_between_tz_and_utc(Eastern, Central)  # can't work
   3193         # self.convert_between_tz_and_utc(Central, Eastern)  # can't work
   3194 
   3195     def test_tricky(self):
   3196         # 22:00 on day before daylight starts.
   3197         fourback = self.dston - timedelta(hours=4)
   3198         ninewest = FixedOffset(-9*60, "-0900", 0)
   3199         fourback = fourback.replace(tzinfo=ninewest)
   3200         # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST.  Since it's "after
   3201         # 2", we should get the 3 spelling.
   3202         # If we plug 22:00 the day before into Eastern, it "looks like std
   3203         # time", so its offset is returned as -5, and -5 - -9 = 4.  Adding 4
   3204         # to 22:00 lands on 2:00, which makes no sense in local time (the
   3205         # local clock jumps from 1 to 3).  The point here is to make sure we
   3206         # get the 3 spelling.
   3207         expected = self.dston.replace(hour=3)
   3208         got = fourback.astimezone(Eastern).replace(tzinfo=None)
   3209         self.assertEqual(expected, got)
   3210 
   3211         # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST.  In that
   3212         # case we want the 1:00 spelling.
   3213         sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
   3214         # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
   3215         # and adding -4-0 == -4 gives the 2:00 spelling.  We want the 1:00 EST
   3216         # spelling.
   3217         expected = self.dston.replace(hour=1)
   3218         got = sixutc.astimezone(Eastern).replace(tzinfo=None)
   3219         self.assertEqual(expected, got)
   3220 
   3221         # Now on the day DST ends, we want "repeat an hour" behavior.
   3222         #  UTC  4:MM  5:MM  6:MM  7:MM  checking these
   3223         #  EST 23:MM  0:MM  1:MM  2:MM
   3224         #  EDT  0:MM  1:MM  2:MM  3:MM
   3225         # wall  0:MM  1:MM  1:MM  2:MM  against these
   3226         for utc in utc_real, utc_fake:
   3227             for tz in Eastern, Pacific:
   3228                 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
   3229                 # Convert that to UTC.
   3230                 first_std_hour -= tz.utcoffset(None)
   3231                 # Adjust for possibly fake UTC.
   3232                 asutc = first_std_hour + utc.utcoffset(None)
   3233                 # First UTC hour to convert; this is 4:00 when utc=utc_real &
   3234                 # tz=Eastern.
   3235                 asutcbase = asutc.replace(tzinfo=utc)
   3236                 for tzhour in (0, 1, 1, 2):
   3237                     expectedbase = self.dstoff.replace(hour=tzhour)
   3238                     for minute in 0, 30, 59:
   3239                         expected = expectedbase.replace(minute=minute)
   3240                         asutc = asutcbase.replace(minute=minute)
   3241                         astz = asutc.astimezone(tz)
   3242                         self.assertEqual(astz.replace(tzinfo=None), expected)
   3243                     asutcbase += HOUR
   3244 
   3245 
   3246     def test_bogus_dst(self):
   3247         class ok(tzinfo):
   3248             def utcoffset(self, dt): return HOUR
   3249             def dst(self, dt): return HOUR
   3250 
   3251         now = self.theclass.now().replace(tzinfo=utc_real)
   3252         # Doesn't blow up.
   3253         now.astimezone(ok())
   3254 
   3255         # Does blow up.
   3256         class notok(ok):
   3257             def dst(self, dt): return None
   3258         self.assertRaises(ValueError, now.astimezone, notok())
   3259 
   3260     def test_fromutc(self):
   3261         self.assertRaises(TypeError, Eastern.fromutc)   # not enough args
   3262         now = datetime.utcnow().replace(tzinfo=utc_real)
   3263         self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
   3264         now = now.replace(tzinfo=Eastern)   # insert correct tzinfo
   3265         enow = Eastern.fromutc(now)         # doesn't blow up
   3266         self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
   3267         self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
   3268         self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
   3269 
   3270         # Always converts UTC to standard time.
   3271         class FauxUSTimeZone(USTimeZone):
   3272             def fromutc(self, dt):
   3273                 return dt + self.stdoffset
   3274         FEastern  = FauxUSTimeZone(-5, "FEastern",  "FEST", "FEDT")
   3275 
   3276         #  UTC  4:MM  5:MM  6:MM  7:MM  8:MM  9:MM
   3277         #  EST 23:MM  0:MM  1:MM  2:MM  3:MM  4:MM
   3278         #  EDT  0:MM  1:MM  2:MM  3:MM  4:MM  5:MM
   3279 
   3280         # Check around DST start.
   3281         start = self.dston.replace(hour=4, tzinfo=Eastern)
   3282         fstart = start.replace(tzinfo=FEastern)
   3283         for wall in 23, 0, 1, 3, 4, 5:
   3284             expected = start.replace(hour=wall)
   3285             if wall == 23:
   3286                 expected -= timedelta(days=1)
   3287             got = Eastern.fromutc(start)
   3288             self.assertEqual(expected, got)
   3289 
   3290             expected = fstart + FEastern.stdoffset
   3291             got = FEastern.fromutc(fstart)
   3292             self.assertEqual(expected, got)
   3293 
   3294             # Ensure astimezone() calls fromutc() too.
   3295             got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
   3296             self.assertEqual(expected, got)
   3297 
   3298             start += HOUR
   3299             fstart += HOUR
   3300 
   3301         # Check around DST end.
   3302         start = self.dstoff.replace(hour=4, tzinfo=Eastern)
   3303         fstart = start.replace(tzinfo=FEastern)
   3304         for wall in 0, 1, 1, 2, 3, 4:
   3305             expected = start.replace(hour=wall)
   3306             got = Eastern.fromutc(start)
   3307             self.assertEqual(expected, got)
   3308 
   3309             expected = fstart + FEastern.stdoffset
   3310             got = FEastern.fromutc(fstart)
   3311             self.assertEqual(expected, got)
   3312 
   3313             # Ensure astimezone() calls fromutc() too.
   3314             got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
   3315             self.assertEqual(expected, got)
   3316 
   3317             start += HOUR
   3318             fstart += HOUR
   3319 
   3320 
   3321 #############################################################################
   3322 # oddballs
   3323 
   3324 class Oddballs(unittest.TestCase):
   3325 
   3326     def test_bug_1028306(self):
   3327         # Trying to compare a date to a datetime should act like a mixed-
   3328         # type comparison, despite that datetime is a subclass of date.
   3329         as_date = date.today()
   3330         as_datetime = datetime.combine(as_date, time())
   3331         self.assertTrue(as_date != as_datetime)
   3332         self.assertTrue(as_datetime != as_date)
   3333         self.assertTrue(not as_date == as_datetime)
   3334         self.assertTrue(not as_datetime == as_date)
   3335         self.assertRaises(TypeError, lambda: as_date < as_datetime)
   3336         self.assertRaises(TypeError, lambda: as_datetime < as_date)
   3337         self.assertRaises(TypeError, lambda: as_date <= as_datetime)
   3338         self.assertRaises(TypeError, lambda: as_datetime <= as_date)
   3339         self.assertRaises(TypeError, lambda: as_date > as_datetime)
   3340         self.assertRaises(TypeError, lambda: as_datetime > as_date)
   3341         self.assertRaises(TypeError, lambda: as_date >= as_datetime)
   3342         self.assertRaises(TypeError, lambda: as_datetime >= as_date)
   3343 
   3344         # Neverthelss, comparison should work with the base-class (date)
   3345         # projection if use of a date method is forced.
   3346         self.assertTrue(as_date.__eq__(as_datetime))
   3347         different_day = (as_date.day + 1) % 20 + 1
   3348         self.assertTrue(not as_date.__eq__(as_datetime.replace(day=
   3349                                                      different_day)))
   3350 
   3351         # And date should compare with other subclasses of date.  If a
   3352         # subclass wants to stop this, it's up to the subclass to do so.
   3353         date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
   3354         self.assertEqual(as_date, date_sc)
   3355         self.assertEqual(date_sc, as_date)
   3356 
   3357         # Ditto for datetimes.
   3358         datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
   3359                                        as_date.day, 0, 0, 0)
   3360         self.assertEqual(as_datetime, datetime_sc)
   3361         self.assertEqual(datetime_sc, as_datetime)
   3362 
   3363 def test_main():
   3364     test_support.run_unittest(__name__)
   3365 
   3366 if __name__ == "__main__":
   3367     test_main()
   3368