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