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