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