Back to index

python3.2  3.2.2
datetimetester.py
Go to the documentation of this file.
00001 """Test date/time type.
00002 
00003 See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
00004 """
00005 
00006 import sys
00007 import pickle
00008 import unittest
00009 
00010 from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
00011 
00012 from test import support
00013 
00014 import datetime as datetime_module
00015 from datetime import MINYEAR, MAXYEAR
00016 from datetime import timedelta
00017 from datetime import tzinfo
00018 from datetime import time
00019 from datetime import timezone
00020 from datetime import date, datetime
00021 import time as _time
00022 
00023 # Needed by test_datetime
00024 import _strptime
00025 #
00026 
00027 
00028 pickle_choices = [(pickle, pickle, proto)
00029                   for proto in range(pickle.HIGHEST_PROTOCOL + 1)]
00030 assert len(pickle_choices) == pickle.HIGHEST_PROTOCOL + 1
00031 
00032 # An arbitrary collection of objects of non-datetime types, for testing
00033 # mixed-type comparisons.
00034 OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
00035 
00036 
00037 # XXX Copied from test_float.
00038 INF = float("inf")
00039 NAN = float("nan")
00040 
00041 
00042 #############################################################################
00043 # module tests
00044 
00045 class TestModule(unittest.TestCase):
00046 
00047     def test_constants(self):
00048         datetime = datetime_module
00049         self.assertEqual(datetime.MINYEAR, 1)
00050         self.assertEqual(datetime.MAXYEAR, 9999)
00051 
00052 #############################################################################
00053 # tzinfo tests
00054 
00055 class FixedOffset(tzinfo):
00056 
00057     def __init__(self, offset, name, dstoffset=42):
00058         if isinstance(offset, int):
00059             offset = timedelta(minutes=offset)
00060         if isinstance(dstoffset, int):
00061             dstoffset = timedelta(minutes=dstoffset)
00062         self.__offset = offset
00063         self.__name = name
00064         self.__dstoffset = dstoffset
00065     def __repr__(self):
00066         return self.__name.lower()
00067     def utcoffset(self, dt):
00068         return self.__offset
00069     def tzname(self, dt):
00070         return self.__name
00071     def dst(self, dt):
00072         return self.__dstoffset
00073 
00074 class PicklableFixedOffset(FixedOffset):
00075 
00076     def __init__(self, offset=None, name=None, dstoffset=None):
00077         FixedOffset.__init__(self, offset, name, dstoffset)
00078 
00079 class TestTZInfo(unittest.TestCase):
00080 
00081     def test_non_abstractness(self):
00082         # In order to allow subclasses to get pickled, the C implementation
00083         # wasn't able to get away with having __init__ raise
00084         # NotImplementedError.
00085         useless = tzinfo()
00086         dt = datetime.max
00087         self.assertRaises(NotImplementedError, useless.tzname, dt)
00088         self.assertRaises(NotImplementedError, useless.utcoffset, dt)
00089         self.assertRaises(NotImplementedError, useless.dst, dt)
00090 
00091     def test_subclass_must_override(self):
00092         class NotEnough(tzinfo):
00093             def __init__(self, offset, name):
00094                 self.__offset = offset
00095                 self.__name = name
00096         self.assertTrue(issubclass(NotEnough, tzinfo))
00097         ne = NotEnough(3, "NotByALongShot")
00098         self.assertIsInstance(ne, tzinfo)
00099 
00100         dt = datetime.now()
00101         self.assertRaises(NotImplementedError, ne.tzname, dt)
00102         self.assertRaises(NotImplementedError, ne.utcoffset, dt)
00103         self.assertRaises(NotImplementedError, ne.dst, dt)
00104 
00105     def test_normal(self):
00106         fo = FixedOffset(3, "Three")
00107         self.assertIsInstance(fo, tzinfo)
00108         for dt in datetime.now(), None:
00109             self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
00110             self.assertEqual(fo.tzname(dt), "Three")
00111             self.assertEqual(fo.dst(dt), timedelta(minutes=42))
00112 
00113     def test_pickling_base(self):
00114         # There's no point to pickling tzinfo objects on their own (they
00115         # carry no data), but they need to be picklable anyway else
00116         # concrete subclasses can't be pickled.
00117         orig = tzinfo.__new__(tzinfo)
00118         self.assertTrue(type(orig) is tzinfo)
00119         for pickler, unpickler, proto in pickle_choices:
00120             green = pickler.dumps(orig, proto)
00121             derived = unpickler.loads(green)
00122             self.assertTrue(type(derived) is tzinfo)
00123 
00124     def test_pickling_subclass(self):
00125         # Make sure we can pickle/unpickle an instance of a subclass.
00126         offset = timedelta(minutes=-300)
00127         for otype, args in [
00128             (PicklableFixedOffset, (offset, 'cookie')),
00129             (timezone, (offset,)),
00130             (timezone, (offset, "EST"))]:
00131             orig = otype(*args)
00132             oname = orig.tzname(None)
00133             self.assertIsInstance(orig, tzinfo)
00134             self.assertIs(type(orig), otype)
00135             self.assertEqual(orig.utcoffset(None), offset)
00136             self.assertEqual(orig.tzname(None), oname)
00137             for pickler, unpickler, proto in pickle_choices:
00138                 green = pickler.dumps(orig, proto)
00139                 derived = unpickler.loads(green)
00140                 self.assertIsInstance(derived, tzinfo)
00141                 self.assertIs(type(derived), otype)
00142                 self.assertEqual(derived.utcoffset(None), offset)
00143                 self.assertEqual(derived.tzname(None), oname)
00144 
00145 class TestTimeZone(unittest.TestCase):
00146 
00147     def setUp(self):
00148         self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
00149         self.EST = timezone(-timedelta(hours=5), 'EST')
00150         self.DT = datetime(2010, 1, 1)
00151 
00152     def test_str(self):
00153         for tz in [self.ACDT, self.EST, timezone.utc,
00154                    timezone.min, timezone.max]:
00155             self.assertEqual(str(tz), tz.tzname(None))
00156 
00157     def test_repr(self):
00158         datetime = datetime_module
00159         for tz in [self.ACDT, self.EST, timezone.utc,
00160                    timezone.min, timezone.max]:
00161             # test round-trip
00162             tzrep = repr(tz)
00163             self.assertEqual(tz, eval(tzrep))
00164 
00165 
00166     def test_class_members(self):
00167         limit = timedelta(hours=23, minutes=59)
00168         self.assertEqual(timezone.utc.utcoffset(None), ZERO)
00169         self.assertEqual(timezone.min.utcoffset(None), -limit)
00170         self.assertEqual(timezone.max.utcoffset(None), limit)
00171 
00172 
00173     def test_constructor(self):
00174         self.assertIs(timezone.utc, timezone(timedelta(0)))
00175         self.assertIsNot(timezone.utc, timezone(timedelta(0), 'UTC'))
00176         self.assertEqual(timezone.utc, timezone(timedelta(0), 'UTC'))
00177         # invalid offsets
00178         for invalid in [timedelta(microseconds=1), timedelta(1, 1),
00179                         timedelta(seconds=1), timedelta(1), -timedelta(1)]:
00180             self.assertRaises(ValueError, timezone, invalid)
00181             self.assertRaises(ValueError, timezone, -invalid)
00182 
00183         with self.assertRaises(TypeError): timezone(None)
00184         with self.assertRaises(TypeError): timezone(42)
00185         with self.assertRaises(TypeError): timezone(ZERO, None)
00186         with self.assertRaises(TypeError): timezone(ZERO, 42)
00187         with self.assertRaises(TypeError): timezone(ZERO, 'ABC', 'extra')
00188 
00189     def test_inheritance(self):
00190         self.assertIsInstance(timezone.utc, tzinfo)
00191         self.assertIsInstance(self.EST, tzinfo)
00192 
00193     def test_utcoffset(self):
00194         dummy = self.DT
00195         for h in [0, 1.5, 12]:
00196             offset = h * HOUR
00197             self.assertEqual(offset, timezone(offset).utcoffset(dummy))
00198             self.assertEqual(-offset, timezone(-offset).utcoffset(dummy))
00199 
00200         with self.assertRaises(TypeError): self.EST.utcoffset('')
00201         with self.assertRaises(TypeError): self.EST.utcoffset(5)
00202 
00203 
00204     def test_dst(self):
00205         self.assertIsNone(timezone.utc.dst(self.DT))
00206 
00207         with self.assertRaises(TypeError): self.EST.dst('')
00208         with self.assertRaises(TypeError): self.EST.dst(5)
00209 
00210     def test_tzname(self):
00211         self.assertEqual('UTC+00:00', timezone(ZERO).tzname(None))
00212         self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None))
00213         self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
00214         self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
00215         self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
00216 
00217         with self.assertRaises(TypeError): self.EST.tzname('')
00218         with self.assertRaises(TypeError): self.EST.tzname(5)
00219 
00220     def test_fromutc(self):
00221         with self.assertRaises(ValueError):
00222             timezone.utc.fromutc(self.DT)
00223         with self.assertRaises(TypeError):
00224             timezone.utc.fromutc('not datetime')
00225         for tz in [self.EST, self.ACDT, Eastern]:
00226             utctime = self.DT.replace(tzinfo=tz)
00227             local = tz.fromutc(utctime)
00228             self.assertEqual(local - utctime, tz.utcoffset(local))
00229             self.assertEqual(local,
00230                              self.DT.replace(tzinfo=timezone.utc))
00231 
00232     def test_comparison(self):
00233         self.assertNotEqual(timezone(ZERO), timezone(HOUR))
00234         self.assertEqual(timezone(HOUR), timezone(HOUR))
00235         self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
00236         with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
00237         self.assertIn(timezone(ZERO), {timezone(ZERO)})
00238 
00239     def test_aware_datetime(self):
00240         # test that timezone instances can be used by datetime
00241         t = datetime(1, 1, 1)
00242         for tz in [timezone.min, timezone.max, timezone.utc]:
00243             self.assertEqual(tz.tzname(t),
00244                              t.replace(tzinfo=tz).tzname())
00245             self.assertEqual(tz.utcoffset(t),
00246                              t.replace(tzinfo=tz).utcoffset())
00247             self.assertEqual(tz.dst(t),
00248                              t.replace(tzinfo=tz).dst())
00249 
00250 #############################################################################
00251 # Base clase for testing a particular aspect of timedelta, time, date and
00252 # datetime comparisons.
00253 
00254 class HarmlessMixedComparison:
00255     # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
00256 
00257     # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
00258     # legit constructor.
00259 
00260     def test_harmless_mixed_comparison(self):
00261         me = self.theclass(1, 1, 1)
00262 
00263         self.assertFalse(me == ())
00264         self.assertTrue(me != ())
00265         self.assertFalse(() == me)
00266         self.assertTrue(() != me)
00267 
00268         self.assertIn(me, [1, 20, [], me])
00269         self.assertIn([], [me, 1, 20, []])
00270 
00271     def test_harmful_mixed_comparison(self):
00272         me = self.theclass(1, 1, 1)
00273 
00274         self.assertRaises(TypeError, lambda: me < ())
00275         self.assertRaises(TypeError, lambda: me <= ())
00276         self.assertRaises(TypeError, lambda: me > ())
00277         self.assertRaises(TypeError, lambda: me >= ())
00278 
00279         self.assertRaises(TypeError, lambda: () < me)
00280         self.assertRaises(TypeError, lambda: () <= me)
00281         self.assertRaises(TypeError, lambda: () > me)
00282         self.assertRaises(TypeError, lambda: () >= me)
00283 
00284 #############################################################################
00285 # timedelta tests
00286 
00287 class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
00288 
00289     theclass = timedelta
00290 
00291     def test_constructor(self):
00292         eq = self.assertEqual
00293         td = timedelta
00294 
00295         # Check keyword args to constructor
00296         eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
00297                     milliseconds=0, microseconds=0))
00298         eq(td(1), td(days=1))
00299         eq(td(0, 1), td(seconds=1))
00300         eq(td(0, 0, 1), td(microseconds=1))
00301         eq(td(weeks=1), td(days=7))
00302         eq(td(days=1), td(hours=24))
00303         eq(td(hours=1), td(minutes=60))
00304         eq(td(minutes=1), td(seconds=60))
00305         eq(td(seconds=1), td(milliseconds=1000))
00306         eq(td(milliseconds=1), td(microseconds=1000))
00307 
00308         # Check float args to constructor
00309         eq(td(weeks=1.0/7), td(days=1))
00310         eq(td(days=1.0/24), td(hours=1))
00311         eq(td(hours=1.0/60), td(minutes=1))
00312         eq(td(minutes=1.0/60), td(seconds=1))
00313         eq(td(seconds=0.001), td(milliseconds=1))
00314         eq(td(milliseconds=0.001), td(microseconds=1))
00315 
00316     def test_computations(self):
00317         eq = self.assertEqual
00318         td = timedelta
00319 
00320         a = td(7) # One week
00321         b = td(0, 60) # One minute
00322         c = td(0, 0, 1000) # One millisecond
00323         eq(a+b+c, td(7, 60, 1000))
00324         eq(a-b, td(6, 24*3600 - 60))
00325         eq(b.__rsub__(a), td(6, 24*3600 - 60))
00326         eq(-a, td(-7))
00327         eq(+a, td(7))
00328         eq(-b, td(-1, 24*3600 - 60))
00329         eq(-c, td(-1, 24*3600 - 1, 999000))
00330         eq(abs(a), a)
00331         eq(abs(-a), a)
00332         eq(td(6, 24*3600), a)
00333         eq(td(0, 0, 60*1000000), b)
00334         eq(a*10, td(70))
00335         eq(a*10, 10*a)
00336         eq(a*10, 10*a)
00337         eq(b*10, td(0, 600))
00338         eq(10*b, td(0, 600))
00339         eq(b*10, td(0, 600))
00340         eq(c*10, td(0, 0, 10000))
00341         eq(10*c, td(0, 0, 10000))
00342         eq(c*10, td(0, 0, 10000))
00343         eq(a*-1, -a)
00344         eq(b*-2, -b-b)
00345         eq(c*-2, -c+-c)
00346         eq(b*(60*24), (b*60)*24)
00347         eq(b*(60*24), (60*b)*24)
00348         eq(c*1000, td(0, 1))
00349         eq(1000*c, td(0, 1))
00350         eq(a//7, td(1))
00351         eq(b//10, td(0, 6))
00352         eq(c//1000, td(0, 0, 1))
00353         eq(a//10, td(0, 7*24*360))
00354         eq(a//3600000, td(0, 0, 7*24*1000))
00355         eq(a/0.5, td(14))
00356         eq(b/0.5, td(0, 120))
00357         eq(a/7, td(1))
00358         eq(b/10, td(0, 6))
00359         eq(c/1000, td(0, 0, 1))
00360         eq(a/10, td(0, 7*24*360))
00361         eq(a/3600000, td(0, 0, 7*24*1000))
00362 
00363         # Multiplication by float
00364         us = td(microseconds=1)
00365         eq((3*us) * 0.5, 2*us)
00366         eq((5*us) * 0.5, 2*us)
00367         eq(0.5 * (3*us), 2*us)
00368         eq(0.5 * (5*us), 2*us)
00369         eq((-3*us) * 0.5, -2*us)
00370         eq((-5*us) * 0.5, -2*us)
00371 
00372         # Division by int and float
00373         eq((3*us) / 2, 2*us)
00374         eq((5*us) / 2, 2*us)
00375         eq((-3*us) / 2.0, -2*us)
00376         eq((-5*us) / 2.0, -2*us)
00377         eq((3*us) / -2, -2*us)
00378         eq((5*us) / -2, -2*us)
00379         eq((3*us) / -2.0, -2*us)
00380         eq((5*us) / -2.0, -2*us)
00381         for i in range(-10, 10):
00382             eq((i*us/3)//us, round(i/3))
00383         for i in range(-10, 10):
00384             eq((i*us/-3)//us, round(i/-3))
00385 
00386         # Issue #11576
00387         eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998),
00388            td(0, 0, 1))
00389         eq(td(999999999, 1, 1) - td(999999999, 1, 0),
00390            td(0, 0, 1))
00391 
00392     def test_disallowed_computations(self):
00393         a = timedelta(42)
00394 
00395         # Add/sub ints or floats should be illegal
00396         for i in 1, 1.0:
00397             self.assertRaises(TypeError, lambda: a+i)
00398             self.assertRaises(TypeError, lambda: a-i)
00399             self.assertRaises(TypeError, lambda: i+a)
00400             self.assertRaises(TypeError, lambda: i-a)
00401 
00402         # Division of int by timedelta doesn't make sense.
00403         # Division by zero doesn't make sense.
00404         zero = 0
00405         self.assertRaises(TypeError, lambda: zero // a)
00406         self.assertRaises(ZeroDivisionError, lambda: a // zero)
00407         self.assertRaises(ZeroDivisionError, lambda: a / zero)
00408         self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
00409         self.assertRaises(TypeError, lambda: a / '')
00410 
00411     @support.requires_IEEE_754
00412     def test_disallowed_special(self):
00413         a = timedelta(42)
00414         self.assertRaises(ValueError, a.__mul__, NAN)
00415         self.assertRaises(ValueError, a.__truediv__, NAN)
00416 
00417     def test_basic_attributes(self):
00418         days, seconds, us = 1, 7, 31
00419         td = timedelta(days, seconds, us)
00420         self.assertEqual(td.days, days)
00421         self.assertEqual(td.seconds, seconds)
00422         self.assertEqual(td.microseconds, us)
00423 
00424     def test_total_seconds(self):
00425         td = timedelta(days=365)
00426         self.assertEqual(td.total_seconds(), 31536000.0)
00427         for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
00428             td = timedelta(seconds=total_seconds)
00429             self.assertEqual(td.total_seconds(), total_seconds)
00430         # Issue8644: Test that td.total_seconds() has the same
00431         # accuracy as td / timedelta(seconds=1).
00432         for ms in [-1, -2, -123]:
00433             td = timedelta(microseconds=ms)
00434             self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
00435 
00436     def test_carries(self):
00437         t1 = timedelta(days=100,
00438                        weeks=-7,
00439                        hours=-24*(100-49),
00440                        minutes=-3,
00441                        seconds=12,
00442                        microseconds=(3*60 - 12) * 1e6 + 1)
00443         t2 = timedelta(microseconds=1)
00444         self.assertEqual(t1, t2)
00445 
00446     def test_hash_equality(self):
00447         t1 = timedelta(days=100,
00448                        weeks=-7,
00449                        hours=-24*(100-49),
00450                        minutes=-3,
00451                        seconds=12,
00452                        microseconds=(3*60 - 12) * 1000000)
00453         t2 = timedelta()
00454         self.assertEqual(hash(t1), hash(t2))
00455 
00456         t1 += timedelta(weeks=7)
00457         t2 += timedelta(days=7*7)
00458         self.assertEqual(t1, t2)
00459         self.assertEqual(hash(t1), hash(t2))
00460 
00461         d = {t1: 1}
00462         d[t2] = 2
00463         self.assertEqual(len(d), 1)
00464         self.assertEqual(d[t1], 2)
00465 
00466     def test_pickling(self):
00467         args = 12, 34, 56
00468         orig = timedelta(*args)
00469         for pickler, unpickler, proto in pickle_choices:
00470             green = pickler.dumps(orig, proto)
00471             derived = unpickler.loads(green)
00472             self.assertEqual(orig, derived)
00473 
00474     def test_compare(self):
00475         t1 = timedelta(2, 3, 4)
00476         t2 = timedelta(2, 3, 4)
00477         self.assertEqual(t1, t2)
00478         self.assertTrue(t1 <= t2)
00479         self.assertTrue(t1 >= t2)
00480         self.assertTrue(not t1 != t2)
00481         self.assertTrue(not t1 < t2)
00482         self.assertTrue(not t1 > t2)
00483 
00484         for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
00485             t2 = timedelta(*args)   # this is larger than t1
00486             self.assertTrue(t1 < t2)
00487             self.assertTrue(t2 > t1)
00488             self.assertTrue(t1 <= t2)
00489             self.assertTrue(t2 >= t1)
00490             self.assertTrue(t1 != t2)
00491             self.assertTrue(t2 != t1)
00492             self.assertTrue(not t1 == t2)
00493             self.assertTrue(not t2 == t1)
00494             self.assertTrue(not t1 > t2)
00495             self.assertTrue(not t2 < t1)
00496             self.assertTrue(not t1 >= t2)
00497             self.assertTrue(not t2 <= t1)
00498 
00499         for badarg in OTHERSTUFF:
00500             self.assertEqual(t1 == badarg, False)
00501             self.assertEqual(t1 != badarg, True)
00502             self.assertEqual(badarg == t1, False)
00503             self.assertEqual(badarg != t1, True)
00504 
00505             self.assertRaises(TypeError, lambda: t1 <= badarg)
00506             self.assertRaises(TypeError, lambda: t1 < badarg)
00507             self.assertRaises(TypeError, lambda: t1 > badarg)
00508             self.assertRaises(TypeError, lambda: t1 >= badarg)
00509             self.assertRaises(TypeError, lambda: badarg <= t1)
00510             self.assertRaises(TypeError, lambda: badarg < t1)
00511             self.assertRaises(TypeError, lambda: badarg > t1)
00512             self.assertRaises(TypeError, lambda: badarg >= t1)
00513 
00514     def test_str(self):
00515         td = timedelta
00516         eq = self.assertEqual
00517 
00518         eq(str(td(1)), "1 day, 0:00:00")
00519         eq(str(td(-1)), "-1 day, 0:00:00")
00520         eq(str(td(2)), "2 days, 0:00:00")
00521         eq(str(td(-2)), "-2 days, 0:00:00")
00522 
00523         eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
00524         eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
00525         eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
00526            "-210 days, 23:12:34")
00527 
00528         eq(str(td(milliseconds=1)), "0:00:00.001000")
00529         eq(str(td(microseconds=3)), "0:00:00.000003")
00530 
00531         eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
00532                    microseconds=999999)),
00533            "999999999 days, 23:59:59.999999")
00534 
00535     def test_repr(self):
00536         name = 'datetime.' + self.theclass.__name__
00537         self.assertEqual(repr(self.theclass(1)),
00538                          "%s(1)" % name)
00539         self.assertEqual(repr(self.theclass(10, 2)),
00540                          "%s(10, 2)" % name)
00541         self.assertEqual(repr(self.theclass(-10, 2, 400000)),
00542                          "%s(-10, 2, 400000)" % name)
00543 
00544     def test_roundtrip(self):
00545         for td in (timedelta(days=999999999, hours=23, minutes=59,
00546                              seconds=59, microseconds=999999),
00547                    timedelta(days=-999999999),
00548                    timedelta(days=-999999999, seconds=1),
00549                    timedelta(days=1, seconds=2, microseconds=3)):
00550 
00551             # Verify td -> string -> td identity.
00552             s = repr(td)
00553             self.assertTrue(s.startswith('datetime.'))
00554             s = s[9:]
00555             td2 = eval(s)
00556             self.assertEqual(td, td2)
00557 
00558             # Verify identity via reconstructing from pieces.
00559             td2 = timedelta(td.days, td.seconds, td.microseconds)
00560             self.assertEqual(td, td2)
00561 
00562     def test_resolution_info(self):
00563         self.assertIsInstance(timedelta.min, timedelta)
00564         self.assertIsInstance(timedelta.max, timedelta)
00565         self.assertIsInstance(timedelta.resolution, timedelta)
00566         self.assertTrue(timedelta.max > timedelta.min)
00567         self.assertEqual(timedelta.min, timedelta(-999999999))
00568         self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
00569         self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
00570 
00571     def test_overflow(self):
00572         tiny = timedelta.resolution
00573 
00574         td = timedelta.min + tiny
00575         td -= tiny  # no problem
00576         self.assertRaises(OverflowError, td.__sub__, tiny)
00577         self.assertRaises(OverflowError, td.__add__, -tiny)
00578 
00579         td = timedelta.max - tiny
00580         td += tiny  # no problem
00581         self.assertRaises(OverflowError, td.__add__, tiny)
00582         self.assertRaises(OverflowError, td.__sub__, -tiny)
00583 
00584         self.assertRaises(OverflowError, lambda: -timedelta.max)
00585 
00586         day = timedelta(1)
00587         self.assertRaises(OverflowError, day.__mul__, 10**9)
00588         self.assertRaises(OverflowError, day.__mul__, 1e9)
00589         self.assertRaises(OverflowError, day.__truediv__, 1e-20)
00590         self.assertRaises(OverflowError, day.__truediv__, 1e-10)
00591         self.assertRaises(OverflowError, day.__truediv__, 9e-10)
00592 
00593     @support.requires_IEEE_754
00594     def _test_overflow_special(self):
00595         day = timedelta(1)
00596         self.assertRaises(OverflowError, day.__mul__, INF)
00597         self.assertRaises(OverflowError, day.__mul__, -INF)
00598 
00599     def test_microsecond_rounding(self):
00600         td = timedelta
00601         eq = self.assertEqual
00602 
00603         # Single-field rounding.
00604         eq(td(milliseconds=0.4/1000), td(0))    # rounds to 0
00605         eq(td(milliseconds=-0.4/1000), td(0))    # rounds to 0
00606         eq(td(milliseconds=0.6/1000), td(microseconds=1))
00607         eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
00608 
00609         # Rounding due to contributions from more than one field.
00610         us_per_hour = 3600e6
00611         us_per_day = us_per_hour * 24
00612         eq(td(days=.4/us_per_day), td(0))
00613         eq(td(hours=.2/us_per_hour), td(0))
00614         eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
00615 
00616         eq(td(days=-.4/us_per_day), td(0))
00617         eq(td(hours=-.2/us_per_hour), td(0))
00618         eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
00619 
00620     def test_massive_normalization(self):
00621         td = timedelta(microseconds=-1)
00622         self.assertEqual((td.days, td.seconds, td.microseconds),
00623                          (-1, 24*3600-1, 999999))
00624 
00625     def test_bool(self):
00626         self.assertTrue(timedelta(1))
00627         self.assertTrue(timedelta(0, 1))
00628         self.assertTrue(timedelta(0, 0, 1))
00629         self.assertTrue(timedelta(microseconds=1))
00630         self.assertTrue(not timedelta(0))
00631 
00632     def test_subclass_timedelta(self):
00633 
00634         class T(timedelta):
00635             @staticmethod
00636             def from_td(td):
00637                 return T(td.days, td.seconds, td.microseconds)
00638 
00639             def as_hours(self):
00640                 sum = (self.days * 24 +
00641                        self.seconds / 3600.0 +
00642                        self.microseconds / 3600e6)
00643                 return round(sum)
00644 
00645         t1 = T(days=1)
00646         self.assertTrue(type(t1) is T)
00647         self.assertEqual(t1.as_hours(), 24)
00648 
00649         t2 = T(days=-1, seconds=-3600)
00650         self.assertTrue(type(t2) is T)
00651         self.assertEqual(t2.as_hours(), -25)
00652 
00653         t3 = t1 + t2
00654         self.assertTrue(type(t3) is timedelta)
00655         t4 = T.from_td(t3)
00656         self.assertTrue(type(t4) is T)
00657         self.assertEqual(t3.days, t4.days)
00658         self.assertEqual(t3.seconds, t4.seconds)
00659         self.assertEqual(t3.microseconds, t4.microseconds)
00660         self.assertEqual(str(t3), str(t4))
00661         self.assertEqual(t4.as_hours(), -1)
00662 
00663     def test_division(self):
00664         t = timedelta(hours=1, minutes=24, seconds=19)
00665         second = timedelta(seconds=1)
00666         self.assertEqual(t / second, 5059.0)
00667         self.assertEqual(t // second, 5059)
00668 
00669         t = timedelta(minutes=2, seconds=30)
00670         minute = timedelta(minutes=1)
00671         self.assertEqual(t / minute, 2.5)
00672         self.assertEqual(t // minute, 2)
00673 
00674         zerotd = timedelta(0)
00675         self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
00676         self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
00677 
00678         # self.assertRaises(TypeError, truediv, t, 2)
00679         # note: floor division of a timedelta by an integer *is*
00680         # currently permitted.
00681 
00682     def test_remainder(self):
00683         t = timedelta(minutes=2, seconds=30)
00684         minute = timedelta(minutes=1)
00685         r = t % minute
00686         self.assertEqual(r, timedelta(seconds=30))
00687 
00688         t = timedelta(minutes=-2, seconds=30)
00689         r = t %  minute
00690         self.assertEqual(r, timedelta(seconds=30))
00691 
00692         zerotd = timedelta(0)
00693         self.assertRaises(ZeroDivisionError, mod, t, zerotd)
00694 
00695         self.assertRaises(TypeError, mod, t, 10)
00696 
00697     def test_divmod(self):
00698         t = timedelta(minutes=2, seconds=30)
00699         minute = timedelta(minutes=1)
00700         q, r = divmod(t, minute)
00701         self.assertEqual(q, 2)
00702         self.assertEqual(r, timedelta(seconds=30))
00703 
00704         t = timedelta(minutes=-2, seconds=30)
00705         q, r = divmod(t, minute)
00706         self.assertEqual(q, -2)
00707         self.assertEqual(r, timedelta(seconds=30))
00708 
00709         zerotd = timedelta(0)
00710         self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
00711 
00712         self.assertRaises(TypeError, divmod, t, 10)
00713 
00714 
00715 #############################################################################
00716 # date tests
00717 
00718 class TestDateOnly(unittest.TestCase):
00719     # Tests here won't pass if also run on datetime objects, so don't
00720     # subclass this to test datetimes too.
00721 
00722     def test_delta_non_days_ignored(self):
00723         dt = date(2000, 1, 2)
00724         delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
00725                           microseconds=5)
00726         days = timedelta(delta.days)
00727         self.assertEqual(days, timedelta(1))
00728 
00729         dt2 = dt + delta
00730         self.assertEqual(dt2, dt + days)
00731 
00732         dt2 = delta + dt
00733         self.assertEqual(dt2, dt + days)
00734 
00735         dt2 = dt - delta
00736         self.assertEqual(dt2, dt - days)
00737 
00738         delta = -delta
00739         days = timedelta(delta.days)
00740         self.assertEqual(days, timedelta(-2))
00741 
00742         dt2 = dt + delta
00743         self.assertEqual(dt2, dt + days)
00744 
00745         dt2 = delta + dt
00746         self.assertEqual(dt2, dt + days)
00747 
00748         dt2 = dt - delta
00749         self.assertEqual(dt2, dt - days)
00750 
00751 class SubclassDate(date):
00752     sub_var = 1
00753 
00754 class TestDate(HarmlessMixedComparison, unittest.TestCase):
00755     # Tests here should pass for both dates and datetimes, except for a
00756     # few tests that TestDateTime overrides.
00757 
00758     theclass = date
00759 
00760     def test_basic_attributes(self):
00761         dt = self.theclass(2002, 3, 1)
00762         self.assertEqual(dt.year, 2002)
00763         self.assertEqual(dt.month, 3)
00764         self.assertEqual(dt.day, 1)
00765 
00766     def test_roundtrip(self):
00767         for dt in (self.theclass(1, 2, 3),
00768                    self.theclass.today()):
00769             # Verify dt -> string -> date identity.
00770             s = repr(dt)
00771             self.assertTrue(s.startswith('datetime.'))
00772             s = s[9:]
00773             dt2 = eval(s)
00774             self.assertEqual(dt, dt2)
00775 
00776             # Verify identity via reconstructing from pieces.
00777             dt2 = self.theclass(dt.year, dt.month, dt.day)
00778             self.assertEqual(dt, dt2)
00779 
00780     def test_ordinal_conversions(self):
00781         # Check some fixed values.
00782         for y, m, d, n in [(1, 1, 1, 1),      # calendar origin
00783                            (1, 12, 31, 365),
00784                            (2, 1, 1, 366),
00785                            # first example from "Calendrical Calculations"
00786                            (1945, 11, 12, 710347)]:
00787             d = self.theclass(y, m, d)
00788             self.assertEqual(n, d.toordinal())
00789             fromord = self.theclass.fromordinal(n)
00790             self.assertEqual(d, fromord)
00791             if hasattr(fromord, "hour"):
00792             # if we're checking something fancier than a date, verify
00793             # the extra fields have been zeroed out
00794                 self.assertEqual(fromord.hour, 0)
00795                 self.assertEqual(fromord.minute, 0)
00796                 self.assertEqual(fromord.second, 0)
00797                 self.assertEqual(fromord.microsecond, 0)
00798 
00799         # Check first and last days of year spottily across the whole
00800         # range of years supported.
00801         for year in range(MINYEAR, MAXYEAR+1, 7):
00802             # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
00803             d = self.theclass(year, 1, 1)
00804             n = d.toordinal()
00805             d2 = self.theclass.fromordinal(n)
00806             self.assertEqual(d, d2)
00807             # Verify that moving back a day gets to the end of year-1.
00808             if year > 1:
00809                 d = self.theclass.fromordinal(n-1)
00810                 d2 = self.theclass(year-1, 12, 31)
00811                 self.assertEqual(d, d2)
00812                 self.assertEqual(d2.toordinal(), n-1)
00813 
00814         # Test every day in a leap-year and a non-leap year.
00815         dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
00816         for year, isleap in (2000, True), (2002, False):
00817             n = self.theclass(year, 1, 1).toordinal()
00818             for month, maxday in zip(range(1, 13), dim):
00819                 if month == 2 and isleap:
00820                     maxday += 1
00821                 for day in range(1, maxday+1):
00822                     d = self.theclass(year, month, day)
00823                     self.assertEqual(d.toordinal(), n)
00824                     self.assertEqual(d, self.theclass.fromordinal(n))
00825                     n += 1
00826 
00827     def test_extreme_ordinals(self):
00828         a = self.theclass.min
00829         a = self.theclass(a.year, a.month, a.day)  # get rid of time parts
00830         aord = a.toordinal()
00831         b = a.fromordinal(aord)
00832         self.assertEqual(a, b)
00833 
00834         self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
00835 
00836         b = a + timedelta(days=1)
00837         self.assertEqual(b.toordinal(), aord + 1)
00838         self.assertEqual(b, self.theclass.fromordinal(aord + 1))
00839 
00840         a = self.theclass.max
00841         a = self.theclass(a.year, a.month, a.day)  # get rid of time parts
00842         aord = a.toordinal()
00843         b = a.fromordinal(aord)
00844         self.assertEqual(a, b)
00845 
00846         self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
00847 
00848         b = a - timedelta(days=1)
00849         self.assertEqual(b.toordinal(), aord - 1)
00850         self.assertEqual(b, self.theclass.fromordinal(aord - 1))
00851 
00852     def test_bad_constructor_arguments(self):
00853         # bad years
00854         self.theclass(MINYEAR, 1, 1)  # no exception
00855         self.theclass(MAXYEAR, 1, 1)  # no exception
00856         self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
00857         self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
00858         # bad months
00859         self.theclass(2000, 1, 1)    # no exception
00860         self.theclass(2000, 12, 1)   # no exception
00861         self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
00862         self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
00863         # bad days
00864         self.theclass(2000, 2, 29)   # no exception
00865         self.theclass(2004, 2, 29)   # no exception
00866         self.theclass(2400, 2, 29)   # no exception
00867         self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
00868         self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
00869         self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
00870         self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
00871         self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
00872         self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
00873 
00874     def test_hash_equality(self):
00875         d = self.theclass(2000, 12, 31)
00876         # same thing
00877         e = self.theclass(2000, 12, 31)
00878         self.assertEqual(d, e)
00879         self.assertEqual(hash(d), hash(e))
00880 
00881         dic = {d: 1}
00882         dic[e] = 2
00883         self.assertEqual(len(dic), 1)
00884         self.assertEqual(dic[d], 2)
00885         self.assertEqual(dic[e], 2)
00886 
00887         d = self.theclass(2001,  1,  1)
00888         # same thing
00889         e = self.theclass(2001,  1,  1)
00890         self.assertEqual(d, e)
00891         self.assertEqual(hash(d), hash(e))
00892 
00893         dic = {d: 1}
00894         dic[e] = 2
00895         self.assertEqual(len(dic), 1)
00896         self.assertEqual(dic[d], 2)
00897         self.assertEqual(dic[e], 2)
00898 
00899     def test_computations(self):
00900         a = self.theclass(2002, 1, 31)
00901         b = self.theclass(1956, 1, 31)
00902         c = self.theclass(2001,2,1)
00903 
00904         diff = a-b
00905         self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
00906         self.assertEqual(diff.seconds, 0)
00907         self.assertEqual(diff.microseconds, 0)
00908 
00909         day = timedelta(1)
00910         week = timedelta(7)
00911         a = self.theclass(2002, 3, 2)
00912         self.assertEqual(a + day, self.theclass(2002, 3, 3))
00913         self.assertEqual(day + a, self.theclass(2002, 3, 3))
00914         self.assertEqual(a - day, self.theclass(2002, 3, 1))
00915         self.assertEqual(-day + a, self.theclass(2002, 3, 1))
00916         self.assertEqual(a + week, self.theclass(2002, 3, 9))
00917         self.assertEqual(a - week, self.theclass(2002, 2, 23))
00918         self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
00919         self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
00920         self.assertEqual((a + week) - a, week)
00921         self.assertEqual((a + day) - a, day)
00922         self.assertEqual((a - week) - a, -week)
00923         self.assertEqual((a - day) - a, -day)
00924         self.assertEqual(a - (a + week), -week)
00925         self.assertEqual(a - (a + day), -day)
00926         self.assertEqual(a - (a - week), week)
00927         self.assertEqual(a - (a - day), day)
00928         self.assertEqual(c - (c - day), day)
00929 
00930         # Add/sub ints or floats should be illegal
00931         for i in 1, 1.0:
00932             self.assertRaises(TypeError, lambda: a+i)
00933             self.assertRaises(TypeError, lambda: a-i)
00934             self.assertRaises(TypeError, lambda: i+a)
00935             self.assertRaises(TypeError, lambda: i-a)
00936 
00937         # delta - date is senseless.
00938         self.assertRaises(TypeError, lambda: day - a)
00939         # mixing date and (delta or date) via * or // is senseless
00940         self.assertRaises(TypeError, lambda: day * a)
00941         self.assertRaises(TypeError, lambda: a * day)
00942         self.assertRaises(TypeError, lambda: day // a)
00943         self.assertRaises(TypeError, lambda: a // day)
00944         self.assertRaises(TypeError, lambda: a * a)
00945         self.assertRaises(TypeError, lambda: a // a)
00946         # date + date is senseless
00947         self.assertRaises(TypeError, lambda: a + a)
00948 
00949     def test_overflow(self):
00950         tiny = self.theclass.resolution
00951 
00952         for delta in [tiny, timedelta(1), timedelta(2)]:
00953             dt = self.theclass.min + delta
00954             dt -= delta  # no problem
00955             self.assertRaises(OverflowError, dt.__sub__, delta)
00956             self.assertRaises(OverflowError, dt.__add__, -delta)
00957 
00958             dt = self.theclass.max - delta
00959             dt += delta  # no problem
00960             self.assertRaises(OverflowError, dt.__add__, delta)
00961             self.assertRaises(OverflowError, dt.__sub__, -delta)
00962 
00963     def test_fromtimestamp(self):
00964         import time
00965 
00966         # Try an arbitrary fixed value.
00967         year, month, day = 1999, 9, 19
00968         ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
00969         d = self.theclass.fromtimestamp(ts)
00970         self.assertEqual(d.year, year)
00971         self.assertEqual(d.month, month)
00972         self.assertEqual(d.day, day)
00973 
00974     def test_insane_fromtimestamp(self):
00975         # It's possible that some platform maps time_t to double,
00976         # and that this test will fail there.  This test should
00977         # exempt such platforms (provided they return reasonable
00978         # results!).
00979         for insane in -1e200, 1e200:
00980             self.assertRaises(ValueError, self.theclass.fromtimestamp,
00981                               insane)
00982 
00983     def test_today(self):
00984         import time
00985 
00986         # We claim that today() is like fromtimestamp(time.time()), so
00987         # prove it.
00988         for dummy in range(3):
00989             today = self.theclass.today()
00990             ts = time.time()
00991             todayagain = self.theclass.fromtimestamp(ts)
00992             if today == todayagain:
00993                 break
00994             # There are several legit reasons that could fail:
00995             # 1. It recently became midnight, between the today() and the
00996             #    time() calls.
00997             # 2. The platform time() has such fine resolution that we'll
00998             #    never get the same value twice.
00999             # 3. The platform time() has poor resolution, and we just
01000             #    happened to call today() right before a resolution quantum
01001             #    boundary.
01002             # 4. The system clock got fiddled between calls.
01003             # In any case, wait a little while and try again.
01004             time.sleep(0.1)
01005 
01006         # It worked or it didn't.  If it didn't, assume it's reason #2, and
01007         # let the test pass if they're within half a second of each other.
01008         self.assertTrue(today == todayagain or
01009                         abs(todayagain - today) < timedelta(seconds=0.5))
01010 
01011     def test_weekday(self):
01012         for i in range(7):
01013             # March 4, 2002 is a Monday
01014             self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
01015             self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
01016             # January 2, 1956 is a Monday
01017             self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
01018             self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
01019 
01020     def test_isocalendar(self):
01021         # Check examples from
01022         # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
01023         for i in range(7):
01024             d = self.theclass(2003, 12, 22+i)
01025             self.assertEqual(d.isocalendar(), (2003, 52, i+1))
01026             d = self.theclass(2003, 12, 29) + timedelta(i)
01027             self.assertEqual(d.isocalendar(), (2004, 1, i+1))
01028             d = self.theclass(2004, 1, 5+i)
01029             self.assertEqual(d.isocalendar(), (2004, 2, i+1))
01030             d = self.theclass(2009, 12, 21+i)
01031             self.assertEqual(d.isocalendar(), (2009, 52, i+1))
01032             d = self.theclass(2009, 12, 28) + timedelta(i)
01033             self.assertEqual(d.isocalendar(), (2009, 53, i+1))
01034             d = self.theclass(2010, 1, 4+i)
01035             self.assertEqual(d.isocalendar(), (2010, 1, i+1))
01036 
01037     def test_iso_long_years(self):
01038         # Calculate long ISO years and compare to table from
01039         # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
01040         ISO_LONG_YEARS_TABLE = """
01041               4   32   60   88
01042               9   37   65   93
01043              15   43   71   99
01044              20   48   76
01045              26   54   82
01046 
01047             105  133  161  189
01048             111  139  167  195
01049             116  144  172
01050             122  150  178
01051             128  156  184
01052 
01053             201  229  257  285
01054             207  235  263  291
01055             212  240  268  296
01056             218  246  274
01057             224  252  280
01058 
01059             303  331  359  387
01060             308  336  364  392
01061             314  342  370  398
01062             320  348  376
01063             325  353  381
01064         """
01065         iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
01066         L = []
01067         for i in range(400):
01068             d = self.theclass(2000+i, 12, 31)
01069             d1 = self.theclass(1600+i, 12, 31)
01070             self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
01071             if d.isocalendar()[1] == 53:
01072                 L.append(i)
01073         self.assertEqual(L, iso_long_years)
01074 
01075     def test_isoformat(self):
01076         t = self.theclass(2, 3, 2)
01077         self.assertEqual(t.isoformat(), "0002-03-02")
01078 
01079     def test_ctime(self):
01080         t = self.theclass(2002, 3, 2)
01081         self.assertEqual(t.ctime(), "Sat Mar  2 00:00:00 2002")
01082 
01083     def test_strftime(self):
01084         t = self.theclass(2005, 3, 2)
01085         self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
01086         self.assertEqual(t.strftime(""), "") # SF bug #761337
01087         self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
01088 
01089         self.assertRaises(TypeError, t.strftime) # needs an arg
01090         self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
01091         self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
01092 
01093         # test that unicode input is allowed (issue 2782)
01094         self.assertEqual(t.strftime("%m"), "03")
01095 
01096         # A naive object replaces %z and %Z w/ empty strings.
01097         self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
01098 
01099         #make sure that invalid format specifiers are handled correctly
01100         #self.assertRaises(ValueError, t.strftime, "%e")
01101         #self.assertRaises(ValueError, t.strftime, "%")
01102         #self.assertRaises(ValueError, t.strftime, "%#")
01103 
01104         #oh well, some systems just ignore those invalid ones.
01105         #at least, excercise them to make sure that no crashes
01106         #are generated
01107         for f in ["%e", "%", "%#"]:
01108             try:
01109                 t.strftime(f)
01110             except ValueError:
01111                 pass
01112 
01113         #check that this standard extension works
01114         t.strftime("%f")
01115 
01116 
01117     def test_format(self):
01118         dt = self.theclass(2007, 9, 10)
01119         self.assertEqual(dt.__format__(''), str(dt))
01120 
01121         # check that a derived class's __str__() gets called
01122         class A(self.theclass):
01123             def __str__(self):
01124                 return 'A'
01125         a = A(2007, 9, 10)
01126         self.assertEqual(a.__format__(''), 'A')
01127 
01128         # check that a derived class's strftime gets called
01129         class B(self.theclass):
01130             def strftime(self, format_spec):
01131                 return 'B'
01132         b = B(2007, 9, 10)
01133         self.assertEqual(b.__format__(''), str(dt))
01134 
01135         for fmt in ["m:%m d:%d y:%y",
01136                     "m:%m d:%d y:%y H:%H M:%M S:%S",
01137                     "%z %Z",
01138                     ]:
01139             self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
01140             self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
01141             self.assertEqual(b.__format__(fmt), 'B')
01142 
01143     def test_resolution_info(self):
01144         # XXX: Should min and max respect subclassing?
01145         if issubclass(self.theclass, datetime):
01146             expected_class = datetime
01147         else:
01148             expected_class = date
01149         self.assertIsInstance(self.theclass.min, expected_class)
01150         self.assertIsInstance(self.theclass.max, expected_class)
01151         self.assertIsInstance(self.theclass.resolution, timedelta)
01152         self.assertTrue(self.theclass.max > self.theclass.min)
01153 
01154     def test_extreme_timedelta(self):
01155         big = self.theclass.max - self.theclass.min
01156         # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
01157         n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
01158         # n == 315537897599999999 ~= 2**58.13
01159         justasbig = timedelta(0, 0, n)
01160         self.assertEqual(big, justasbig)
01161         self.assertEqual(self.theclass.min + big, self.theclass.max)
01162         self.assertEqual(self.theclass.max - big, self.theclass.min)
01163 
01164     def test_timetuple(self):
01165         for i in range(7):
01166             # January 2, 1956 is a Monday (0)
01167             d = self.theclass(1956, 1, 2+i)
01168             t = d.timetuple()
01169             self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
01170             # February 1, 1956 is a Wednesday (2)
01171             d = self.theclass(1956, 2, 1+i)
01172             t = d.timetuple()
01173             self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
01174             # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
01175             # of the year.
01176             d = self.theclass(1956, 3, 1+i)
01177             t = d.timetuple()
01178             self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
01179             self.assertEqual(t.tm_year, 1956)
01180             self.assertEqual(t.tm_mon, 3)
01181             self.assertEqual(t.tm_mday, 1+i)
01182             self.assertEqual(t.tm_hour, 0)
01183             self.assertEqual(t.tm_min, 0)
01184             self.assertEqual(t.tm_sec, 0)
01185             self.assertEqual(t.tm_wday, (3+i)%7)
01186             self.assertEqual(t.tm_yday, 61+i)
01187             self.assertEqual(t.tm_isdst, -1)
01188 
01189     def test_pickling(self):
01190         args = 6, 7, 23
01191         orig = self.theclass(*args)
01192         for pickler, unpickler, proto in pickle_choices:
01193             green = pickler.dumps(orig, proto)
01194             derived = unpickler.loads(green)
01195             self.assertEqual(orig, derived)
01196 
01197     def test_compare(self):
01198         t1 = self.theclass(2, 3, 4)
01199         t2 = self.theclass(2, 3, 4)
01200         self.assertEqual(t1, t2)
01201         self.assertTrue(t1 <= t2)
01202         self.assertTrue(t1 >= t2)
01203         self.assertTrue(not t1 != t2)
01204         self.assertTrue(not t1 < t2)
01205         self.assertTrue(not t1 > t2)
01206 
01207         for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
01208             t2 = self.theclass(*args)   # this is larger than t1
01209             self.assertTrue(t1 < t2)
01210             self.assertTrue(t2 > t1)
01211             self.assertTrue(t1 <= t2)
01212             self.assertTrue(t2 >= t1)
01213             self.assertTrue(t1 != t2)
01214             self.assertTrue(t2 != t1)
01215             self.assertTrue(not t1 == t2)
01216             self.assertTrue(not t2 == t1)
01217             self.assertTrue(not t1 > t2)
01218             self.assertTrue(not t2 < t1)
01219             self.assertTrue(not t1 >= t2)
01220             self.assertTrue(not t2 <= t1)
01221 
01222         for badarg in OTHERSTUFF:
01223             self.assertEqual(t1 == badarg, False)
01224             self.assertEqual(t1 != badarg, True)
01225             self.assertEqual(badarg == t1, False)
01226             self.assertEqual(badarg != t1, True)
01227 
01228             self.assertRaises(TypeError, lambda: t1 < badarg)
01229             self.assertRaises(TypeError, lambda: t1 > badarg)
01230             self.assertRaises(TypeError, lambda: t1 >= badarg)
01231             self.assertRaises(TypeError, lambda: badarg <= t1)
01232             self.assertRaises(TypeError, lambda: badarg < t1)
01233             self.assertRaises(TypeError, lambda: badarg > t1)
01234             self.assertRaises(TypeError, lambda: badarg >= t1)
01235 
01236     def test_mixed_compare(self):
01237         our = self.theclass(2000, 4, 5)
01238 
01239         # Our class can be compared for equality to other classes
01240         self.assertEqual(our == 1, False)
01241         self.assertEqual(1 == our, False)
01242         self.assertEqual(our != 1, True)
01243         self.assertEqual(1 != our, True)
01244 
01245         # But the ordering is undefined
01246         self.assertRaises(TypeError, lambda: our < 1)
01247         self.assertRaises(TypeError, lambda: 1 < our)
01248 
01249         # Repeat those tests with a different class
01250 
01251         class SomeClass:
01252             pass
01253 
01254         their = SomeClass()
01255         self.assertEqual(our == their, False)
01256         self.assertEqual(their == our, False)
01257         self.assertEqual(our != their, True)
01258         self.assertEqual(their != our, True)
01259         self.assertRaises(TypeError, lambda: our < their)
01260         self.assertRaises(TypeError, lambda: their < our)
01261 
01262         # However, if the other class explicitly defines ordering
01263         # relative to our class, it is allowed to do so
01264 
01265         class LargerThanAnything:
01266             def __lt__(self, other):
01267                 return False
01268             def __le__(self, other):
01269                 return isinstance(other, LargerThanAnything)
01270             def __eq__(self, other):
01271                 return isinstance(other, LargerThanAnything)
01272             def __ne__(self, other):
01273                 return not isinstance(other, LargerThanAnything)
01274             def __gt__(self, other):
01275                 return not isinstance(other, LargerThanAnything)
01276             def __ge__(self, other):
01277                 return True
01278 
01279         their = LargerThanAnything()
01280         self.assertEqual(our == their, False)
01281         self.assertEqual(their == our, False)
01282         self.assertEqual(our != their, True)
01283         self.assertEqual(their != our, True)
01284         self.assertEqual(our < their, True)
01285         self.assertEqual(their < our, False)
01286 
01287     def test_bool(self):
01288         # All dates are considered true.
01289         self.assertTrue(self.theclass.min)
01290         self.assertTrue(self.theclass.max)
01291 
01292     def test_strftime_out_of_range(self):
01293         # For nasty technical reasons, we can't handle years before 1000.
01294         cls = self.theclass
01295         self.assertEqual(cls(1000, 1, 1).strftime("%Y"), "1000")
01296         for y in 1, 49, 51, 99, 100, 999:
01297             self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
01298 
01299     def test_replace(self):
01300         cls = self.theclass
01301         args = [1, 2, 3]
01302         base = cls(*args)
01303         self.assertEqual(base, base.replace())
01304 
01305         i = 0
01306         for name, newval in (("year", 2),
01307                              ("month", 3),
01308                              ("day", 4)):
01309             newargs = args[:]
01310             newargs[i] = newval
01311             expected = cls(*newargs)
01312             got = base.replace(**{name: newval})
01313             self.assertEqual(expected, got)
01314             i += 1
01315 
01316         # Out of bounds.
01317         base = cls(2000, 2, 29)
01318         self.assertRaises(ValueError, base.replace, year=2001)
01319 
01320     def test_subclass_date(self):
01321 
01322         class C(self.theclass):
01323             theAnswer = 42
01324 
01325             def __new__(cls, *args, **kws):
01326                 temp = kws.copy()
01327                 extra = temp.pop('extra')
01328                 result = self.theclass.__new__(cls, *args, **temp)
01329                 result.extra = extra
01330                 return result
01331 
01332             def newmeth(self, start):
01333                 return start + self.year + self.month
01334 
01335         args = 2003, 4, 14
01336 
01337         dt1 = self.theclass(*args)
01338         dt2 = C(*args, **{'extra': 7})
01339 
01340         self.assertEqual(dt2.__class__, C)
01341         self.assertEqual(dt2.theAnswer, 42)
01342         self.assertEqual(dt2.extra, 7)
01343         self.assertEqual(dt1.toordinal(), dt2.toordinal())
01344         self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
01345 
01346     def test_pickling_subclass_date(self):
01347 
01348         args = 6, 7, 23
01349         orig = SubclassDate(*args)
01350         for pickler, unpickler, proto in pickle_choices:
01351             green = pickler.dumps(orig, proto)
01352             derived = unpickler.loads(green)
01353             self.assertEqual(orig, derived)
01354 
01355     def test_backdoor_resistance(self):
01356         # For fast unpickling, the constructor accepts a pickle byte string.
01357         # This is a low-overhead backdoor.  A user can (by intent or
01358         # mistake) pass a string directly, which (if it's the right length)
01359         # will get treated like a pickle, and bypass the normal sanity
01360         # checks in the constructor.  This can create insane objects.
01361         # The constructor doesn't want to burn the time to validate all
01362         # fields, but does check the month field.  This stops, e.g.,
01363         # datetime.datetime('1995-03-25') from yielding an insane object.
01364         base = b'1995-03-25'
01365         if not issubclass(self.theclass, datetime):
01366             base = base[:4]
01367         for month_byte in b'9', b'\0', b'\r', b'\xff':
01368             self.assertRaises(TypeError, self.theclass,
01369                                          base[:2] + month_byte + base[3:])
01370         # Good bytes, but bad tzinfo:
01371         self.assertRaises(TypeError, self.theclass,
01372                           bytes([1] * len(base)), 'EST')
01373 
01374         for ord_byte in range(1, 13):
01375             # This shouldn't blow up because of the month byte alone.  If
01376             # the implementation changes to do more-careful checking, it may
01377             # blow up because other fields are insane.
01378             self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
01379 
01380 #############################################################################
01381 # datetime tests
01382 
01383 class SubclassDatetime(datetime):
01384     sub_var = 1
01385 
01386 class TestDateTime(TestDate):
01387 
01388     theclass = datetime
01389 
01390     def test_basic_attributes(self):
01391         dt = self.theclass(2002, 3, 1, 12, 0)
01392         self.assertEqual(dt.year, 2002)
01393         self.assertEqual(dt.month, 3)
01394         self.assertEqual(dt.day, 1)
01395         self.assertEqual(dt.hour, 12)
01396         self.assertEqual(dt.minute, 0)
01397         self.assertEqual(dt.second, 0)
01398         self.assertEqual(dt.microsecond, 0)
01399 
01400     def test_basic_attributes_nonzero(self):
01401         # Make sure all attributes are non-zero so bugs in
01402         # bit-shifting access show up.
01403         dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
01404         self.assertEqual(dt.year, 2002)
01405         self.assertEqual(dt.month, 3)
01406         self.assertEqual(dt.day, 1)
01407         self.assertEqual(dt.hour, 12)
01408         self.assertEqual(dt.minute, 59)
01409         self.assertEqual(dt.second, 59)
01410         self.assertEqual(dt.microsecond, 8000)
01411 
01412     def test_roundtrip(self):
01413         for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
01414                    self.theclass.now()):
01415             # Verify dt -> string -> datetime identity.
01416             s = repr(dt)
01417             self.assertTrue(s.startswith('datetime.'))
01418             s = s[9:]
01419             dt2 = eval(s)
01420             self.assertEqual(dt, dt2)
01421 
01422             # Verify identity via reconstructing from pieces.
01423             dt2 = self.theclass(dt.year, dt.month, dt.day,
01424                                 dt.hour, dt.minute, dt.second,
01425                                 dt.microsecond)
01426             self.assertEqual(dt, dt2)
01427 
01428     def test_isoformat(self):
01429         t = self.theclass(2, 3, 2, 4, 5, 1, 123)
01430         self.assertEqual(t.isoformat(),    "0002-03-02T04:05:01.000123")
01431         self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
01432         self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
01433         self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
01434         # str is ISO format with the separator forced to a blank.
01435         self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
01436 
01437         t = self.theclass(2, 3, 2)
01438         self.assertEqual(t.isoformat(),    "0002-03-02T00:00:00")
01439         self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
01440         self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
01441         # str is ISO format with the separator forced to a blank.
01442         self.assertEqual(str(t), "0002-03-02 00:00:00")
01443 
01444     def test_format(self):
01445         dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
01446         self.assertEqual(dt.__format__(''), str(dt))
01447 
01448         # check that a derived class's __str__() gets called
01449         class A(self.theclass):
01450             def __str__(self):
01451                 return 'A'
01452         a = A(2007, 9, 10, 4, 5, 1, 123)
01453         self.assertEqual(a.__format__(''), 'A')
01454 
01455         # check that a derived class's strftime gets called
01456         class B(self.theclass):
01457             def strftime(self, format_spec):
01458                 return 'B'
01459         b = B(2007, 9, 10, 4, 5, 1, 123)
01460         self.assertEqual(b.__format__(''), str(dt))
01461 
01462         for fmt in ["m:%m d:%d y:%y",
01463                     "m:%m d:%d y:%y H:%H M:%M S:%S",
01464                     "%z %Z",
01465                     ]:
01466             self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
01467             self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
01468             self.assertEqual(b.__format__(fmt), 'B')
01469 
01470     def test_more_ctime(self):
01471         # Test fields that TestDate doesn't touch.
01472         import time
01473 
01474         t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
01475         self.assertEqual(t.ctime(), "Sat Mar  2 18:03:05 2002")
01476         # Oops!  The next line fails on Win2K under MSVC 6, so it's commented
01477         # out.  The difference is that t.ctime() produces " 2" for the day,
01478         # but platform ctime() produces "02" for the day.  According to
01479         # C99, t.ctime() is correct here.
01480         # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
01481 
01482         # So test a case where that difference doesn't matter.
01483         t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
01484         self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
01485 
01486     def test_tz_independent_comparing(self):
01487         dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
01488         dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
01489         dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
01490         self.assertEqual(dt1, dt3)
01491         self.assertTrue(dt2 > dt3)
01492 
01493         # Make sure comparison doesn't forget microseconds, and isn't done
01494         # via comparing a float timestamp (an IEEE double doesn't have enough
01495         # precision to span microsecond resolution across years 1 thru 9999,
01496         # so comparing via timestamp necessarily calls some distinct values
01497         # equal).
01498         dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
01499         us = timedelta(microseconds=1)
01500         dt2 = dt1 + us
01501         self.assertEqual(dt2 - dt1, us)
01502         self.assertTrue(dt1 < dt2)
01503 
01504     def test_strftime_with_bad_tzname_replace(self):
01505         # verify ok if tzinfo.tzname().replace() returns a non-string
01506         class MyTzInfo(FixedOffset):
01507             def tzname(self, dt):
01508                 class MyStr(str):
01509                     def replace(self, *args):
01510                         return None
01511                 return MyStr('name')
01512         t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
01513         self.assertRaises(TypeError, t.strftime, '%Z')
01514 
01515     def test_bad_constructor_arguments(self):
01516         # bad years
01517         self.theclass(MINYEAR, 1, 1)  # no exception
01518         self.theclass(MAXYEAR, 1, 1)  # no exception
01519         self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
01520         self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
01521         # bad months
01522         self.theclass(2000, 1, 1)    # no exception
01523         self.theclass(2000, 12, 1)   # no exception
01524         self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
01525         self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
01526         # bad days
01527         self.theclass(2000, 2, 29)   # no exception
01528         self.theclass(2004, 2, 29)   # no exception
01529         self.theclass(2400, 2, 29)   # no exception
01530         self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
01531         self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
01532         self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
01533         self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
01534         self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
01535         self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
01536         # bad hours
01537         self.theclass(2000, 1, 31, 0)    # no exception
01538         self.theclass(2000, 1, 31, 23)   # no exception
01539         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
01540         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
01541         # bad minutes
01542         self.theclass(2000, 1, 31, 23, 0)    # no exception
01543         self.theclass(2000, 1, 31, 23, 59)   # no exception
01544         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
01545         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
01546         # bad seconds
01547         self.theclass(2000, 1, 31, 23, 59, 0)    # no exception
01548         self.theclass(2000, 1, 31, 23, 59, 59)   # no exception
01549         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
01550         self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
01551         # bad microseconds
01552         self.theclass(2000, 1, 31, 23, 59, 59, 0)    # no exception
01553         self.theclass(2000, 1, 31, 23, 59, 59, 999999)   # no exception
01554         self.assertRaises(ValueError, self.theclass,
01555                           2000, 1, 31, 23, 59, 59, -1)
01556         self.assertRaises(ValueError, self.theclass,
01557                           2000, 1, 31, 23, 59, 59,
01558                           1000000)
01559 
01560     def test_hash_equality(self):
01561         d = self.theclass(2000, 12, 31, 23, 30, 17)
01562         e = self.theclass(2000, 12, 31, 23, 30, 17)
01563         self.assertEqual(d, e)
01564         self.assertEqual(hash(d), hash(e))
01565 
01566         dic = {d: 1}
01567         dic[e] = 2
01568         self.assertEqual(len(dic), 1)
01569         self.assertEqual(dic[d], 2)
01570         self.assertEqual(dic[e], 2)
01571 
01572         d = self.theclass(2001,  1,  1,  0,  5, 17)
01573         e = self.theclass(2001,  1,  1,  0,  5, 17)
01574         self.assertEqual(d, e)
01575         self.assertEqual(hash(d), hash(e))
01576 
01577         dic = {d: 1}
01578         dic[e] = 2
01579         self.assertEqual(len(dic), 1)
01580         self.assertEqual(dic[d], 2)
01581         self.assertEqual(dic[e], 2)
01582 
01583     def test_computations(self):
01584         a = self.theclass(2002, 1, 31)
01585         b = self.theclass(1956, 1, 31)
01586         diff = a-b
01587         self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
01588         self.assertEqual(diff.seconds, 0)
01589         self.assertEqual(diff.microseconds, 0)
01590         a = self.theclass(2002, 3, 2, 17, 6)
01591         millisec = timedelta(0, 0, 1000)
01592         hour = timedelta(0, 3600)
01593         day = timedelta(1)
01594         week = timedelta(7)
01595         self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
01596         self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
01597         self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
01598         self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
01599         self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
01600         self.assertEqual(a - hour, a + -hour)
01601         self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
01602         self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
01603         self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
01604         self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
01605         self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
01606         self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
01607         self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
01608         self.assertEqual((a + week) - a, week)
01609         self.assertEqual((a + day) - a, day)
01610         self.assertEqual((a + hour) - a, hour)
01611         self.assertEqual((a + millisec) - a, millisec)
01612         self.assertEqual((a - week) - a, -week)
01613         self.assertEqual((a - day) - a, -day)
01614         self.assertEqual((a - hour) - a, -hour)
01615         self.assertEqual((a - millisec) - a, -millisec)
01616         self.assertEqual(a - (a + week), -week)
01617         self.assertEqual(a - (a + day), -day)
01618         self.assertEqual(a - (a + hour), -hour)
01619         self.assertEqual(a - (a + millisec), -millisec)
01620         self.assertEqual(a - (a - week), week)
01621         self.assertEqual(a - (a - day), day)
01622         self.assertEqual(a - (a - hour), hour)
01623         self.assertEqual(a - (a - millisec), millisec)
01624         self.assertEqual(a + (week + day + hour + millisec),
01625                          self.theclass(2002, 3, 10, 18, 6, 0, 1000))
01626         self.assertEqual(a + (week + day + hour + millisec),
01627                          (((a + week) + day) + hour) + millisec)
01628         self.assertEqual(a - (week + day + hour + millisec),
01629                          self.theclass(2002, 2, 22, 16, 5, 59, 999000))
01630         self.assertEqual(a - (week + day + hour + millisec),
01631                          (((a - week) - day) - hour) - millisec)
01632         # Add/sub ints or floats should be illegal
01633         for i in 1, 1.0:
01634             self.assertRaises(TypeError, lambda: a+i)
01635             self.assertRaises(TypeError, lambda: a-i)
01636             self.assertRaises(TypeError, lambda: i+a)
01637             self.assertRaises(TypeError, lambda: i-a)
01638 
01639         # delta - datetime is senseless.
01640         self.assertRaises(TypeError, lambda: day - a)
01641         # mixing datetime and (delta or datetime) via * or // is senseless
01642         self.assertRaises(TypeError, lambda: day * a)
01643         self.assertRaises(TypeError, lambda: a * day)
01644         self.assertRaises(TypeError, lambda: day // a)
01645         self.assertRaises(TypeError, lambda: a // day)
01646         self.assertRaises(TypeError, lambda: a * a)
01647         self.assertRaises(TypeError, lambda: a // a)
01648         # datetime + datetime is senseless
01649         self.assertRaises(TypeError, lambda: a + a)
01650 
01651     def test_pickling(self):
01652         args = 6, 7, 23, 20, 59, 1, 64**2
01653         orig = self.theclass(*args)
01654         for pickler, unpickler, proto in pickle_choices:
01655             green = pickler.dumps(orig, proto)
01656             derived = unpickler.loads(green)
01657             self.assertEqual(orig, derived)
01658 
01659     def test_more_pickling(self):
01660         a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
01661         s = pickle.dumps(a)
01662         b = pickle.loads(s)
01663         self.assertEqual(b.year, 2003)
01664         self.assertEqual(b.month, 2)
01665         self.assertEqual(b.day, 7)
01666 
01667     def test_pickling_subclass_datetime(self):
01668         args = 6, 7, 23, 20, 59, 1, 64**2
01669         orig = SubclassDatetime(*args)
01670         for pickler, unpickler, proto in pickle_choices:
01671             green = pickler.dumps(orig, proto)
01672             derived = unpickler.loads(green)
01673             self.assertEqual(orig, derived)
01674 
01675     def test_more_compare(self):
01676         # The test_compare() inherited from TestDate covers the error cases.
01677         # We just want to test lexicographic ordering on the members datetime
01678         # has that date lacks.
01679         args = [2000, 11, 29, 20, 58, 16, 999998]
01680         t1 = self.theclass(*args)
01681         t2 = self.theclass(*args)
01682         self.assertEqual(t1, t2)
01683         self.assertTrue(t1 <= t2)
01684         self.assertTrue(t1 >= t2)
01685         self.assertTrue(not t1 != t2)
01686         self.assertTrue(not t1 < t2)
01687         self.assertTrue(not t1 > t2)
01688 
01689         for i in range(len(args)):
01690             newargs = args[:]
01691             newargs[i] = args[i] + 1
01692             t2 = self.theclass(*newargs)   # this is larger than t1
01693             self.assertTrue(t1 < t2)
01694             self.assertTrue(t2 > t1)
01695             self.assertTrue(t1 <= t2)
01696             self.assertTrue(t2 >= t1)
01697             self.assertTrue(t1 != t2)
01698             self.assertTrue(t2 != t1)
01699             self.assertTrue(not t1 == t2)
01700             self.assertTrue(not t2 == t1)
01701             self.assertTrue(not t1 > t2)
01702             self.assertTrue(not t2 < t1)
01703             self.assertTrue(not t1 >= t2)
01704             self.assertTrue(not t2 <= t1)
01705 
01706 
01707     # A helper for timestamp constructor tests.
01708     def verify_field_equality(self, expected, got):
01709         self.assertEqual(expected.tm_year, got.year)
01710         self.assertEqual(expected.tm_mon, got.month)
01711         self.assertEqual(expected.tm_mday, got.day)
01712         self.assertEqual(expected.tm_hour, got.hour)
01713         self.assertEqual(expected.tm_min, got.minute)
01714         self.assertEqual(expected.tm_sec, got.second)
01715 
01716     def test_fromtimestamp(self):
01717         import time
01718 
01719         ts = time.time()
01720         expected = time.localtime(ts)
01721         got = self.theclass.fromtimestamp(ts)
01722         self.verify_field_equality(expected, got)
01723 
01724     def test_utcfromtimestamp(self):
01725         import time
01726 
01727         ts = time.time()
01728         expected = time.gmtime(ts)
01729         got = self.theclass.utcfromtimestamp(ts)
01730         self.verify_field_equality(expected, got)
01731 
01732     def test_microsecond_rounding(self):
01733         # Test whether fromtimestamp "rounds up" floats that are less
01734         # than 1/2 microsecond smaller than an integer.
01735         for fts in [self.theclass.fromtimestamp,
01736                     self.theclass.utcfromtimestamp]:
01737             self.assertEqual(fts(0.9999999), fts(1))
01738             self.assertEqual(fts(0.99999949).microsecond, 999999)
01739 
01740     def test_insane_fromtimestamp(self):
01741         # It's possible that some platform maps time_t to double,
01742         # and that this test will fail there.  This test should
01743         # exempt such platforms (provided they return reasonable
01744         # results!).
01745         for insane in -1e200, 1e200:
01746             self.assertRaises(ValueError, self.theclass.fromtimestamp,
01747                               insane)
01748 
01749     def test_insane_utcfromtimestamp(self):
01750         # It's possible that some platform maps time_t to double,
01751         # and that this test will fail there.  This test should
01752         # exempt such platforms (provided they return reasonable
01753         # results!).
01754         for insane in -1e200, 1e200:
01755             self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
01756                               insane)
01757     @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
01758     def test_negative_float_fromtimestamp(self):
01759         # The result is tz-dependent; at least test that this doesn't
01760         # fail (like it did before bug 1646728 was fixed).
01761         self.theclass.fromtimestamp(-1.05)
01762 
01763     @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
01764     def test_negative_float_utcfromtimestamp(self):
01765         d = self.theclass.utcfromtimestamp(-1.05)
01766         self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
01767 
01768     def test_utcnow(self):
01769         import time
01770 
01771         # Call it a success if utcnow() and utcfromtimestamp() are within
01772         # a second of each other.
01773         tolerance = timedelta(seconds=1)
01774         for dummy in range(3):
01775             from_now = self.theclass.utcnow()
01776             from_timestamp = self.theclass.utcfromtimestamp(time.time())
01777             if abs(from_timestamp - from_now) <= tolerance:
01778                 break
01779             # Else try again a few times.
01780         self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
01781 
01782     def test_strptime(self):
01783         import _strptime
01784 
01785         string = '2004-12-01 13:02:47.197'
01786         format = '%Y-%m-%d %H:%M:%S.%f'
01787         expected = _strptime._strptime_datetime(self.theclass, string, format)
01788         got = self.theclass.strptime(string, format)
01789         self.assertEqual(expected, got)
01790         self.assertIs(type(expected), self.theclass)
01791         self.assertIs(type(got), self.theclass)
01792 
01793         strptime = self.theclass.strptime
01794         self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
01795         self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
01796         # Only local timezone and UTC are supported
01797         for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
01798                                  (-_time.timezone, _time.tzname[0])):
01799             if tzseconds < 0:
01800                 sign = '-'
01801                 seconds = -tzseconds
01802             else:
01803                 sign ='+'
01804                 seconds = tzseconds
01805             hours, minutes = divmod(seconds//60, 60)
01806             dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
01807             dt = strptime(dtstr, "%z %Z")
01808             self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
01809             self.assertEqual(dt.tzname(), tzname)
01810         # Can produce inconsistent datetime
01811         dtstr, fmt = "+1234 UTC", "%z %Z"
01812         dt = strptime(dtstr, fmt)
01813         self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
01814         self.assertEqual(dt.tzname(), 'UTC')
01815         # yet will roundtrip
01816         self.assertEqual(dt.strftime(fmt), dtstr)
01817 
01818         # Produce naive datetime if no %z is provided
01819         self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
01820 
01821         with self.assertRaises(ValueError): strptime("-2400", "%z")
01822         with self.assertRaises(ValueError): strptime("-000", "%z")
01823 
01824     def test_more_timetuple(self):
01825         # This tests fields beyond those tested by the TestDate.test_timetuple.
01826         t = self.theclass(2004, 12, 31, 6, 22, 33)
01827         self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
01828         self.assertEqual(t.timetuple(),
01829                          (t.year, t.month, t.day,
01830                           t.hour, t.minute, t.second,
01831                           t.weekday(),
01832                           t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
01833                           -1))
01834         tt = t.timetuple()
01835         self.assertEqual(tt.tm_year, t.year)
01836         self.assertEqual(tt.tm_mon, t.month)
01837         self.assertEqual(tt.tm_mday, t.day)
01838         self.assertEqual(tt.tm_hour, t.hour)
01839         self.assertEqual(tt.tm_min, t.minute)
01840         self.assertEqual(tt.tm_sec, t.second)
01841         self.assertEqual(tt.tm_wday, t.weekday())
01842         self.assertEqual(tt.tm_yday, t.toordinal() -
01843                                      date(t.year, 1, 1).toordinal() + 1)
01844         self.assertEqual(tt.tm_isdst, -1)
01845 
01846     def test_more_strftime(self):
01847         # This tests fields beyond those tested by the TestDate.test_strftime.
01848         t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
01849         self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
01850                                     "12 31 04 000047 33 22 06 366")
01851 
01852     def test_extract(self):
01853         dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
01854         self.assertEqual(dt.date(), date(2002, 3, 4))
01855         self.assertEqual(dt.time(), time(18, 45, 3, 1234))
01856 
01857     def test_combine(self):
01858         d = date(2002, 3, 4)
01859         t = time(18, 45, 3, 1234)
01860         expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
01861         combine = self.theclass.combine
01862         dt = combine(d, t)
01863         self.assertEqual(dt, expected)
01864 
01865         dt = combine(time=t, date=d)
01866         self.assertEqual(dt, expected)
01867 
01868         self.assertEqual(d, dt.date())
01869         self.assertEqual(t, dt.time())
01870         self.assertEqual(dt, combine(dt.date(), dt.time()))
01871 
01872         self.assertRaises(TypeError, combine) # need an arg
01873         self.assertRaises(TypeError, combine, d) # need two args
01874         self.assertRaises(TypeError, combine, t, d) # args reversed
01875         self.assertRaises(TypeError, combine, d, t, 1) # too many args
01876         self.assertRaises(TypeError, combine, "date", "time") # wrong types
01877         self.assertRaises(TypeError, combine, d, "time") # wrong type
01878         self.assertRaises(TypeError, combine, "date", t) # wrong type
01879 
01880     def test_replace(self):
01881         cls = self.theclass
01882         args = [1, 2, 3, 4, 5, 6, 7]
01883         base = cls(*args)
01884         self.assertEqual(base, base.replace())
01885 
01886         i = 0
01887         for name, newval in (("year", 2),
01888                              ("month", 3),
01889                              ("day", 4),
01890                              ("hour", 5),
01891                              ("minute", 6),
01892                              ("second", 7),
01893                              ("microsecond", 8)):
01894             newargs = args[:]
01895             newargs[i] = newval
01896             expected = cls(*newargs)
01897             got = base.replace(**{name: newval})
01898             self.assertEqual(expected, got)
01899             i += 1
01900 
01901         # Out of bounds.
01902         base = cls(2000, 2, 29)
01903         self.assertRaises(ValueError, base.replace, year=2001)
01904 
01905     def test_astimezone(self):
01906         # Pretty boring!  The TZ test is more interesting here.  astimezone()
01907         # simply can't be applied to a naive object.
01908         dt = self.theclass.now()
01909         f = FixedOffset(44, "")
01910         self.assertRaises(TypeError, dt.astimezone) # not enough args
01911         self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
01912         self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
01913         self.assertRaises(ValueError, dt.astimezone, f) # naive
01914         self.assertRaises(ValueError, dt.astimezone, tz=f)  # naive
01915 
01916         class Bogus(tzinfo):
01917             def utcoffset(self, dt): return None
01918             def dst(self, dt): return timedelta(0)
01919         bog = Bogus()
01920         self.assertRaises(ValueError, dt.astimezone, bog)   # naive
01921         self.assertRaises(ValueError,
01922                           dt.replace(tzinfo=bog).astimezone, f)
01923 
01924         class AlsoBogus(tzinfo):
01925             def utcoffset(self, dt): return timedelta(0)
01926             def dst(self, dt): return None
01927         alsobog = AlsoBogus()
01928         self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
01929 
01930     def test_subclass_datetime(self):
01931 
01932         class C(self.theclass):
01933             theAnswer = 42
01934 
01935             def __new__(cls, *args, **kws):
01936                 temp = kws.copy()
01937                 extra = temp.pop('extra')
01938                 result = self.theclass.__new__(cls, *args, **temp)
01939                 result.extra = extra
01940                 return result
01941 
01942             def newmeth(self, start):
01943                 return start + self.year + self.month + self.second
01944 
01945         args = 2003, 4, 14, 12, 13, 41
01946 
01947         dt1 = self.theclass(*args)
01948         dt2 = C(*args, **{'extra': 7})
01949 
01950         self.assertEqual(dt2.__class__, C)
01951         self.assertEqual(dt2.theAnswer, 42)
01952         self.assertEqual(dt2.extra, 7)
01953         self.assertEqual(dt1.toordinal(), dt2.toordinal())
01954         self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
01955                                           dt1.second - 7)
01956 
01957 class TestSubclassDateTime(TestDateTime):
01958     theclass = SubclassDatetime
01959     # Override tests not designed for subclass
01960     def test_roundtrip(self):
01961         pass
01962 
01963 class SubclassTime(time):
01964     sub_var = 1
01965 
01966 class TestTime(HarmlessMixedComparison, unittest.TestCase):
01967 
01968     theclass = time
01969 
01970     def test_basic_attributes(self):
01971         t = self.theclass(12, 0)
01972         self.assertEqual(t.hour, 12)
01973         self.assertEqual(t.minute, 0)
01974         self.assertEqual(t.second, 0)
01975         self.assertEqual(t.microsecond, 0)
01976 
01977     def test_basic_attributes_nonzero(self):
01978         # Make sure all attributes are non-zero so bugs in
01979         # bit-shifting access show up.
01980         t = self.theclass(12, 59, 59, 8000)
01981         self.assertEqual(t.hour, 12)
01982         self.assertEqual(t.minute, 59)
01983         self.assertEqual(t.second, 59)
01984         self.assertEqual(t.microsecond, 8000)
01985 
01986     def test_roundtrip(self):
01987         t = self.theclass(1, 2, 3, 4)
01988 
01989         # Verify t -> string -> time identity.
01990         s = repr(t)
01991         self.assertTrue(s.startswith('datetime.'))
01992         s = s[9:]
01993         t2 = eval(s)
01994         self.assertEqual(t, t2)
01995 
01996         # Verify identity via reconstructing from pieces.
01997         t2 = self.theclass(t.hour, t.minute, t.second,
01998                            t.microsecond)
01999         self.assertEqual(t, t2)
02000 
02001     def test_comparing(self):
02002         args = [1, 2, 3, 4]
02003         t1 = self.theclass(*args)
02004         t2 = self.theclass(*args)
02005         self.assertEqual(t1, t2)
02006         self.assertTrue(t1 <= t2)
02007         self.assertTrue(t1 >= t2)
02008         self.assertTrue(not t1 != t2)
02009         self.assertTrue(not t1 < t2)
02010         self.assertTrue(not t1 > t2)
02011 
02012         for i in range(len(args)):
02013             newargs = args[:]
02014             newargs[i] = args[i] + 1
02015             t2 = self.theclass(*newargs)   # this is larger than t1
02016             self.assertTrue(t1 < t2)
02017             self.assertTrue(t2 > t1)
02018             self.assertTrue(t1 <= t2)
02019             self.assertTrue(t2 >= t1)
02020             self.assertTrue(t1 != t2)
02021             self.assertTrue(t2 != t1)
02022             self.assertTrue(not t1 == t2)
02023             self.assertTrue(not t2 == t1)
02024             self.assertTrue(not t1 > t2)
02025             self.assertTrue(not t2 < t1)
02026             self.assertTrue(not t1 >= t2)
02027             self.assertTrue(not t2 <= t1)
02028 
02029         for badarg in OTHERSTUFF:
02030             self.assertEqual(t1 == badarg, False)
02031             self.assertEqual(t1 != badarg, True)
02032             self.assertEqual(badarg == t1, False)
02033             self.assertEqual(badarg != t1, True)
02034 
02035             self.assertRaises(TypeError, lambda: t1 <= badarg)
02036             self.assertRaises(TypeError, lambda: t1 < badarg)
02037             self.assertRaises(TypeError, lambda: t1 > badarg)
02038             self.assertRaises(TypeError, lambda: t1 >= badarg)
02039             self.assertRaises(TypeError, lambda: badarg <= t1)
02040             self.assertRaises(TypeError, lambda: badarg < t1)
02041             self.assertRaises(TypeError, lambda: badarg > t1)
02042             self.assertRaises(TypeError, lambda: badarg >= t1)
02043 
02044     def test_bad_constructor_arguments(self):
02045         # bad hours
02046         self.theclass(0, 0)    # no exception
02047         self.theclass(23, 0)   # no exception
02048         self.assertRaises(ValueError, self.theclass, -1, 0)
02049         self.assertRaises(ValueError, self.theclass, 24, 0)
02050         # bad minutes
02051         self.theclass(23, 0)    # no exception
02052         self.theclass(23, 59)   # no exception
02053         self.assertRaises(ValueError, self.theclass, 23, -1)
02054         self.assertRaises(ValueError, self.theclass, 23, 60)
02055         # bad seconds
02056         self.theclass(23, 59, 0)    # no exception
02057         self.theclass(23, 59, 59)   # no exception
02058         self.assertRaises(ValueError, self.theclass, 23, 59, -1)
02059         self.assertRaises(ValueError, self.theclass, 23, 59, 60)
02060         # bad microseconds
02061         self.theclass(23, 59, 59, 0)        # no exception
02062         self.theclass(23, 59, 59, 999999)   # no exception
02063         self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
02064         self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
02065 
02066     def test_hash_equality(self):
02067         d = self.theclass(23, 30, 17)
02068         e = self.theclass(23, 30, 17)
02069         self.assertEqual(d, e)
02070         self.assertEqual(hash(d), hash(e))
02071 
02072         dic = {d: 1}
02073         dic[e] = 2
02074         self.assertEqual(len(dic), 1)
02075         self.assertEqual(dic[d], 2)
02076         self.assertEqual(dic[e], 2)
02077 
02078         d = self.theclass(0,  5, 17)
02079         e = self.theclass(0,  5, 17)
02080         self.assertEqual(d, e)
02081         self.assertEqual(hash(d), hash(e))
02082 
02083         dic = {d: 1}
02084         dic[e] = 2
02085         self.assertEqual(len(dic), 1)
02086         self.assertEqual(dic[d], 2)
02087         self.assertEqual(dic[e], 2)
02088 
02089     def test_isoformat(self):
02090         t = self.theclass(4, 5, 1, 123)
02091         self.assertEqual(t.isoformat(), "04:05:01.000123")
02092         self.assertEqual(t.isoformat(), str(t))
02093 
02094         t = self.theclass()
02095         self.assertEqual(t.isoformat(), "00:00:00")
02096         self.assertEqual(t.isoformat(), str(t))
02097 
02098         t = self.theclass(microsecond=1)
02099         self.assertEqual(t.isoformat(), "00:00:00.000001")
02100         self.assertEqual(t.isoformat(), str(t))
02101 
02102         t = self.theclass(microsecond=10)
02103         self.assertEqual(t.isoformat(), "00:00:00.000010")
02104         self.assertEqual(t.isoformat(), str(t))
02105 
02106         t = self.theclass(microsecond=100)
02107         self.assertEqual(t.isoformat(), "00:00:00.000100")
02108         self.assertEqual(t.isoformat(), str(t))
02109 
02110         t = self.theclass(microsecond=1000)
02111         self.assertEqual(t.isoformat(), "00:00:00.001000")
02112         self.assertEqual(t.isoformat(), str(t))
02113 
02114         t = self.theclass(microsecond=10000)
02115         self.assertEqual(t.isoformat(), "00:00:00.010000")
02116         self.assertEqual(t.isoformat(), str(t))
02117 
02118         t = self.theclass(microsecond=100000)
02119         self.assertEqual(t.isoformat(), "00:00:00.100000")
02120         self.assertEqual(t.isoformat(), str(t))
02121 
02122     def test_1653736(self):
02123         # verify it doesn't accept extra keyword arguments
02124         t = self.theclass(second=1)
02125         self.assertRaises(TypeError, t.isoformat, foo=3)
02126 
02127     def test_strftime(self):
02128         t = self.theclass(1, 2, 3, 4)
02129         self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
02130         # A naive object replaces %z and %Z with empty strings.
02131         self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
02132 
02133     def test_format(self):
02134         t = self.theclass(1, 2, 3, 4)
02135         self.assertEqual(t.__format__(''), str(t))
02136 
02137         # check that a derived class's __str__() gets called
02138         class A(self.theclass):
02139             def __str__(self):
02140                 return 'A'
02141         a = A(1, 2, 3, 4)
02142         self.assertEqual(a.__format__(''), 'A')
02143 
02144         # check that a derived class's strftime gets called
02145         class B(self.theclass):
02146             def strftime(self, format_spec):
02147                 return 'B'
02148         b = B(1, 2, 3, 4)
02149         self.assertEqual(b.__format__(''), str(t))
02150 
02151         for fmt in ['%H %M %S',
02152                     ]:
02153             self.assertEqual(t.__format__(fmt), t.strftime(fmt))
02154             self.assertEqual(a.__format__(fmt), t.strftime(fmt))
02155             self.assertEqual(b.__format__(fmt), 'B')
02156 
02157     def test_str(self):
02158         self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
02159         self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
02160         self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
02161         self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
02162         self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
02163 
02164     def test_repr(self):
02165         name = 'datetime.' + self.theclass.__name__
02166         self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
02167                          "%s(1, 2, 3, 4)" % name)
02168         self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
02169                          "%s(10, 2, 3, 4000)" % name)
02170         self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
02171                          "%s(0, 2, 3, 400000)" % name)
02172         self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
02173                          "%s(12, 2, 3)" % name)
02174         self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
02175                          "%s(23, 15)" % name)
02176 
02177     def test_resolution_info(self):
02178         self.assertIsInstance(self.theclass.min, self.theclass)
02179         self.assertIsInstance(self.theclass.max, self.theclass)
02180         self.assertIsInstance(self.theclass.resolution, timedelta)
02181         self.assertTrue(self.theclass.max > self.theclass.min)
02182 
02183     def test_pickling(self):
02184         args = 20, 59, 16, 64**2
02185         orig = self.theclass(*args)
02186         for pickler, unpickler, proto in pickle_choices:
02187             green = pickler.dumps(orig, proto)
02188             derived = unpickler.loads(green)
02189             self.assertEqual(orig, derived)
02190 
02191     def test_pickling_subclass_time(self):
02192         args = 20, 59, 16, 64**2
02193         orig = SubclassTime(*args)
02194         for pickler, unpickler, proto in pickle_choices:
02195             green = pickler.dumps(orig, proto)
02196             derived = unpickler.loads(green)
02197             self.assertEqual(orig, derived)
02198 
02199     def test_bool(self):
02200         cls = self.theclass
02201         self.assertTrue(cls(1))
02202         self.assertTrue(cls(0, 1))
02203         self.assertTrue(cls(0, 0, 1))
02204         self.assertTrue(cls(0, 0, 0, 1))
02205         self.assertTrue(not cls(0))
02206         self.assertTrue(not cls())
02207 
02208     def test_replace(self):
02209         cls = self.theclass
02210         args = [1, 2, 3, 4]
02211         base = cls(*args)
02212         self.assertEqual(base, base.replace())
02213 
02214         i = 0
02215         for name, newval in (("hour", 5),
02216                              ("minute", 6),
02217                              ("second", 7),
02218                              ("microsecond", 8)):
02219             newargs = args[:]
02220             newargs[i] = newval
02221             expected = cls(*newargs)
02222             got = base.replace(**{name: newval})
02223             self.assertEqual(expected, got)
02224             i += 1
02225 
02226         # Out of bounds.
02227         base = cls(1)
02228         self.assertRaises(ValueError, base.replace, hour=24)
02229         self.assertRaises(ValueError, base.replace, minute=-1)
02230         self.assertRaises(ValueError, base.replace, second=100)
02231         self.assertRaises(ValueError, base.replace, microsecond=1000000)
02232 
02233     def test_subclass_time(self):
02234 
02235         class C(self.theclass):
02236             theAnswer = 42
02237 
02238             def __new__(cls, *args, **kws):
02239                 temp = kws.copy()
02240                 extra = temp.pop('extra')
02241                 result = self.theclass.__new__(cls, *args, **temp)
02242                 result.extra = extra
02243                 return result
02244 
02245             def newmeth(self, start):
02246                 return start + self.hour + self.second
02247 
02248         args = 4, 5, 6
02249 
02250         dt1 = self.theclass(*args)
02251         dt2 = C(*args, **{'extra': 7})
02252 
02253         self.assertEqual(dt2.__class__, C)
02254         self.assertEqual(dt2.theAnswer, 42)
02255         self.assertEqual(dt2.extra, 7)
02256         self.assertEqual(dt1.isoformat(), dt2.isoformat())
02257         self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
02258 
02259     def test_backdoor_resistance(self):
02260         # see TestDate.test_backdoor_resistance().
02261         base = '2:59.0'
02262         for hour_byte in ' ', '9', chr(24), '\xff':
02263             self.assertRaises(TypeError, self.theclass,
02264                                          hour_byte + base[1:])
02265 
02266 # A mixin for classes with a tzinfo= argument.  Subclasses must define
02267 # theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
02268 # must be legit (which is true for time and datetime).
02269 class TZInfoBase:
02270 
02271     def test_argument_passing(self):
02272         cls = self.theclass
02273         # A datetime passes itself on, a time passes None.
02274         class introspective(tzinfo):
02275             def tzname(self, dt):    return dt and "real" or "none"
02276             def utcoffset(self, dt):
02277                 return timedelta(minutes = dt and 42 or -42)
02278             dst = utcoffset
02279 
02280         obj = cls(1, 2, 3, tzinfo=introspective())
02281 
02282         expected = cls is time and "none" or "real"
02283         self.assertEqual(obj.tzname(), expected)
02284 
02285         expected = timedelta(minutes=(cls is time and -42 or 42))
02286         self.assertEqual(obj.utcoffset(), expected)
02287         self.assertEqual(obj.dst(), expected)
02288 
02289     def test_bad_tzinfo_classes(self):
02290         cls = self.theclass
02291         self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
02292 
02293         class NiceTry(object):
02294             def __init__(self): pass
02295             def utcoffset(self, dt): pass
02296         self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
02297 
02298         class BetterTry(tzinfo):
02299             def __init__(self): pass
02300             def utcoffset(self, dt): pass
02301         b = BetterTry()
02302         t = cls(1, 1, 1, tzinfo=b)
02303         self.assertTrue(t.tzinfo is b)
02304 
02305     def test_utc_offset_out_of_bounds(self):
02306         class Edgy(tzinfo):
02307             def __init__(self, offset):
02308                 self.offset = timedelta(minutes=offset)
02309             def utcoffset(self, dt):
02310                 return self.offset
02311 
02312         cls = self.theclass
02313         for offset, legit in ((-1440, False),
02314                               (-1439, True),
02315                               (1439, True),
02316                               (1440, False)):
02317             if cls is time:
02318                 t = cls(1, 2, 3, tzinfo=Edgy(offset))
02319             elif cls is datetime:
02320                 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
02321             else:
02322                 assert 0, "impossible"
02323             if legit:
02324                 aofs = abs(offset)
02325                 h, m = divmod(aofs, 60)
02326                 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
02327                 if isinstance(t, datetime):
02328                     t = t.timetz()
02329                 self.assertEqual(str(t), "01:02:03" + tag)
02330             else:
02331                 self.assertRaises(ValueError, str, t)
02332 
02333     def test_tzinfo_classes(self):
02334         cls = self.theclass
02335         class C1(tzinfo):
02336             def utcoffset(self, dt): return None
02337             def dst(self, dt): return None
02338             def tzname(self, dt): return None
02339         for t in (cls(1, 1, 1),
02340                   cls(1, 1, 1, tzinfo=None),
02341                   cls(1, 1, 1, tzinfo=C1())):
02342             self.assertTrue(t.utcoffset() is None)
02343             self.assertTrue(t.dst() is None)
02344             self.assertTrue(t.tzname() is None)
02345 
02346         class C3(tzinfo):
02347             def utcoffset(self, dt): return timedelta(minutes=-1439)
02348             def dst(self, dt): return timedelta(minutes=1439)
02349             def tzname(self, dt): return "aname"
02350         t = cls(1, 1, 1, tzinfo=C3())
02351         self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
02352         self.assertEqual(t.dst(), timedelta(minutes=1439))
02353         self.assertEqual(t.tzname(), "aname")
02354 
02355         # Wrong types.
02356         class C4(tzinfo):
02357             def utcoffset(self, dt): return "aname"
02358             def dst(self, dt): return 7
02359             def tzname(self, dt): return 0
02360         t = cls(1, 1, 1, tzinfo=C4())
02361         self.assertRaises(TypeError, t.utcoffset)
02362         self.assertRaises(TypeError, t.dst)
02363         self.assertRaises(TypeError, t.tzname)
02364 
02365         # Offset out of range.
02366         class C6(tzinfo):
02367             def utcoffset(self, dt): return timedelta(hours=-24)
02368             def dst(self, dt): return timedelta(hours=24)
02369         t = cls(1, 1, 1, tzinfo=C6())
02370         self.assertRaises(ValueError, t.utcoffset)
02371         self.assertRaises(ValueError, t.dst)
02372 
02373         # Not a whole number of minutes.
02374         class C7(tzinfo):
02375             def utcoffset(self, dt): return timedelta(seconds=61)
02376             def dst(self, dt): return timedelta(microseconds=-81)
02377         t = cls(1, 1, 1, tzinfo=C7())
02378         self.assertRaises(ValueError, t.utcoffset)
02379         self.assertRaises(ValueError, t.dst)
02380 
02381     def test_aware_compare(self):
02382         cls = self.theclass
02383 
02384         # Ensure that utcoffset() gets ignored if the comparands have
02385         # the same tzinfo member.
02386         class OperandDependentOffset(tzinfo):
02387             def utcoffset(self, t):
02388                 if t.minute < 10:
02389                     # d0 and d1 equal after adjustment
02390                     return timedelta(minutes=t.minute)
02391                 else:
02392                     # d2 off in the weeds
02393                     return timedelta(minutes=59)
02394 
02395         base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
02396         d0 = base.replace(minute=3)
02397         d1 = base.replace(minute=9)
02398         d2 = base.replace(minute=11)
02399         for x in d0, d1, d2:
02400             for y in d0, d1, d2:
02401                 for op in lt, le, gt, ge, eq, ne:
02402                     got = op(x, y)
02403                     expected = op(x.minute, y.minute)
02404                     self.assertEqual(got, expected)
02405 
02406         # However, if they're different members, uctoffset is not ignored.
02407         # Note that a time can't actually have an operand-depedent offset,
02408         # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
02409         # so skip this test for time.
02410         if cls is not time:
02411             d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
02412             d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
02413             d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
02414             for x in d0, d1, d2:
02415                 for y in d0, d1, d2:
02416                     got = (x > y) - (x < y)
02417                     if (x is d0 or x is d1) and (y is d0 or y is d1):
02418                         expected = 0
02419                     elif x is y is d2:
02420                         expected = 0
02421                     elif x is d2:
02422                         expected = -1
02423                     else:
02424                         assert y is d2
02425                         expected = 1
02426                     self.assertEqual(got, expected)
02427 
02428 
02429 # Testing time objects with a non-None tzinfo.
02430 class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
02431     theclass = time
02432 
02433     def test_empty(self):
02434         t = self.theclass()
02435         self.assertEqual(t.hour, 0)
02436         self.assertEqual(t.minute, 0)
02437         self.assertEqual(t.second, 0)
02438         self.assertEqual(t.microsecond, 0)
02439         self.assertTrue(t.tzinfo is None)
02440 
02441     def test_zones(self):
02442         est = FixedOffset(-300, "EST", 1)
02443         utc = FixedOffset(0, "UTC", -2)
02444         met = FixedOffset(60, "MET", 3)
02445         t1 = time( 7, 47, tzinfo=est)
02446         t2 = time(12, 47, tzinfo=utc)
02447         t3 = time(13, 47, tzinfo=met)
02448         t4 = time(microsecond=40)
02449         t5 = time(microsecond=40, tzinfo=utc)
02450 
02451         self.assertEqual(t1.tzinfo, est)
02452         self.assertEqual(t2.tzinfo, utc)
02453         self.assertEqual(t3.tzinfo, met)
02454         self.assertTrue(t4.tzinfo is None)
02455         self.assertEqual(t5.tzinfo, utc)
02456 
02457         self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
02458         self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
02459         self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
02460         self.assertTrue(t4.utcoffset() is None)
02461         self.assertRaises(TypeError, t1.utcoffset, "no args")
02462 
02463         self.assertEqual(t1.tzname(), "EST")
02464         self.assertEqual(t2.tzname(), "UTC")
02465         self.assertEqual(t3.tzname(), "MET")
02466         self.assertTrue(t4.tzname() is None)
02467         self.assertRaises(TypeError, t1.tzname, "no args")
02468 
02469         self.assertEqual(t1.dst(), timedelta(minutes=1))
02470         self.assertEqual(t2.dst(), timedelta(minutes=-2))
02471         self.assertEqual(t3.dst(), timedelta(minutes=3))
02472         self.assertTrue(t4.dst() is None)
02473         self.assertRaises(TypeError, t1.dst, "no args")
02474 
02475         self.assertEqual(hash(t1), hash(t2))
02476         self.assertEqual(hash(t1), hash(t3))
02477         self.assertEqual(hash(t2), hash(t3))
02478 
02479         self.assertEqual(t1, t2)
02480         self.assertEqual(t1, t3)
02481         self.assertEqual(t2, t3)
02482         self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
02483         self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
02484         self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
02485 
02486         self.assertEqual(str(t1), "07:47:00-05:00")
02487         self.assertEqual(str(t2), "12:47:00+00:00")
02488         self.assertEqual(str(t3), "13:47:00+01:00")
02489         self.assertEqual(str(t4), "00:00:00.000040")
02490         self.assertEqual(str(t5), "00:00:00.000040+00:00")
02491 
02492         self.assertEqual(t1.isoformat(), "07:47:00-05:00")
02493         self.assertEqual(t2.isoformat(), "12:47:00+00:00")
02494         self.assertEqual(t3.isoformat(), "13:47:00+01:00")
02495         self.assertEqual(t4.isoformat(), "00:00:00.000040")
02496         self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
02497 
02498         d = 'datetime.time'
02499         self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
02500         self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
02501         self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
02502         self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
02503         self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
02504 
02505         self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
02506                                      "07:47:00 %Z=EST %z=-0500")
02507         self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
02508         self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
02509 
02510         yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
02511         t1 = time(23, 59, tzinfo=yuck)
02512         self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
02513                                      "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
02514 
02515         # Check that an invalid tzname result raises an exception.
02516         class Badtzname(tzinfo):
02517             tz = 42
02518             def tzname(self, dt): return self.tz
02519         t = time(2, 3, 4, tzinfo=Badtzname())
02520         self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
02521         self.assertRaises(TypeError, t.strftime, "%Z")
02522 
02523         # Issue #6697:
02524         if '_Fast' in str(type(self)):
02525             Badtzname.tz = '\ud800'
02526             self.assertRaises(ValueError, t.strftime, "%Z")
02527 
02528     def test_hash_edge_cases(self):
02529         # Offsets that overflow a basic time.
02530         t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
02531         t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
02532         self.assertEqual(hash(t1), hash(t2))
02533 
02534         t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
02535         t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
02536         self.assertEqual(hash(t1), hash(t2))
02537 
02538     def test_pickling(self):
02539         # Try one without a tzinfo.
02540         args = 20, 59, 16, 64**2
02541         orig = self.theclass(*args)
02542         for pickler, unpickler, proto in pickle_choices:
02543             green = pickler.dumps(orig, proto)
02544             derived = unpickler.loads(green)
02545             self.assertEqual(orig, derived)
02546 
02547         # Try one with a tzinfo.
02548         tinfo = PicklableFixedOffset(-300, 'cookie')
02549         orig = self.theclass(5, 6, 7, tzinfo=tinfo)
02550         for pickler, unpickler, proto in pickle_choices:
02551             green = pickler.dumps(orig, proto)
02552             derived = unpickler.loads(green)
02553             self.assertEqual(orig, derived)
02554             self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
02555             self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
02556             self.assertEqual(derived.tzname(), 'cookie')
02557 
02558     def test_more_bool(self):
02559         # Test cases with non-None tzinfo.
02560         cls = self.theclass
02561 
02562         t = cls(0, tzinfo=FixedOffset(-300, ""))
02563         self.assertTrue(t)
02564 
02565         t = cls(5, tzinfo=FixedOffset(-300, ""))
02566         self.assertTrue(t)
02567 
02568         t = cls(5, tzinfo=FixedOffset(300, ""))
02569         self.assertTrue(not t)
02570 
02571         t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
02572         self.assertTrue(not t)
02573 
02574         # Mostly ensuring this doesn't overflow internally.
02575         t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
02576         self.assertTrue(t)
02577 
02578         # But this should yield a value error -- the utcoffset is bogus.
02579         t = cls(0, tzinfo=FixedOffset(24*60, ""))
02580         self.assertRaises(ValueError, lambda: bool(t))
02581 
02582         # Likewise.
02583         t = cls(0, tzinfo=FixedOffset(-24*60, ""))
02584         self.assertRaises(ValueError, lambda: bool(t))
02585 
02586     def test_replace(self):
02587         cls = self.theclass
02588         z100 = FixedOffset(100, "+100")
02589         zm200 = FixedOffset(timedelta(minutes=-200), "-200")
02590         args = [1, 2, 3, 4, z100]
02591         base = cls(*args)
02592         self.assertEqual(base, base.replace())
02593 
02594         i = 0
02595         for name, newval in (("hour", 5),
02596                              ("minute", 6),
02597                              ("second", 7),
02598                              ("microsecond", 8),
02599                              ("tzinfo", zm200)):
02600             newargs = args[:]
02601             newargs[i] = newval
02602             expected = cls(*newargs)
02603             got = base.replace(**{name: newval})
02604             self.assertEqual(expected, got)
02605             i += 1
02606 
02607         # Ensure we can get rid of a tzinfo.
02608         self.assertEqual(base.tzname(), "+100")
02609         base2 = base.replace(tzinfo=None)
02610         self.assertTrue(base2.tzinfo is None)
02611         self.assertTrue(base2.tzname() is None)
02612 
02613         # Ensure we can add one.
02614         base3 = base2.replace(tzinfo=z100)
02615         self.assertEqual(base, base3)
02616         self.assertTrue(base.tzinfo is base3.tzinfo)
02617 
02618         # Out of bounds.
02619         base = cls(1)
02620         self.assertRaises(ValueError, base.replace, hour=24)
02621         self.assertRaises(ValueError, base.replace, minute=-1)
02622         self.assertRaises(ValueError, base.replace, second=100)
02623         self.assertRaises(ValueError, base.replace, microsecond=1000000)
02624 
02625     def test_mixed_compare(self):
02626         t1 = time(1, 2, 3)
02627         t2 = time(1, 2, 3)
02628         self.assertEqual(t1, t2)
02629         t2 = t2.replace(tzinfo=None)
02630         self.assertEqual(t1, t2)
02631         t2 = t2.replace(tzinfo=FixedOffset(None, ""))
02632         self.assertEqual(t1, t2)
02633         t2 = t2.replace(tzinfo=FixedOffset(0, ""))
02634         self.assertRaises(TypeError, lambda: t1 == t2)
02635 
02636         # In time w/ identical tzinfo objects, utcoffset is ignored.
02637         class Varies(tzinfo):
02638             def __init__(self):
02639                 self.offset = timedelta(minutes=22)
02640             def utcoffset(self, t):
02641                 self.offset += timedelta(minutes=1)
02642                 return self.offset
02643 
02644         v = Varies()
02645         t1 = t2.replace(tzinfo=v)
02646         t2 = t2.replace(tzinfo=v)
02647         self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
02648         self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
02649         self.assertEqual(t1, t2)
02650 
02651         # But if they're not identical, it isn't ignored.
02652         t2 = t2.replace(tzinfo=Varies())
02653         self.assertTrue(t1 < t2)  # t1's offset counter still going up
02654 
02655     def test_subclass_timetz(self):
02656 
02657         class C(self.theclass):
02658             theAnswer = 42
02659 
02660             def __new__(cls, *args, **kws):
02661                 temp = kws.copy()
02662                 extra = temp.pop('extra')
02663                 result = self.theclass.__new__(cls, *args, **temp)
02664                 result.extra = extra
02665                 return result
02666 
02667             def newmeth(self, start):
02668                 return start + self.hour + self.second
02669 
02670         args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
02671 
02672         dt1 = self.theclass(*args)
02673         dt2 = C(*args, **{'extra': 7})
02674 
02675         self.assertEqual(dt2.__class__, C)
02676         self.assertEqual(dt2.theAnswer, 42)
02677         self.assertEqual(dt2.extra, 7)
02678         self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
02679         self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
02680 
02681 
02682 # Testing datetime objects with a non-None tzinfo.
02683 
02684 class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
02685     theclass = datetime
02686 
02687     def test_trivial(self):
02688         dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
02689         self.assertEqual(dt.year, 1)
02690         self.assertEqual(dt.month, 2)
02691         self.assertEqual(dt.day, 3)
02692         self.assertEqual(dt.hour, 4)
02693         self.assertEqual(dt.minute, 5)
02694         self.assertEqual(dt.second, 6)
02695         self.assertEqual(dt.microsecond, 7)
02696         self.assertEqual(dt.tzinfo, None)
02697 
02698     def test_even_more_compare(self):
02699         # The test_compare() and test_more_compare() inherited from TestDate
02700         # and TestDateTime covered non-tzinfo cases.
02701 
02702         # Smallest possible after UTC adjustment.
02703         t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
02704         # Largest possible after UTC adjustment.
02705         t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
02706                            tzinfo=FixedOffset(-1439, ""))
02707 
02708         # Make sure those compare correctly, and w/o overflow.
02709         self.assertTrue(t1 < t2)
02710         self.assertTrue(t1 != t2)
02711         self.assertTrue(t2 > t1)
02712 
02713         self.assertEqual(t1, t1)
02714         self.assertEqual(t2, t2)
02715 
02716         # Equal afer adjustment.
02717         t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
02718         t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
02719         self.assertEqual(t1, t2)
02720 
02721         # Change t1 not to subtract a minute, and t1 should be larger.
02722         t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
02723         self.assertTrue(t1 > t2)
02724 
02725         # Change t1 to subtract 2 minutes, and t1 should be smaller.
02726         t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
02727         self.assertTrue(t1 < t2)
02728 
02729         # Back to the original t1, but make seconds resolve it.
02730         t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
02731                            second=1)
02732         self.assertTrue(t1 > t2)
02733 
02734         # Likewise, but make microseconds resolve it.
02735         t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
02736                            microsecond=1)
02737         self.assertTrue(t1 > t2)
02738 
02739         # Make t2 naive and it should fail.
02740         t2 = self.theclass.min
02741         self.assertRaises(TypeError, lambda: t1 == t2)
02742         self.assertEqual(t2, t2)
02743 
02744         # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
02745         class Naive(tzinfo):
02746             def utcoffset(self, dt): return None
02747         t2 = self.theclass(5, 6, 7, tzinfo=Naive())
02748         self.assertRaises(TypeError, lambda: t1 == t2)
02749         self.assertEqual(t2, t2)
02750 
02751         # OTOH, it's OK to compare two of these mixing the two ways of being
02752         # naive.
02753         t1 = self.theclass(5, 6, 7)
02754         self.assertEqual(t1, t2)
02755 
02756         # Try a bogus uctoffset.
02757         class Bogus(tzinfo):
02758             def utcoffset(self, dt):
02759                 return timedelta(minutes=1440) # out of bounds
02760         t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
02761         t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
02762         self.assertRaises(ValueError, lambda: t1 == t2)
02763 
02764     def test_pickling(self):
02765         # Try one without a tzinfo.
02766         args = 6, 7, 23, 20, 59, 1, 64**2
02767         orig = self.theclass(*args)
02768         for pickler, unpickler, proto in pickle_choices:
02769             green = pickler.dumps(orig, proto)
02770             derived = unpickler.loads(green)
02771             self.assertEqual(orig, derived)
02772 
02773         # Try one with a tzinfo.
02774         tinfo = PicklableFixedOffset(-300, 'cookie')
02775         orig = self.theclass(*args, **{'tzinfo': tinfo})
02776         derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
02777         for pickler, unpickler, proto in pickle_choices:
02778             green = pickler.dumps(orig, proto)
02779             derived = unpickler.loads(green)
02780             self.assertEqual(orig, derived)
02781             self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
02782             self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
02783             self.assertEqual(derived.tzname(), 'cookie')
02784 
02785     def test_extreme_hashes(self):
02786         # If an attempt is made to hash these via subtracting the offset
02787         # then hashing a datetime object, OverflowError results.  The
02788         # Python implementation used to blow up here.
02789         t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
02790         hash(t)
02791         t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
02792                           tzinfo=FixedOffset(-1439, ""))
02793         hash(t)
02794 
02795         # OTOH, an OOB offset should blow up.
02796         t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
02797         self.assertRaises(ValueError, hash, t)
02798 
02799     def test_zones(self):
02800         est = FixedOffset(-300, "EST")
02801         utc = FixedOffset(0, "UTC")
02802         met = FixedOffset(60, "MET")
02803         t1 = datetime(2002, 3, 19,  7, 47, tzinfo=est)
02804         t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
02805         t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
02806         self.assertEqual(t1.tzinfo, est)
02807         self.assertEqual(t2.tzinfo, utc)
02808         self.assertEqual(t3.tzinfo, met)
02809         self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
02810         self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
02811         self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
02812         self.assertEqual(t1.tzname(), "EST")
02813         self.assertEqual(t2.tzname(), "UTC")
02814         self.assertEqual(t3.tzname(), "MET")
02815         self.assertEqual(hash(t1), hash(t2))
02816         self.assertEqual(hash(t1), hash(t3))
02817         self.assertEqual(hash(t2), hash(t3))
02818         self.assertEqual(t1, t2)
02819         self.assertEqual(t1, t3)
02820         self.assertEqual(t2, t3)
02821         self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
02822         self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
02823         self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
02824         d = 'datetime.datetime(2002, 3, 19, '
02825         self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
02826         self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
02827         self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
02828 
02829     def test_combine(self):
02830         met = FixedOffset(60, "MET")
02831         d = date(2002, 3, 4)
02832         tz = time(18, 45, 3, 1234, tzinfo=met)
02833         dt = datetime.combine(d, tz)
02834         self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
02835                                         tzinfo=met))
02836 
02837     def test_extract(self):
02838         met = FixedOffset(60, "MET")
02839         dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
02840         self.assertEqual(dt.date(), date(2002, 3, 4))
02841         self.assertEqual(dt.time(), time(18, 45, 3, 1234))
02842         self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
02843 
02844     def test_tz_aware_arithmetic(self):
02845         import random
02846 
02847         now = self.theclass.now()
02848         tz55 = FixedOffset(-330, "west 5:30")
02849         timeaware = now.time().replace(tzinfo=tz55)
02850         nowaware = self.theclass.combine(now.date(), timeaware)
02851         self.assertTrue(nowaware.tzinfo is tz55)
02852         self.assertEqual(nowaware.timetz(), timeaware)
02853 
02854         # Can't mix aware and non-aware.
02855         self.assertRaises(TypeError, lambda: now - nowaware)
02856         self.assertRaises(TypeError, lambda: nowaware - now)
02857 
02858         # And adding datetime's doesn't make sense, aware or not.
02859         self.assertRaises(TypeError, lambda: now + nowaware)
02860         self.assertRaises(TypeError, lambda: nowaware + now)
02861         self.assertRaises(TypeError, lambda: nowaware + nowaware)
02862 
02863         # Subtracting should yield 0.
02864         self.assertEqual(now - now, timedelta(0))
02865         self.assertEqual(nowaware - nowaware, timedelta(0))
02866 
02867         # Adding a delta should preserve tzinfo.
02868         delta = timedelta(weeks=1, minutes=12, microseconds=5678)
02869         nowawareplus = nowaware + delta
02870         self.assertTrue(nowaware.tzinfo is tz55)
02871         nowawareplus2 = delta + nowaware
02872         self.assertTrue(nowawareplus2.tzinfo is tz55)
02873         self.assertEqual(nowawareplus, nowawareplus2)
02874 
02875         # that - delta should be what we started with, and that - what we
02876         # started with should be delta.
02877         diff = nowawareplus - delta
02878         self.assertTrue(diff.tzinfo is tz55)
02879         self.assertEqual(nowaware, diff)
02880         self.assertRaises(TypeError, lambda: delta - nowawareplus)
02881         self.assertEqual(nowawareplus - nowaware, delta)
02882 
02883         # Make up a random timezone.
02884         tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
02885         # Attach it to nowawareplus.
02886         nowawareplus = nowawareplus.replace(tzinfo=tzr)
02887         self.assertTrue(nowawareplus.tzinfo is tzr)
02888         # Make sure the difference takes the timezone adjustments into account.
02889         got = nowaware - nowawareplus
02890         # Expected:  (nowaware base - nowaware offset) -
02891         #            (nowawareplus base - nowawareplus offset) =
02892         #            (nowaware base - nowawareplus base) +
02893         #            (nowawareplus offset - nowaware offset) =
02894         #            -delta + nowawareplus offset - nowaware offset
02895         expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
02896         self.assertEqual(got, expected)
02897 
02898         # Try max possible difference.
02899         min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
02900         max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
02901                             tzinfo=FixedOffset(-1439, "max"))
02902         maxdiff = max - min
02903         self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
02904                                   timedelta(minutes=2*1439))
02905         # Different tzinfo, but the same offset
02906         tza = timezone(HOUR, 'A')
02907         tzb = timezone(HOUR, 'B')
02908         delta = min.replace(tzinfo=tza) - max.replace(tzinfo=tzb)
02909         self.assertEqual(delta, self.theclass.min - self.theclass.max)
02910 
02911     def test_tzinfo_now(self):
02912         meth = self.theclass.now
02913         # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
02914         base = meth()
02915         # Try with and without naming the keyword.
02916         off42 = FixedOffset(42, "42")
02917         another = meth(off42)
02918         again = meth(tz=off42)
02919         self.assertTrue(another.tzinfo is again.tzinfo)
02920         self.assertEqual(another.utcoffset(), timedelta(minutes=42))
02921         # Bad argument with and w/o naming the keyword.
02922         self.assertRaises(TypeError, meth, 16)
02923         self.assertRaises(TypeError, meth, tzinfo=16)
02924         # Bad keyword name.
02925         self.assertRaises(TypeError, meth, tinfo=off42)
02926         # Too many args.
02927         self.assertRaises(TypeError, meth, off42, off42)
02928 
02929         # We don't know which time zone we're in, and don't have a tzinfo
02930         # class to represent it, so seeing whether a tz argument actually
02931         # does a conversion is tricky.
02932         utc = FixedOffset(0, "utc", 0)
02933         for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
02934                         timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
02935             for dummy in range(3):
02936                 now = datetime.now(weirdtz)
02937                 self.assertTrue(now.tzinfo is weirdtz)
02938                 utcnow = datetime.utcnow().replace(tzinfo=utc)
02939                 now2 = utcnow.astimezone(weirdtz)
02940                 if abs(now - now2) < timedelta(seconds=30):
02941                     break
02942                 # Else the code is broken, or more than 30 seconds passed between
02943                 # calls; assuming the latter, just try again.
02944             else:
02945                 # Three strikes and we're out.
02946                 self.fail("utcnow(), now(tz), or astimezone() may be broken")
02947 
02948     def test_tzinfo_fromtimestamp(self):
02949         import time
02950         meth = self.theclass.fromtimestamp
02951         ts = time.time()
02952         # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
02953         base = meth(ts)
02954         # Try with and without naming the keyword.
02955         off42 = FixedOffset(42, "42")
02956         another = meth(ts, off42)
02957         again = meth(ts, tz=off42)
02958         self.assertTrue(another.tzinfo is again.tzinfo)
02959         self.assertEqual(another.utcoffset(), timedelta(minutes=42))
02960         # Bad argument with and w/o naming the keyword.
02961         self.assertRaises(TypeError, meth, ts, 16)
02962         self.assertRaises(TypeError, meth, ts, tzinfo=16)
02963         # Bad keyword name.
02964         self.assertRaises(TypeError, meth, ts, tinfo=off42)
02965         # Too many args.
02966         self.assertRaises(TypeError, meth, ts, off42, off42)
02967         # Too few args.
02968         self.assertRaises(TypeError, meth)
02969 
02970         # Try to make sure tz= actually does some conversion.
02971         timestamp = 1000000000
02972         utcdatetime = datetime.utcfromtimestamp(timestamp)
02973         # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
02974         # But on some flavor of Mac, it's nowhere near that.  So we can't have
02975         # any idea here what time that actually is, we can only test that
02976         # relative changes match.
02977         utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
02978         tz = FixedOffset(utcoffset, "tz", 0)
02979         expected = utcdatetime + utcoffset
02980         got = datetime.fromtimestamp(timestamp, tz)
02981         self.assertEqual(expected, got.replace(tzinfo=None))
02982 
02983     def test_tzinfo_utcnow(self):
02984         meth = self.theclass.utcnow
02985         # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
02986         base = meth()
02987         # Try with and without naming the keyword; for whatever reason,
02988         # utcnow() doesn't accept a tzinfo argument.
02989         off42 = FixedOffset(42, "42")
02990         self.assertRaises(TypeError, meth, off42)
02991         self.assertRaises(TypeError, meth, tzinfo=off42)
02992 
02993     def test_tzinfo_utcfromtimestamp(self):
02994         import time
02995         meth = self.theclass.utcfromtimestamp
02996         ts = time.time()
02997         # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
02998         base = meth(ts)
02999         # Try with and without naming the keyword; for whatever reason,
03000         # utcfromtimestamp() doesn't accept a tzinfo argument.
03001         off42 = FixedOffset(42, "42")
03002         self.assertRaises(TypeError, meth, ts, off42)
03003         self.assertRaises(TypeError, meth, ts, tzinfo=off42)
03004 
03005     def test_tzinfo_timetuple(self):
03006         # TestDateTime tested most of this.  datetime adds a twist to the
03007         # DST flag.
03008         class DST(tzinfo):
03009             def __init__(self, dstvalue):
03010                 if isinstance(dstvalue, int):
03011                     dstvalue = timedelta(minutes=dstvalue)
03012                 self.dstvalue = dstvalue
03013             def dst(self, dt):
03014                 return self.dstvalue
03015 
03016         cls = self.theclass
03017         for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
03018             d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
03019             t = d.timetuple()
03020             self.assertEqual(1, t.tm_year)
03021             self.assertEqual(1, t.tm_mon)
03022             self.assertEqual(1, t.tm_mday)
03023             self.assertEqual(10, t.tm_hour)
03024             self.assertEqual(20, t.tm_min)
03025             self.assertEqual(30, t.tm_sec)
03026             self.assertEqual(0, t.tm_wday)
03027             self.assertEqual(1, t.tm_yday)
03028             self.assertEqual(flag, t.tm_isdst)
03029 
03030         # dst() returns wrong type.
03031         self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
03032 
03033         # dst() at the edge.
03034         self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
03035         self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
03036 
03037         # dst() out of range.
03038         self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
03039         self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
03040 
03041     def test_utctimetuple(self):
03042         class DST(tzinfo):
03043             def __init__(self, dstvalue=0):
03044                 if isinstance(dstvalue, int):
03045                     dstvalue = timedelta(minutes=dstvalue)
03046                 self.dstvalue = dstvalue
03047             def dst(self, dt):
03048                 return self.dstvalue
03049 
03050         cls = self.theclass
03051         # This can't work:  DST didn't implement utcoffset.
03052         self.assertRaises(NotImplementedError,
03053                           cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
03054 
03055         class UOFS(DST):
03056             def __init__(self, uofs, dofs=None):
03057                 DST.__init__(self, dofs)
03058                 self.uofs = timedelta(minutes=uofs)
03059             def utcoffset(self, dt):
03060                 return self.uofs
03061 
03062         for dstvalue in -33, 33, 0, None:
03063             d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
03064             t = d.utctimetuple()
03065             self.assertEqual(d.year, t.tm_year)
03066             self.assertEqual(d.month, t.tm_mon)
03067             self.assertEqual(d.day, t.tm_mday)
03068             self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
03069             self.assertEqual(13, t.tm_min)
03070             self.assertEqual(d.second, t.tm_sec)
03071             self.assertEqual(d.weekday(), t.tm_wday)
03072             self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
03073                              t.tm_yday)
03074             # Ensure tm_isdst is 0 regardless of what dst() says: DST
03075             # is never in effect for a UTC time.
03076             self.assertEqual(0, t.tm_isdst)
03077 
03078         # For naive datetime, utctimetuple == timetuple except for isdst
03079         d = cls(1, 2, 3, 10, 20, 30, 40)
03080         t = d.utctimetuple()
03081         self.assertEqual(t[:-1], d.timetuple()[:-1])
03082         self.assertEqual(0, t.tm_isdst)
03083         # Same if utcoffset is None
03084         class NOFS(DST):
03085             def utcoffset(self, dt):
03086                 return None
03087         d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=NOFS())
03088         t = d.utctimetuple()
03089         self.assertEqual(t[:-1], d.timetuple()[:-1])
03090         self.assertEqual(0, t.tm_isdst)
03091         # Check that bad tzinfo is detected
03092         class BOFS(DST):
03093             def utcoffset(self, dt):
03094                 return "EST"
03095         d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=BOFS())
03096         self.assertRaises(TypeError, d.utctimetuple)
03097 
03098         # Check that utctimetuple() is the same as
03099         # astimezone(utc).timetuple()
03100         d = cls(2010, 11, 13, 14, 15, 16, 171819)
03101         for tz in [timezone.min, timezone.utc, timezone.max]:
03102             dtz = d.replace(tzinfo=tz)
03103             self.assertEqual(dtz.utctimetuple()[:-1],
03104                              dtz.astimezone(timezone.utc).timetuple()[:-1])
03105         # At the edges, UTC adjustment can produce years out-of-range
03106         # for a datetime object.  Ensure that an OverflowError is
03107         # raised.
03108         tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
03109         # That goes back 1 minute less than a full day.
03110         self.assertRaises(OverflowError, tiny.utctimetuple)
03111 
03112         huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
03113         # That goes forward 1 minute less than a full day.
03114         self.assertRaises(OverflowError, huge.utctimetuple)
03115         # More overflow cases
03116         tiny = cls.min.replace(tzinfo=timezone(MINUTE))
03117         self.assertRaises(OverflowError, tiny.utctimetuple)
03118         huge = cls.max.replace(tzinfo=timezone(-MINUTE))
03119         self.assertRaises(OverflowError, huge.utctimetuple)
03120 
03121     def test_tzinfo_isoformat(self):
03122         zero = FixedOffset(0, "+00:00")
03123         plus = FixedOffset(220, "+03:40")
03124         minus = FixedOffset(-231, "-03:51")
03125         unknown = FixedOffset(None, "")
03126 
03127         cls = self.theclass
03128         datestr = '0001-02-03'
03129         for ofs in None, zero, plus, minus, unknown:
03130             for us in 0, 987001:
03131                 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
03132                 timestr = '04:05:59' + (us and '.987001' or '')
03133                 ofsstr = ofs is not None and d.tzname() or ''
03134                 tailstr = timestr + ofsstr
03135                 iso = d.isoformat()
03136                 self.assertEqual(iso, datestr + 'T' + tailstr)
03137                 self.assertEqual(iso, d.isoformat('T'))
03138                 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
03139                 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
03140                 self.assertEqual(str(d), datestr + ' ' + tailstr)
03141 
03142     def test_replace(self):
03143         cls = self.theclass
03144         z100 = FixedOffset(100, "+100")
03145         zm200 = FixedOffset(timedelta(minutes=-200), "-200")
03146         args = [1, 2, 3, 4, 5, 6, 7, z100]
03147         base = cls(*args)
03148         self.assertEqual(base, base.replace())
03149 
03150         i = 0
03151         for name, newval in (("year", 2),
03152                              ("month", 3),
03153                              ("day", 4),
03154                              ("hour", 5),
03155                              ("minute", 6),
03156                              ("second", 7),
03157                              ("microsecond", 8),
03158                              ("tzinfo", zm200)):
03159             newargs = args[:]
03160             newargs[i] = newval
03161             expected = cls(*newargs)
03162             got = base.replace(**{name: newval})
03163             self.assertEqual(expected, got)
03164             i += 1
03165 
03166         # Ensure we can get rid of a tzinfo.
03167         self.assertEqual(base.tzname(), "+100")
03168         base2 = base.replace(tzinfo=None)
03169         self.assertTrue(base2.tzinfo is None)
03170         self.assertTrue(base2.tzname() is None)
03171 
03172         # Ensure we can add one.
03173         base3 = base2.replace(tzinfo=z100)
03174         self.assertEqual(base, base3)
03175         self.assertTrue(base.tzinfo is base3.tzinfo)
03176 
03177         # Out of bounds.
03178         base = cls(2000, 2, 29)
03179         self.assertRaises(ValueError, base.replace, year=2001)
03180 
03181     def test_more_astimezone(self):
03182         # The inherited test_astimezone covered some trivial and error cases.
03183         fnone = FixedOffset(None, "None")
03184         f44m = FixedOffset(44, "44")
03185         fm5h = FixedOffset(-timedelta(hours=5), "m300")
03186 
03187         dt = self.theclass.now(tz=f44m)
03188         self.assertTrue(dt.tzinfo is f44m)
03189         # Replacing with degenerate tzinfo raises an exception.
03190         self.assertRaises(ValueError, dt.astimezone, fnone)
03191         # Ditto with None tz.
03192         self.assertRaises(TypeError, dt.astimezone, None)
03193         # Replacing with same tzinfo makes no change.
03194         x = dt.astimezone(dt.tzinfo)
03195         self.assertTrue(x.tzinfo is f44m)
03196         self.assertEqual(x.date(), dt.date())
03197         self.assertEqual(x.time(), dt.time())
03198 
03199         # Replacing with different tzinfo does adjust.
03200         got = dt.astimezone(fm5h)
03201         self.assertTrue(got.tzinfo is fm5h)
03202         self.assertEqual(got.utcoffset(), timedelta(hours=-5))
03203         expected = dt - dt.utcoffset()  # in effect, convert to UTC
03204         expected += fm5h.utcoffset(dt)  # and from there to local time
03205         expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
03206         self.assertEqual(got.date(), expected.date())
03207         self.assertEqual(got.time(), expected.time())
03208         self.assertEqual(got.timetz(), expected.timetz())
03209         self.assertTrue(got.tzinfo is expected.tzinfo)
03210         self.assertEqual(got, expected)
03211 
03212     def test_aware_subtract(self):
03213         cls = self.theclass
03214 
03215         # Ensure that utcoffset() is ignored when the operands have the
03216         # same tzinfo member.
03217         class OperandDependentOffset(tzinfo):
03218             def utcoffset(self, t):
03219                 if t.minute < 10:
03220                     # d0 and d1 equal after adjustment
03221                     return timedelta(minutes=t.minute)
03222                 else:
03223                     # d2 off in the weeds
03224                     return timedelta(minutes=59)
03225 
03226         base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
03227         d0 = base.replace(minute=3)
03228         d1 = base.replace(minute=9)
03229         d2 = base.replace(minute=11)
03230         for x in d0, d1, d2:
03231             for y in d0, d1, d2:
03232                 got = x - y
03233                 expected = timedelta(minutes=x.minute - y.minute)
03234                 self.assertEqual(got, expected)
03235 
03236         # OTOH, if the tzinfo members are distinct, utcoffsets aren't
03237         # ignored.
03238         base = cls(8, 9, 10, 11, 12, 13, 14)
03239         d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
03240         d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
03241         d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
03242         for x in d0, d1, d2:
03243             for y in d0, d1, d2:
03244                 got = x - y
03245                 if (x is d0 or x is d1) and (y is d0 or y is d1):
03246                     expected = timedelta(0)
03247                 elif x is y is d2:
03248                     expected = timedelta(0)
03249                 elif x is d2:
03250                     expected = timedelta(minutes=(11-59)-0)
03251                 else:
03252                     assert y is d2
03253                     expected = timedelta(minutes=0-(11-59))
03254                 self.assertEqual(got, expected)
03255 
03256     def test_mixed_compare(self):
03257         t1 = datetime(1, 2, 3, 4, 5, 6, 7)
03258         t2 = datetime(1, 2, 3, 4, 5, 6, 7)
03259         self.assertEqual(t1, t2)
03260         t2 = t2.replace(tzinfo=None)
03261         self.assertEqual(t1, t2)
03262         t2 = t2.replace(tzinfo=FixedOffset(None, ""))
03263         self.assertEqual(t1, t2)
03264         t2 = t2.replace(tzinfo=FixedOffset(0, ""))
03265         self.assertRaises(TypeError, lambda: t1 == t2)
03266 
03267         # In datetime w/ identical tzinfo objects, utcoffset is ignored.
03268         class Varies(tzinfo):
03269             def __init__(self):
03270                 self.offset = timedelta(minutes=22)
03271             def utcoffset(self, t):
03272                 self.offset += timedelta(minutes=1)
03273                 return self.offset
03274 
03275         v = Varies()
03276         t1 = t2.replace(tzinfo=v)
03277         t2 = t2.replace(tzinfo=v)
03278         self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
03279         self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
03280         self.assertEqual(t1, t2)
03281 
03282         # But if they're not identical, it isn't ignored.
03283         t2 = t2.replace(tzinfo=Varies())
03284         self.assertTrue(t1 < t2)  # t1's offset counter still going up
03285 
03286     def test_subclass_datetimetz(self):
03287 
03288         class C(self.theclass):
03289             theAnswer = 42
03290 
03291             def __new__(cls, *args, **kws):
03292                 temp = kws.copy()
03293                 extra = temp.pop('extra')
03294                 result = self.theclass.__new__(cls, *args, **temp)
03295                 result.extra = extra
03296                 return result
03297 
03298             def newmeth(self, start):
03299                 return start + self.hour + self.year
03300 
03301         args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
03302 
03303         dt1 = self.theclass(*args)
03304         dt2 = C(*args, **{'extra': 7})
03305 
03306         self.assertEqual(dt2.__class__, C)
03307         self.assertEqual(dt2.theAnswer, 42)
03308         self.assertEqual(dt2.extra, 7)
03309         self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
03310         self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
03311 
03312 # Pain to set up DST-aware tzinfo classes.
03313 
03314 def first_sunday_on_or_after(dt):
03315     days_to_go = 6 - dt.weekday()
03316     if days_to_go:
03317         dt += timedelta(days_to_go)
03318     return dt
03319 
03320 ZERO = timedelta(0)
03321 MINUTE = timedelta(minutes=1)
03322 HOUR = timedelta(hours=1)
03323 DAY = timedelta(days=1)
03324 # In the US, DST starts at 2am (standard time) on the first Sunday in April.
03325 DSTSTART = datetime(1, 4, 1, 2)
03326 # and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
03327 # which is the first Sunday on or after Oct 25.  Because we view 1:MM as
03328 # being standard time on that day, there is no spelling in local time of
03329 # the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
03330 DSTEND = datetime(1, 10, 25, 1)
03331 
03332 class USTimeZone(tzinfo):
03333 
03334     def __init__(self, hours, reprname, stdname, dstname):
03335         self.stdoffset = timedelta(hours=hours)
03336         self.reprname = reprname
03337         self.stdname = stdname
03338         self.dstname = dstname
03339 
03340     def __repr__(self):
03341         return self.reprname
03342 
03343     def tzname(self, dt):
03344         if self.dst(dt):
03345             return self.dstname
03346         else:
03347             return self.stdname
03348 
03349     def utcoffset(self, dt):
03350         return self.stdoffset + self.dst(dt)
03351 
03352     def dst(self, dt):
03353         if dt is None or dt.tzinfo is None:
03354             # An exception instead may be sensible here, in one or more of
03355             # the cases.
03356             return ZERO
03357         assert dt.tzinfo is self
03358 
03359         # Find first Sunday in April.
03360         start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
03361         assert start.weekday() == 6 and start.month == 4 and start.day <= 7
03362 
03363         # Find last Sunday in October.
03364         end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
03365         assert end.weekday() == 6 and end.month == 10 and end.day >= 25
03366 
03367         # Can't compare naive to aware objects, so strip the timezone from
03368         # dt first.
03369         if start <= dt.replace(tzinfo=None) < end:
03370             return HOUR
03371         else:
03372             return ZERO
03373 
03374 Eastern  = USTimeZone(-5, "Eastern",  "EST", "EDT")
03375 Central  = USTimeZone(-6, "Central",  "CST", "CDT")
03376 Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
03377 Pacific  = USTimeZone(-8, "Pacific",  "PST", "PDT")
03378 utc_real = FixedOffset(0, "UTC", 0)
03379 # For better test coverage, we want another flavor of UTC that's west of
03380 # the Eastern and Pacific timezones.
03381 utc_fake = FixedOffset(-12*60, "UTCfake", 0)
03382 
03383 class TestTimezoneConversions(unittest.TestCase):
03384     # The DST switch times for 2002, in std time.
03385     dston = datetime(2002, 4, 7, 2)
03386     dstoff = datetime(2002, 10, 27, 1)
03387 
03388     theclass = datetime
03389 
03390     # Check a time that's inside DST.
03391     def checkinside(self, dt, tz, utc, dston, dstoff):
03392         self.assertEqual(dt.dst(), HOUR)
03393 
03394         # Conversion to our own timezone is always an identity.
03395         self.assertEqual(dt.astimezone(tz), dt)
03396 
03397         asutc = dt.astimezone(utc)
03398         there_and_back = asutc.astimezone(tz)
03399 
03400         # Conversion to UTC and back isn't always an identity here,
03401         # because there are redundant spellings (in local time) of
03402         # UTC time when DST begins:  the clock jumps from 1:59:59
03403         # to 3:00:00, and a local time of 2:MM:SS doesn't really
03404         # make sense then.  The classes above treat 2:MM:SS as
03405         # daylight time then (it's "after 2am"), really an alias
03406         # for 1:MM:SS standard time.  The latter form is what
03407         # conversion back from UTC produces.
03408         if dt.date() == dston.date() and dt.hour == 2:
03409             # We're in the redundant hour, and coming back from
03410             # UTC gives the 1:MM:SS standard-time spelling.
03411             self.assertEqual(there_and_back + HOUR, dt)
03412             # Although during was considered to be in daylight
03413             # time, there_and_back is not.
03414             self.assertEqual(there_and_back.dst(), ZERO)
03415             # They're the same times in UTC.
03416             self.assertEqual(there_and_back.astimezone(utc),
03417                              dt.astimezone(utc))
03418         else:
03419             # We're not in the redundant hour.
03420             self.assertEqual(dt, there_and_back)
03421 
03422         # Because we have a redundant spelling when DST begins, there is
03423         # (unfortunately) an hour when DST ends that can't be spelled at all in
03424         # local time.  When DST ends, the clock jumps from 1:59 back to 1:00
03425         # again.  The hour 1:MM DST has no spelling then:  1:MM is taken to be
03426         # standard time.  1:MM DST == 0:MM EST, but 0:MM is taken to be
03427         # daylight time.  The hour 1:MM daylight == 0:MM standard can't be
03428         # expressed in local time.  Nevertheless, we want conversion back
03429         # from UTC to mimic the local clock's "repeat an hour" behavior.
03430         nexthour_utc = asutc + HOUR
03431         nexthour_tz = nexthour_utc.astimezone(tz)
03432         if dt.date() == dstoff.date() and dt.hour == 0:
03433             # We're in the hour before the last DST hour.  The last DST hour
03434             # is ineffable.  We want the conversion back to repeat 1:MM.
03435             self.assertEqual(nexthour_tz, dt.replace(hour=1))
03436             nexthour_utc += HOUR
03437             nexthour_tz = nexthour_utc.astimezone(tz)
03438             self.assertEqual(nexthour_tz, dt.replace(hour=1))
03439         else:
03440             self.assertEqual(nexthour_tz - dt, HOUR)
03441 
03442     # Check a time that's outside DST.
03443     def checkoutside(self, dt, tz, utc):
03444         self.assertEqual(dt.dst(), ZERO)
03445 
03446         # Conversion to our own timezone is always an identity.
03447         self.assertEqual(dt.astimezone(tz), dt)
03448 
03449         # Converting to UTC and back is an identity too.
03450         asutc = dt.astimezone(utc)
03451         there_and_back = asutc.astimezone(tz)
03452         self.assertEqual(dt, there_and_back)
03453 
03454     def convert_between_tz_and_utc(self, tz, utc):
03455         dston = self.dston.replace(tzinfo=tz)
03456         # Because 1:MM on the day DST ends is taken as being standard time,
03457         # there is no spelling in tz for the last hour of daylight time.
03458         # For purposes of the test, the last hour of DST is 0:MM, which is
03459         # taken as being daylight time (and 1:MM is taken as being standard
03460         # time).
03461         dstoff = self.dstoff.replace(tzinfo=tz)
03462         for delta in (timedelta(weeks=13),
03463                       DAY,
03464                       HOUR,
03465                       timedelta(minutes=1),
03466                       timedelta(microseconds=1)):
03467 
03468             self.checkinside(dston, tz, utc, dston, dstoff)
03469             for during in dston + delta, dstoff - delta:
03470                 self.checkinside(during, tz, utc, dston, dstoff)
03471 
03472             self.checkoutside(dstoff, tz, utc)
03473             for outside in dston - delta, dstoff + delta:
03474                 self.checkoutside(outside, tz, utc)
03475 
03476     def test_easy(self):
03477         # Despite the name of this test, the endcases are excruciating.
03478         self.convert_between_tz_and_utc(Eastern, utc_real)
03479         self.convert_between_tz_and_utc(Pacific, utc_real)
03480         self.convert_between_tz_and_utc(Eastern, utc_fake)
03481         self.convert_between_tz_and_utc(Pacific, utc_fake)
03482         # The next is really dancing near the edge.  It works because
03483         # Pacific and Eastern are far enough apart that their "problem
03484         # hours" don't overlap.
03485         self.convert_between_tz_and_utc(Eastern, Pacific)
03486         self.convert_between_tz_and_utc(Pacific, Eastern)
03487         # OTOH, these fail!  Don't enable them.  The difficulty is that
03488         # the edge case tests assume that every hour is representable in
03489         # the "utc" class.  This is always true for a fixed-offset tzinfo
03490         # class (lke utc_real and utc_fake), but not for Eastern or Central.
03491         # For these adjacent DST-aware time zones, the range of time offsets
03492         # tested ends up creating hours in the one that aren't representable
03493         # in the other.  For the same reason, we would see failures in the
03494         # Eastern vs Pacific tests too if we added 3*HOUR to the list of
03495         # offset deltas in convert_between_tz_and_utc().
03496         #
03497         # self.convert_between_tz_and_utc(Eastern, Central)  # can't work
03498         # self.convert_between_tz_and_utc(Central, Eastern)  # can't work
03499 
03500     def test_tricky(self):
03501         # 22:00 on day before daylight starts.
03502         fourback = self.dston - timedelta(hours=4)
03503         ninewest = FixedOffset(-9*60, "-0900", 0)
03504         fourback = fourback.replace(tzinfo=ninewest)
03505         # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST.  Since it's "after
03506         # 2", we should get the 3 spelling.
03507         # If we plug 22:00 the day before into Eastern, it "looks like std
03508         # time", so its offset is returned as -5, and -5 - -9 = 4.  Adding 4
03509         # to 22:00 lands on 2:00, which makes no sense in local time (the
03510         # local clock jumps from 1 to 3).  The point here is to make sure we
03511         # get the 3 spelling.
03512         expected = self.dston.replace(hour=3)
03513         got = fourback.astimezone(Eastern).replace(tzinfo=None)
03514         self.assertEqual(expected, got)
03515 
03516         # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST.  In that
03517         # case we want the 1:00 spelling.
03518         sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
03519         # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
03520         # and adding -4-0 == -4 gives the 2:00 spelling.  We want the 1:00 EST
03521         # spelling.
03522         expected = self.dston.replace(hour=1)
03523         got = sixutc.astimezone(Eastern).replace(tzinfo=None)
03524         self.assertEqual(expected, got)
03525 
03526         # Now on the day DST ends, we want "repeat an hour" behavior.
03527         #  UTC  4:MM  5:MM  6:MM  7:MM  checking these
03528         #  EST 23:MM  0:MM  1:MM  2:MM
03529         #  EDT  0:MM  1:MM  2:MM  3:MM
03530         # wall  0:MM  1:MM  1:MM  2:MM  against these
03531         for utc in utc_real, utc_fake:
03532             for tz in Eastern, Pacific:
03533                 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
03534                 # Convert that to UTC.
03535                 first_std_hour -= tz.utcoffset(None)
03536                 # Adjust for possibly fake UTC.
03537                 asutc = first_std_hour + utc.utcoffset(None)
03538                 # First UTC hour to convert; this is 4:00 when utc=utc_real &
03539                 # tz=Eastern.
03540                 asutcbase = asutc.replace(tzinfo=utc)
03541                 for tzhour in (0, 1, 1, 2):
03542                     expectedbase = self.dstoff.replace(hour=tzhour)
03543                     for minute in 0, 30, 59:
03544                         expected = expectedbase.replace(minute=minute)
03545                         asutc = asutcbase.replace(minute=minute)
03546                         astz = asutc.astimezone(tz)
03547                         self.assertEqual(astz.replace(tzinfo=None), expected)
03548                     asutcbase += HOUR
03549 
03550 
03551     def test_bogus_dst(self):
03552         class ok(tzinfo):
03553             def utcoffset(self, dt): return HOUR
03554             def dst(self, dt): return HOUR
03555 
03556         now = self.theclass.now().replace(tzinfo=utc_real)
03557         # Doesn't blow up.
03558         now.astimezone(ok())
03559 
03560         # Does blow up.
03561         class notok(ok):
03562             def dst(self, dt): return None
03563         self.assertRaises(ValueError, now.astimezone, notok())
03564 
03565         # Sometimes blow up. In the following, tzinfo.dst()
03566         # implementation may return None or not None depending on
03567         # whether DST is assumed to be in effect.  In this situation,
03568         # a ValueError should be raised by astimezone().
03569         class tricky_notok(ok):
03570             def dst(self, dt):
03571                 if dt.year == 2000:
03572                     return None
03573                 else:
03574                     return 10*HOUR
03575         dt = self.theclass(2001, 1, 1).replace(tzinfo=utc_real)
03576         self.assertRaises(ValueError, dt.astimezone, tricky_notok())
03577 
03578     def test_fromutc(self):
03579         self.assertRaises(TypeError, Eastern.fromutc)   # not enough args
03580         now = datetime.utcnow().replace(tzinfo=utc_real)
03581         self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
03582         now = now.replace(tzinfo=Eastern)   # insert correct tzinfo
03583         enow = Eastern.fromutc(now)         # doesn't blow up
03584         self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
03585         self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
03586         self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
03587 
03588         # Always converts UTC to standard time.
03589         class FauxUSTimeZone(USTimeZone):
03590             def fromutc(self, dt):
03591                 return dt + self.stdoffset
03592         FEastern  = FauxUSTimeZone(-5, "FEastern",  "FEST", "FEDT")
03593 
03594         #  UTC  4:MM  5:MM  6:MM  7:MM  8:MM  9:MM
03595         #  EST 23:MM  0:MM  1:MM  2:MM  3:MM  4:MM
03596         #  EDT  0:MM  1:MM  2:MM  3:MM  4:MM  5:MM
03597 
03598         # Check around DST start.
03599         start = self.dston.replace(hour=4, tzinfo=Eastern)
03600         fstart = start.replace(tzinfo=FEastern)
03601         for wall in 23, 0, 1, 3, 4, 5:
03602             expected = start.replace(hour=wall)
03603             if wall == 23:
03604                 expected -= timedelta(days=1)
03605             got = Eastern.fromutc(start)
03606             self.assertEqual(expected, got)
03607 
03608             expected = fstart + FEastern.stdoffset
03609             got = FEastern.fromutc(fstart)
03610             self.assertEqual(expected, got)
03611 
03612             # Ensure astimezone() calls fromutc() too.
03613             got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
03614             self.assertEqual(expected, got)
03615 
03616             start += HOUR
03617             fstart += HOUR
03618 
03619         # Check around DST end.
03620         start = self.dstoff.replace(hour=4, tzinfo=Eastern)
03621         fstart = start.replace(tzinfo=FEastern)
03622         for wall in 0, 1, 1, 2, 3, 4:
03623             expected = start.replace(hour=wall)
03624             got = Eastern.fromutc(start)
03625             self.assertEqual(expected, got)
03626 
03627             expected = fstart + FEastern.stdoffset
03628             got = FEastern.fromutc(fstart)
03629             self.assertEqual(expected, got)
03630 
03631             # Ensure astimezone() calls fromutc() too.
03632             got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
03633             self.assertEqual(expected, got)
03634 
03635             start += HOUR
03636             fstart += HOUR
03637 
03638 
03639 #############################################################################
03640 # oddballs
03641 
03642 class Oddballs(unittest.TestCase):
03643 
03644     def test_bug_1028306(self):
03645         # Trying to compare a date to a datetime should act like a mixed-
03646         # type comparison, despite that datetime is a subclass of date.
03647         as_date = date.today()
03648         as_datetime = datetime.combine(as_date, time())
03649         self.assertTrue(as_date != as_datetime)
03650         self.assertTrue(as_datetime != as_date)
03651         self.assertTrue(not as_date == as_datetime)
03652         self.assertTrue(not as_datetime == as_date)
03653         self.assertRaises(TypeError, lambda: as_date < as_datetime)
03654         self.assertRaises(TypeError, lambda: as_datetime < as_date)
03655         self.assertRaises(TypeError, lambda: as_date <= as_datetime)
03656         self.assertRaises(TypeError, lambda: as_datetime <= as_date)
03657         self.assertRaises(TypeError, lambda: as_date > as_datetime)
03658         self.assertRaises(TypeError, lambda: as_datetime > as_date)
03659         self.assertRaises(TypeError, lambda: as_date >= as_datetime)
03660         self.assertRaises(TypeError, lambda: as_datetime >= as_date)
03661 
03662         # Neverthelss, comparison should work with the base-class (date)
03663         # projection if use of a date method is forced.
03664         self.assertEqual(as_date.__eq__(as_datetime), True)
03665         different_day = (as_date.day + 1) % 20 + 1
03666         as_different = as_datetime.replace(day= different_day)
03667         self.assertEqual(as_date.__eq__(as_different), False)
03668 
03669         # And date should compare with other subclasses of date.  If a
03670         # subclass wants to stop this, it's up to the subclass to do so.
03671         date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
03672         self.assertEqual(as_date, date_sc)
03673         self.assertEqual(date_sc, as_date)
03674 
03675         # Ditto for datetimes.
03676         datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
03677                                        as_date.day, 0, 0, 0)
03678         self.assertEqual(as_datetime, datetime_sc)
03679         self.assertEqual(datetime_sc, as_datetime)
03680 
03681 def test_main():
03682     support.run_unittest(__name__)
03683 
03684 if __name__ == "__main__":
03685     test_main()