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