github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/third_party/stdlib/test/test_datetime.py (about)

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