home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 June / PCWorld_2005-06_cd.bin / software / vyzkuste / firewally / firewally.exe / framework-2.3.exe / test_datetime.py < prev    next >
Text File  |  2003-12-30  |  124KB  |  3,114 lines

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