0001 """Test date/time type. 0002 0003 See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases 0004 """ 0005 0006 import sys 0007 import pickle 0008 import cPickle 0009 import unittest 0010 0011 from test import test_support 0012 0013 from datetime import MINYEAR, MAXYEAR 0014 from datetime import timedelta 0015 from datetime import tzinfo 0016 from datetime import time 0017 from datetime import date, datetime 0018 0019 pickle_choices = [(pickler, unpickler, proto) 0020 for pickler in pickle, cPickle 0021 for unpickler in pickle, cPickle 0022 for proto in range(3)] 0023 assert len(pickle_choices) == 2*2*3 0024 0025 # An arbitrary collection of objects of non-datetime types, for testing 0026 # mixed-type comparisons. 0027 OTHERSTUFF = (10, 10L, 34.5, "abc", {}, [], ()) 0028 0029 0030 ############################################################################# 0031 # module tests 0032 0033 class TestModule(unittest.TestCase): 0034 0035 def test_constants(self): 0036 import datetime 0037 self.assertEqual(datetime.MINYEAR, 1) 0038 self.assertEqual(datetime.MAXYEAR, 9999) 0039 0040 ############################################################################# 0041 # tzinfo tests 0042 0043 class FixedOffset(tzinfo): 0044 def __init__(self, offset, name, dstoffset=42): 0045 if isinstance(offset, int): 0046 offset = timedelta(minutes=offset) 0047 if isinstance(dstoffset, int): 0048 dstoffset = timedelta(minutes=dstoffset) 0049 self.__offset = offset 0050 self.__name = name 0051 self.__dstoffset = dstoffset 0052 def __repr__(self): 0053 return self.__name.lower() 0054 def utcoffset(self, dt): 0055 return self.__offset 0056 def tzname(self, dt): 0057 return self.__name 0058 def dst(self, dt): 0059 return self.__dstoffset 0060 0061 class PicklableFixedOffset(FixedOffset): 0062 def __init__(self, offset=None, name=None, dstoffset=None): 0063 FixedOffset.__init__(self, offset, name, dstoffset) 0064 0065 class TestTZInfo(unittest.TestCase): 0066 0067 def test_non_abstractness(self): 0068 # In order to allow subclasses to get pickled, the C implementation 0069 # wasn't able to get away with having __init__ raise 0070 # NotImplementedError. 0071 useless = tzinfo() 0072 dt = datetime.max 0073 self.assertRaises(NotImplementedError, useless.tzname, dt) 0074 self.assertRaises(NotImplementedError, useless.utcoffset, dt) 0075 self.assertRaises(NotImplementedError, useless.dst, dt) 0076 0077 def test_subclass_must_override(self): 0078 class NotEnough(tzinfo): 0079 def __init__(self, offset, name): 0080 self.__offset = offset 0081 self.__name = name 0082 self.failUnless(issubclass(NotEnough, tzinfo)) 0083 ne = NotEnough(3, "NotByALongShot") 0084 self.failUnless(isinstance(ne, tzinfo)) 0085 0086 dt = datetime.now() 0087 self.assertRaises(NotImplementedError, ne.tzname, dt) 0088 self.assertRaises(NotImplementedError, ne.utcoffset, dt) 0089 self.assertRaises(NotImplementedError, ne.dst, dt) 0090 0091 def test_normal(self): 0092 fo = FixedOffset(3, "Three") 0093 self.failUnless(isinstance(fo, tzinfo)) 0094 for dt in datetime.now(), None: 0095 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3)) 0096 self.assertEqual(fo.tzname(dt), "Three") 0097 self.assertEqual(fo.dst(dt), timedelta(minutes=42)) 0098 0099 def test_pickling_base(self): 0100 # There's no point to pickling tzinfo objects on their own (they 0101 # carry no data), but they need to be picklable anyway else 0102 # concrete subclasses can't be pickled. 0103 orig = tzinfo.__new__(tzinfo) 0104 self.failUnless(type(orig) is tzinfo) 0105 for pickler, unpickler, proto in pickle_choices: 0106 green = pickler.dumps(orig, proto) 0107 derived = unpickler.loads(green) 0108 self.failUnless(type(derived) is tzinfo) 0109 0110 def test_pickling_subclass(self): 0111 # Make sure we can pickle/unpickle an instance of a subclass. 0112 offset = timedelta(minutes=-300) 0113 orig = PicklableFixedOffset(offset, 'cookie') 0114 self.failUnless(isinstance(orig, tzinfo)) 0115 self.failUnless(type(orig) is PicklableFixedOffset) 0116 self.assertEqual(orig.utcoffset(None), offset) 0117 self.assertEqual(orig.tzname(None), 'cookie') 0118 for pickler, unpickler, proto in pickle_choices: 0119 green = pickler.dumps(orig, proto) 0120 derived = unpickler.loads(green) 0121 self.failUnless(isinstance(derived, tzinfo)) 0122 self.failUnless(type(derived) is PicklableFixedOffset) 0123 self.assertEqual(derived.utcoffset(None), offset) 0124 self.assertEqual(derived.tzname(None), 'cookie') 0125 0126 ############################################################################# 0127 # Base clase for testing a particular aspect of timedelta, time, date and 0128 # datetime comparisons. 0129 0130 class HarmlessMixedComparison(unittest.TestCase): 0131 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons. 0132 0133 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a 0134 # legit constructor. 0135 0136 def test_harmless_mixed_comparison(self): 0137 me = self.theclass(1, 1, 1) 0138 0139 self.failIf(me == ()) 0140 self.failUnless(me != ()) 0141 self.failIf(() == me) 0142 self.failUnless(() != me) 0143 0144 self.failUnless(me in [1, 20L, [], me]) 0145 self.failIf(me not in [1, 20L, [], me]) 0146 0147 self.failUnless([] in [me, 1, 20L, []]) 0148 self.failIf([] not in [me, 1, 20L, []]) 0149 0150 def test_harmful_mixed_comparison(self): 0151 me = self.theclass(1, 1, 1) 0152 0153 self.assertRaises(TypeError, lambda: me < ()) 0154 self.assertRaises(TypeError, lambda: me <= ()) 0155 self.assertRaises(TypeError, lambda: me > ()) 0156 self.assertRaises(TypeError, lambda: me >= ()) 0157 0158 self.assertRaises(TypeError, lambda: () < me) 0159 self.assertRaises(TypeError, lambda: () <= me) 0160 self.assertRaises(TypeError, lambda: () > me) 0161 self.assertRaises(TypeError, lambda: () >= me) 0162 0163 self.assertRaises(TypeError, cmp, (), me) 0164 self.assertRaises(TypeError, cmp, me, ()) 0165 0166 ############################################################################# 0167 # timedelta tests 0168 0169 class TestTimeDelta(HarmlessMixedComparison): 0170 0171 theclass = timedelta 0172 0173 def test_constructor(self): 0174 eq = self.assertEqual 0175 td = timedelta 0176 0177 # Check keyword args to constructor 0178 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0, 0179 milliseconds=0, microseconds=0)) 0180 eq(td(1), td(days=1)) 0181 eq(td(0, 1), td(seconds=1)) 0182 eq(td(0, 0, 1), td(microseconds=1)) 0183 eq(td(weeks=1), td(days=7)) 0184 eq(td(days=1), td(hours=24)) 0185 eq(td(hours=1), td(minutes=60)) 0186 eq(td(minutes=1), td(seconds=60)) 0187 eq(td(seconds=1), td(milliseconds=1000)) 0188 eq(td(milliseconds=1), td(microseconds=1000)) 0189 0190 # Check float args to constructor 0191 eq(td(weeks=1.0/7), td(days=1)) 0192 eq(td(days=1.0/24), td(hours=1)) 0193 eq(td(hours=1.0/60), td(minutes=1)) 0194 eq(td(minutes=1.0/60), td(seconds=1)) 0195 eq(td(seconds=0.001), td(milliseconds=1)) 0196 eq(td(milliseconds=0.001), td(microseconds=1)) 0197 0198 def test_computations(self): 0199 eq = self.assertEqual 0200 td = timedelta 0201 0202 a = td(7) # One week 0203 b = td(0, 60) # One minute 0204 c = td(0, 0, 1000) # One millisecond 0205 eq(a+b+c, td(7, 60, 1000)) 0206 eq(a-b, td(6, 24*3600 - 60)) 0207 eq(-a, td(-7)) 0208 eq(+a, td(7)) 0209 eq(-b, td(-1, 24*3600 - 60)) 0210 eq(-c, td(-1, 24*3600 - 1, 999000)) 0211 eq(abs(a), a) 0212 eq(abs(-a), a) 0213 eq(td(6, 24*3600), a) 0214 eq(td(0, 0, 60*1000000), b) 0215 eq(a*10, td(70)) 0216 eq(a*10, 10*a) 0217 eq(a*10L, 10*a) 0218 eq(b*10, td(0, 600)) 0219 eq(10*b, td(0, 600)) 0220 eq(b*10L, td(0, 600)) 0221 eq(c*10, td(0, 0, 10000)) 0222 eq(10*c, td(0, 0, 10000)) 0223 eq(c*10L, td(0, 0, 10000)) 0224 eq(a*-1, -a) 0225 eq(b*-2, -b-b) 0226 eq(c*-2, -c+-c) 0227 eq(b*(60*24), (b*60)*24) 0228 eq(b*(60*24), (60*b)*24) 0229 eq(c*1000, td(0, 1)) 0230 eq(1000*c, td(0, 1)) 0231 eq(a//7, td(1)) 0232 eq(b//10, td(0, 6)) 0233 eq(c//1000, td(0, 0, 1)) 0234 eq(a//10, td(0, 7*24*360)) 0235 eq(a//3600000, td(0, 0, 7*24*1000)) 0236 0237 def test_disallowed_computations(self): 0238 a = timedelta(42) 0239 0240 # Add/sub ints, longs, floats should be illegal 0241 for i in 1, 1L, 1.0: 0242 self.assertRaises(TypeError, lambda: a+i) 0243 self.assertRaises(TypeError, lambda: a-i) 0244 self.assertRaises(TypeError, lambda: i+a) 0245 self.assertRaises(TypeError, lambda: i-a) 0246 0247 # Mul/div by float isn't supported. 0248 x = 2.3 0249 self.assertRaises(TypeError, lambda: a*x) 0250 self.assertRaises(TypeError, lambda: x*a) 0251 self.assertRaises(TypeError, lambda: a/x) 0252 self.assertRaises(TypeError, lambda: x/a) 0253 self.assertRaises(TypeError, lambda: a // x) 0254 self.assertRaises(TypeError, lambda: x // a) 0255 0256 # Divison of int by timedelta doesn't make sense. 0257 # Division by zero doesn't make sense. 0258 for zero in 0, 0L: 0259 self.assertRaises(TypeError, lambda: zero // a) 0260 self.assertRaises(ZeroDivisionError, lambda: a // zero) 0261 0262 def test_basic_attributes(self): 0263 days, seconds, us = 1, 7, 31 0264 td = timedelta(days, seconds, us) 0265 self.assertEqual(td.days, days) 0266 self.assertEqual(td.seconds, seconds) 0267 self.assertEqual(td.microseconds, us) 0268 0269 def test_carries(self): 0270 t1 = timedelta(days=100, 0271 weeks=-7, 0272 hours=-24*(100-49), 0273 minutes=-3, 0274 seconds=12, 0275 microseconds=(3*60 - 12) * 1e6 + 1) 0276 t2 = timedelta(microseconds=1) 0277 self.assertEqual(t1, t2) 0278 0279 def test_hash_equality(self): 0280 t1 = timedelta(days=100, 0281 weeks=-7, 0282 hours=-24*(100-49), 0283 minutes=-3, 0284 seconds=12, 0285 microseconds=(3*60 - 12) * 1000000) 0286 t2 = timedelta() 0287 self.assertEqual(hash(t1), hash(t2)) 0288 0289 t1 += timedelta(weeks=7) 0290 t2 += timedelta(days=7*7) 0291 self.assertEqual(t1, t2) 0292 self.assertEqual(hash(t1), hash(t2)) 0293 0294 d = {t1: 1} 0295 d[t2] = 2 0296 self.assertEqual(len(d), 1) 0297 self.assertEqual(d[t1], 2) 0298 0299 def test_pickling(self): 0300 args = 12, 34, 56 0301 orig = timedelta(*args) 0302 for pickler, unpickler, proto in pickle_choices: 0303 green = pickler.dumps(orig, proto) 0304 derived = unpickler.loads(green) 0305 self.assertEqual(orig, derived) 0306 0307 def test_compare(self): 0308 t1 = timedelta(2, 3, 4) 0309 t2 = timedelta(2, 3, 4) 0310 self.failUnless(t1 == t2) 0311 self.failUnless(t1 <= t2) 0312 self.failUnless(t1 >= t2) 0313 self.failUnless(not t1 != t2) 0314 self.failUnless(not t1 < t2) 0315 self.failUnless(not t1 > t2) 0316 self.assertEqual(cmp(t1, t2), 0) 0317 self.assertEqual(cmp(t2, t1), 0) 0318 0319 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5): 0320 t2 = timedelta(*args) # this is larger than t1 0321 self.failUnless(t1 < t2) 0322 self.failUnless(t2 > t1) 0323 self.failUnless(t1 <= t2) 0324 self.failUnless(t2 >= t1) 0325 self.failUnless(t1 != t2) 0326 self.failUnless(t2 != t1) 0327 self.failUnless(not t1 == t2) 0328 self.failUnless(not t2 == t1) 0329 self.failUnless(not t1 > t2) 0330 self.failUnless(not t2 < t1) 0331 self.failUnless(not t1 >= t2) 0332 self.failUnless(not t2 <= t1) 0333 self.assertEqual(cmp(t1, t2), -1) 0334 self.assertEqual(cmp(t2, t1), 1) 0335 0336 for badarg in OTHERSTUFF: 0337 self.assertEqual(t1 == badarg, False) 0338 self.assertEqual(t1 != badarg, True) 0339 self.assertEqual(badarg == t1, False) 0340 self.assertEqual(badarg != t1, True) 0341 0342 self.assertRaises(TypeError, lambda: t1 <= badarg) 0343 self.assertRaises(TypeError, lambda: t1 < badarg) 0344 self.assertRaises(TypeError, lambda: t1 > badarg) 0345 self.assertRaises(TypeError, lambda: t1 >= badarg) 0346 self.assertRaises(TypeError, lambda: badarg <= t1) 0347 self.assertRaises(TypeError, lambda: badarg < t1) 0348 self.assertRaises(TypeError, lambda: badarg > t1) 0349 self.assertRaises(TypeError, lambda: badarg >= t1) 0350 0351 def test_str(self): 0352 td = timedelta 0353 eq = self.assertEqual 0354 0355 eq(str(td(1)), "1 day, 0:00:00") 0356 eq(str(td(-1)), "-1 day, 0:00:00") 0357 eq(str(td(2)), "2 days, 0:00:00") 0358 eq(str(td(-2)), "-2 days, 0:00:00") 0359 0360 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59") 0361 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04") 0362 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)), 0363 "-210 days, 23:12:34") 0364 0365 eq(str(td(milliseconds=1)), "0:00:00.001000") 0366 eq(str(td(microseconds=3)), "0:00:00.000003") 0367 0368 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59, 0369 microseconds=999999)), 0370 "999999999 days, 23:59:59.999999") 0371 0372 def test_roundtrip(self): 0373 for td in (timedelta(days=999999999, hours=23, minutes=59, 0374 seconds=59, microseconds=999999), 0375 timedelta(days=-999999999), 0376 timedelta(days=1, seconds=2, microseconds=3)): 0377 0378 # Verify td -> string -> td identity. 0379 s = repr(td) 0380 self.failUnless(s.startswith('datetime.')) 0381 s = s[9:] 0382 td2 = eval(s) 0383 self.assertEqual(td, td2) 0384 0385 # Verify identity via reconstructing from pieces. 0386 td2 = timedelta(td.days, td.seconds, td.microseconds) 0387 self.assertEqual(td, td2) 0388 0389 def test_resolution_info(self): 0390 self.assert_(isinstance(timedelta.min, timedelta)) 0391 self.assert_(isinstance(timedelta.max, timedelta)) 0392 self.assert_(isinstance(timedelta.resolution, timedelta)) 0393 self.assert_(timedelta.max > timedelta.min) 0394 self.assertEqual(timedelta.min, timedelta(-999999999)) 0395 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1)) 0396 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1)) 0397 0398 def test_overflow(self): 0399 tiny = timedelta.resolution 0400 0401 td = timedelta.min + tiny 0402 td -= tiny # no problem 0403 self.assertRaises(OverflowError, td.__sub__, tiny) 0404 self.assertRaises(OverflowError, td.__add__, -tiny) 0405 0406 td = timedelta.max - tiny 0407 td += tiny # no problem 0408 self.assertRaises(OverflowError, td.__add__, tiny) 0409 self.assertRaises(OverflowError, td.__sub__, -tiny) 0410 0411 self.assertRaises(OverflowError, lambda: -timedelta.max) 0412 0413 def test_microsecond_rounding(self): 0414 td = timedelta 0415 eq = self.assertEqual 0416 0417 # Single-field rounding. 0418 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0 0419 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0 0420 eq(td(milliseconds=0.6/1000), td(microseconds=1)) 0421 eq(td(milliseconds=-0.6/1000), td(microseconds=-1)) 0422 0423 # Rounding due to contributions from more than one field. 0424 us_per_hour = 3600e6 0425 us_per_day = us_per_hour * 24 0426 eq(td(days=.4/us_per_day), td(0)) 0427 eq(td(hours=.2/us_per_hour), td(0)) 0428 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1)) 0429 0430 eq(td(days=-.4/us_per_day), td(0)) 0431 eq(td(hours=-.2/us_per_hour), td(0)) 0432 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1)) 0433 0434 def test_massive_normalization(self): 0435 td = timedelta(microseconds=-1) 0436 self.assertEqual((td.days, td.seconds, td.microseconds), 0437 (-1, 24*3600-1, 999999)) 0438 0439 def test_bool(self): 0440 self.failUnless(timedelta(1)) 0441 self.failUnless(timedelta(0, 1)) 0442 self.failUnless(timedelta(0, 0, 1)) 0443 self.failUnless(timedelta(microseconds=1)) 0444 self.failUnless(not timedelta(0)) 0445 0446 def test_subclass_timedelta(self): 0447 0448 class T(timedelta): 0449 def from_td(td): 0450 return T(td.days, td.seconds, td.microseconds) 0451 from_td = staticmethod(from_td) 0452 0453 def as_hours(self): 0454 sum = (self.days * 24 + 0455 self.seconds / 3600.0 + 0456 self.microseconds / 3600e6) 0457 return round(sum) 0458 0459 t1 = T(days=1) 0460 self.assert_(type(t1) is T) 0461 self.assertEqual(t1.as_hours(), 24) 0462 0463 t2 = T(days=-1, seconds=-3600) 0464 self.assert_(type(t2) is T) 0465 self.assertEqual(t2.as_hours(), -25) 0466 0467 t3 = t1 + t2 0468 self.assert_(type(t3) is timedelta) 0469 t4 = T.from_td(t3) 0470 self.assert_(type(t4) is T) 0471 self.assertEqual(t3.days, t4.days) 0472 self.assertEqual(t3.seconds, t4.seconds) 0473 self.assertEqual(t3.microseconds, t4.microseconds) 0474 self.assertEqual(str(t3), str(t4)) 0475 self.assertEqual(t4.as_hours(), -1) 0476 0477 ############################################################################# 0478 # date tests 0479 0480 class TestDateOnly(unittest.TestCase): 0481 # Tests here won't pass if also run on datetime objects, so don't 0482 # subclass this to test datetimes too. 0483 0484 def test_delta_non_days_ignored(self): 0485 dt = date(2000, 1, 2) 0486 delta = timedelta(days=1, hours=2, minutes=3, seconds=4, 0487 microseconds=5) 0488 days = timedelta(delta.days) 0489 self.assertEqual(days, timedelta(1)) 0490 0491 dt2 = dt + delta 0492 self.assertEqual(dt2, dt + days) 0493 0494 dt2 = delta + dt 0495 self.assertEqual(dt2, dt + days) 0496 0497 dt2 = dt - delta 0498 self.assertEqual(dt2, dt - days) 0499 0500 delta = -delta 0501 days = timedelta(delta.days) 0502 self.assertEqual(days, timedelta(-2)) 0503 0504 dt2 = dt + delta 0505 self.assertEqual(dt2, dt + days) 0506 0507 dt2 = delta + dt 0508 self.assertEqual(dt2, dt + days) 0509 0510 dt2 = dt - delta 0511 self.assertEqual(dt2, dt - days) 0512 0513 class SubclassDate(date): 0514 sub_var = 1 0515 0516 class TestDate(HarmlessMixedComparison): 0517 # Tests here should pass for both dates and datetimes, except for a 0518 # few tests that TestDateTime overrides. 0519 0520 theclass = date 0521 0522 def test_basic_attributes(self): 0523 dt = self.theclass(2002, 3, 1) 0524 self.assertEqual(dt.year, 2002) 0525 self.assertEqual(dt.month, 3) 0526 self.assertEqual(dt.day, 1) 0527 0528 def test_roundtrip(self): 0529 for dt in (self.theclass(1, 2, 3), 0530 self.theclass.today()): 0531 # Verify dt -> string -> date identity. 0532 s = repr(dt) 0533 self.failUnless(s.startswith('datetime.')) 0534 s = s[9:] 0535 dt2 = eval(s) 0536 self.assertEqual(dt, dt2) 0537 0538 # Verify identity via reconstructing from pieces. 0539 dt2 = self.theclass(dt.year, dt.month, dt.day) 0540 self.assertEqual(dt, dt2) 0541 0542 def test_ordinal_conversions(self): 0543 # Check some fixed values. 0544 for y, m, d, n in [(1, 1, 1, 1), # calendar origin 0545 (1, 12, 31, 365), 0546 (2, 1, 1, 366), 0547 # first example from "Calendrical Calculations" 0548 (1945, 11, 12, 710347)]: 0549 d = self.theclass(y, m, d) 0550 self.assertEqual(n, d.toordinal()) 0551 fromord = self.theclass.fromordinal(n) 0552 self.assertEqual(d, fromord) 0553 if hasattr(fromord, "hour"): 0554 # if we're checking something fancier than a date, verify 0555 # the extra fields have been zeroed out 0556 self.assertEqual(fromord.hour, 0) 0557 self.assertEqual(fromord.minute, 0) 0558 self.assertEqual(fromord.second, 0) 0559 self.assertEqual(fromord.microsecond, 0) 0560 0561 # Check first and last days of year spottily across the whole 0562 # range of years supported. 0563 for year in xrange(MINYEAR, MAXYEAR+1, 7): 0564 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity. 0565 d = self.theclass(year, 1, 1) 0566 n = d.toordinal() 0567 d2 = self.theclass.fromordinal(n) 0568 self.assertEqual(d, d2) 0569 # Verify that moving back a day gets to the end of year-1. 0570 if year > 1: 0571 d = self.theclass.fromordinal(n-1) 0572 d2 = self.theclass(year-1, 12, 31) 0573 self.assertEqual(d, d2) 0574 self.assertEqual(d2.toordinal(), n-1) 0575 0576 # Test every day in a leap-year and a non-leap year. 0577 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 0578 for year, isleap in (2000, True), (2002, False): 0579 n = self.theclass(year, 1, 1).toordinal() 0580 for month, maxday in zip(range(1, 13), dim): 0581 if month == 2 and isleap: 0582 maxday += 1 0583 for day in range(1, maxday+1): 0584 d = self.theclass(year, month, day) 0585 self.assertEqual(d.toordinal(), n) 0586 self.assertEqual(d, self.theclass.fromordinal(n)) 0587 n += 1 0588 0589 def test_extreme_ordinals(self): 0590 a = self.theclass.min 0591 a = self.theclass(a.year, a.month, a.day) # get rid of time parts 0592 aord = a.toordinal() 0593 b = a.fromordinal(aord) 0594 self.assertEqual(a, b) 0595 0596 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1)) 0597 0598 b = a + timedelta(days=1) 0599 self.assertEqual(b.toordinal(), aord + 1) 0600 self.assertEqual(b, self.theclass.fromordinal(aord + 1)) 0601 0602 a = self.theclass.max 0603 a = self.theclass(a.year, a.month, a.day) # get rid of time parts 0604 aord = a.toordinal() 0605 b = a.fromordinal(aord) 0606 self.assertEqual(a, b) 0607 0608 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1)) 0609 0610 b = a - timedelta(days=1) 0611 self.assertEqual(b.toordinal(), aord - 1) 0612 self.assertEqual(b, self.theclass.fromordinal(aord - 1)) 0613 0614 def test_bad_constructor_arguments(self): 0615 # bad years 0616 self.theclass(MINYEAR, 1, 1) # no exception 0617 self.theclass(MAXYEAR, 1, 1) # no exception 0618 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1) 0619 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1) 0620 # bad months 0621 self.theclass(2000, 1, 1) # no exception 0622 self.theclass(2000, 12, 1) # no exception 0623 self.assertRaises(ValueError, self.theclass, 2000, 0, 1) 0624 self.assertRaises(ValueError, self.theclass, 2000, 13, 1) 0625 # bad days 0626 self.theclass(2000, 2, 29) # no exception 0627 self.theclass(2004, 2, 29) # no exception 0628 self.theclass(2400, 2, 29) # no exception 0629 self.assertRaises(ValueError, self.theclass, 2000, 2, 30) 0630 self.assertRaises(ValueError, self.theclass, 2001, 2, 29) 0631 self.assertRaises(ValueError, self.theclass, 2100, 2, 29) 0632 self.assertRaises(ValueError, self.theclass, 1900, 2, 29) 0633 self.assertRaises(ValueError, self.theclass, 2000, 1, 0) 0634 self.assertRaises(ValueError, self.theclass, 2000, 1, 32) 0635 0636 def test_hash_equality(self): 0637 d = self.theclass(2000, 12, 31) 0638 # same thing 0639 e = self.theclass(2000, 12, 31) 0640 self.assertEqual(d, e) 0641 self.assertEqual(hash(d), hash(e)) 0642 0643 dic = {d: 1} 0644 dic[e] = 2 0645 self.assertEqual(len(dic), 1) 0646 self.assertEqual(dic[d], 2) 0647 self.assertEqual(dic[e], 2) 0648 0649 d = self.theclass(2001, 1, 1) 0650 # same thing 0651 e = self.theclass(2001, 1, 1) 0652 self.assertEqual(d, e) 0653 self.assertEqual(hash(d), hash(e)) 0654 0655 dic = {d: 1} 0656 dic[e] = 2 0657 self.assertEqual(len(dic), 1) 0658 self.assertEqual(dic[d], 2) 0659 self.assertEqual(dic[e], 2) 0660 0661 def test_computations(self): 0662 a = self.theclass(2002, 1, 31) 0663 b = self.theclass(1956, 1, 31) 0664 0665 diff = a-b 0666 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4))) 0667 self.assertEqual(diff.seconds, 0) 0668 self.assertEqual(diff.microseconds, 0) 0669 0670 day = timedelta(1) 0671 week = timedelta(7) 0672 a = self.theclass(2002, 3, 2) 0673 self.assertEqual(a + day, self.theclass(2002, 3, 3)) 0674 self.assertEqual(day + a, self.theclass(2002, 3, 3)) 0675 self.assertEqual(a - day, self.theclass(2002, 3, 1)) 0676 self.assertEqual(-day + a, self.theclass(2002, 3, 1)) 0677 self.assertEqual(a + week, self.theclass(2002, 3, 9)) 0678 self.assertEqual(a - week, self.theclass(2002, 2, 23)) 0679 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1)) 0680 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3)) 0681 self.assertEqual((a + week) - a, week) 0682 self.assertEqual((a + day) - a, day) 0683 self.assertEqual((a - week) - a, -week) 0684 self.assertEqual((a - day) - a, -day) 0685 self.assertEqual(a - (a + week), -week) 0686 self.assertEqual(a - (a + day), -day) 0687 self.assertEqual(a - (a - week), week) 0688 self.assertEqual(a - (a - day), day) 0689 0690 # Add/sub ints, longs, floats should be illegal 0691 for i in 1, 1L, 1.0: 0692 self.assertRaises(TypeError, lambda: a+i) 0693 self.assertRaises(TypeError, lambda: a-i) 0694 self.assertRaises(TypeError, lambda: i+a) 0695 self.assertRaises(TypeError, lambda: i-a) 0696 0697 # delta - date is senseless. 0698 self.assertRaises(TypeError, lambda: day - a) 0699 # mixing date and (delta or date) via * or // is senseless 0700 self.assertRaises(TypeError, lambda: day * a) 0701 self.assertRaises(TypeError, lambda: a * day) 0702 self.assertRaises(TypeError, lambda: day // a) 0703 self.assertRaises(TypeError, lambda: a // day) 0704 self.assertRaises(TypeError, lambda: a * a) 0705 self.assertRaises(TypeError, lambda: a // a) 0706 # date + date is senseless 0707 self.assertRaises(TypeError, lambda: a + a) 0708 0709 def test_overflow(self): 0710 tiny = self.theclass.resolution 0711 0712 dt = self.theclass.min + tiny 0713 dt -= tiny # no problem 0714 self.assertRaises(OverflowError, dt.__sub__, tiny) 0715 self.assertRaises(OverflowError, dt.__add__, -tiny) 0716 0717 dt = self.theclass.max - tiny 0718 dt += tiny # no problem 0719 self.assertRaises(OverflowError, dt.__add__, tiny) 0720 self.assertRaises(OverflowError, dt.__sub__, -tiny) 0721 0722 def test_fromtimestamp(self): 0723 import time 0724 0725 # Try an arbitrary fixed value. 0726 year, month, day = 1999, 9, 19 0727 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1)) 0728 d = self.theclass.fromtimestamp(ts) 0729 self.assertEqual(d.year, year) 0730 self.assertEqual(d.month, month) 0731 self.assertEqual(d.day, day) 0732 0733 def test_insane_fromtimestamp(self): 0734 # It's possible that some platform maps time_t to double, 0735 # and that this test will fail there. This test should 0736 # exempt such platforms (provided they return reasonable 0737 # results!). 0738 for insane in -1e200, 1e200: 0739 self.assertRaises(ValueError, self.theclass.fromtimestamp, 0740 insane) 0741 0742 def test_today(self): 0743 import time 0744 0745 # We claim that today() is like fromtimestamp(time.time()), so 0746 # prove it. 0747 for dummy in range(3): 0748 today = self.theclass.today() 0749 ts = time.time() 0750 todayagain = self.theclass.fromtimestamp(ts) 0751 if today == todayagain: 0752 break 0753 # There are several legit reasons that could fail: 0754 # 1. It recently became midnight, between the today() and the 0755 # time() calls. 0756 # 2. The platform time() has such fine resolution that we'll 0757 # never get the same value twice. 0758 # 3. The platform time() has poor resolution, and we just 0759 # happened to call today() right before a resolution quantum 0760 # boundary. 0761 # 4. The system clock got fiddled between calls. 0762 # In any case, wait a little while and try again. 0763 time.sleep(0.1) 0764 0765 # It worked or it didn't. If it didn't, assume it's reason #2, and 0766 # let the test pass if they're within half a second of each other. 0767 self.failUnless(today == todayagain or 0768 abs(todayagain - today) < timedelta(seconds=0.5)) 0769 0770 def test_weekday(self): 0771 for i in range(7): 0772 # March 4, 2002 is a Monday 0773 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i) 0774 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1) 0775 # January 2, 1956 is a Monday 0776 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i) 0777 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1) 0778 0779 def test_isocalendar(self): 0780 # Check examples from 0781 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm 0782 for i in range(7): 0783 d = self.theclass(2003, 12, 22+i) 0784 self.assertEqual(d.isocalendar(), (2003, 52, i+1)) 0785 d = self.theclass(2003, 12, 29) + timedelta(i) 0786 self.assertEqual(d.isocalendar(), (2004, 1, i+1)) 0787 d = self.theclass(2004, 1, 5+i) 0788 self.assertEqual(d.isocalendar(), (2004, 2, i+1)) 0789 d = self.theclass(2009, 12, 21+i) 0790 self.assertEqual(d.isocalendar(), (2009, 52, i+1)) 0791 d = self.theclass(2009, 12, 28) + timedelta(i) 0792 self.assertEqual(d.isocalendar(), (2009, 53, i+1)) 0793 d = self.theclass(2010, 1, 4+i) 0794 self.assertEqual(d.isocalendar(), (2010, 1, i+1)) 0795 0796 def test_iso_long_years(self): 0797 # Calculate long ISO years and compare to table from 0798 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm 0799 ISO_LONG_YEARS_TABLE = """ 0800 4 32 60 88 0801 9 37 65 93 0802 15 43 71 99 0803 20 48 76 0804 26 54 82 0805 0806 105 133 161 189 0807 111 139 167 195 0808 116 144 172 0809 122 150 178 0810 128 156 184 0811 0812 201 229 257 285 0813 207 235 263 291 0814 212 240 268 296 0815 218 246 274 0816 224 252 280 0817 0818 303 331 359 387 0819 308 336 364 392 0820 314 342 370 398 0821 320 348 376 0822 325 353 381 0823 """ 0824 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split()) 0825 iso_long_years.sort() 0826 L = [] 0827 for i in range(400): 0828 d = self.theclass(2000+i, 12, 31) 0829 d1 = self.theclass(1600+i, 12, 31) 0830 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:]) 0831 if d.isocalendar()[1] == 53: 0832 L.append(i) 0833 self.assertEqual(L, iso_long_years) 0834 0835 def test_isoformat(self): 0836 t = self.theclass(2, 3, 2) 0837 self.assertEqual(t.isoformat(), "0002-03-02") 0838 0839 def test_ctime(self): 0840 t = self.theclass(2002, 3, 2) 0841 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002") 0842 0843 def test_strftime(self): 0844 t = self.theclass(2005, 3, 2) 0845 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05") 0846 self.assertEqual(t.strftime(""), "") # SF bug #761337 0847 0848 self.assertRaises(TypeError, t.strftime) # needs an arg 0849 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args 0850 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type 0851 0852 # A naive object replaces %z and %Z w/ empty strings. 0853 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''") 0854 0855 def test_resolution_info(self): 0856 self.assert_(isinstance(self.theclass.min, self.theclass)) 0857 self.assert_(isinstance(self.theclass.max, self.theclass)) 0858 self.assert_(isinstance(self.theclass.resolution, timedelta)) 0859 self.assert_(self.theclass.max > self.theclass.min) 0860 0861 def test_extreme_timedelta(self): 0862 big = self.theclass.max - self.theclass.min 0863 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds 0864 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds 0865 # n == 315537897599999999 ~= 2**58.13 0866 justasbig = timedelta(0, 0, n) 0867 self.assertEqual(big, justasbig) 0868 self.assertEqual(self.theclass.min + big, self.theclass.max) 0869 self.assertEqual(self.theclass.max - big, self.theclass.min) 0870 0871 def test_timetuple(self): 0872 for i in range(7): 0873 # January 2, 1956 is a Monday (0) 0874 d = self.theclass(1956, 1, 2+i) 0875 t = d.timetuple() 0876 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1)) 0877 # February 1, 1956 is a Wednesday (2) 0878 d = self.theclass(1956, 2, 1+i) 0879 t = d.timetuple() 0880 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1)) 0881 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day 0882 # of the year. 0883 d = self.theclass(1956, 3, 1+i) 0884 t = d.timetuple() 0885 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1)) 0886 self.assertEqual(t.tm_year, 1956) 0887 self.assertEqual(t.tm_mon, 3) 0888 self.assertEqual(t.tm_mday, 1+i) 0889 self.assertEqual(t.tm_hour, 0) 0890 self.assertEqual(t.tm_min, 0) 0891 self.assertEqual(t.tm_sec, 0) 0892 self.assertEqual(t.tm_wday, (3+i)%7) 0893 self.assertEqual(t.tm_yday, 61+i) 0894 self.assertEqual(t.tm_isdst, -1) 0895 0896 def test_pickling(self): 0897 args = 6, 7, 23 0898 orig = self.theclass(*args) 0899 for pickler, unpickler, proto in pickle_choices: 0900 green = pickler.dumps(orig, proto) 0901 derived = unpickler.loads(green) 0902 self.assertEqual(orig, derived) 0903 0904 def test_compare(self): 0905 t1 = self.theclass(2, 3, 4) 0906 t2 = self.theclass(2, 3, 4) 0907 self.failUnless(t1 == t2) 0908 self.failUnless(t1 <= t2) 0909 self.failUnless(t1 >= t2) 0910 self.failUnless(not t1 != t2) 0911 self.failUnless(not t1 < t2) 0912 self.failUnless(not t1 > t2) 0913 self.assertEqual(cmp(t1, t2), 0) 0914 self.assertEqual(cmp(t2, t1), 0) 0915 0916 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5): 0917 t2 = self.theclass(*args) # this is larger than t1 0918 self.failUnless(t1 < t2) 0919 self.failUnless(t2 > t1) 0920 self.failUnless(t1 <= t2) 0921 self.failUnless(t2 >= t1) 0922 self.failUnless(t1 != t2) 0923 self.failUnless(t2 != t1) 0924 self.failUnless(not t1 == t2) 0925 self.failUnless(not t2 == t1) 0926 self.failUnless(not t1 > t2) 0927 self.failUnless(not t2 < t1) 0928 self.failUnless(not t1 >= t2) 0929 self.failUnless(not t2 <= t1) 0930 self.assertEqual(cmp(t1, t2), -1) 0931 self.assertEqual(cmp(t2, t1), 1) 0932 0933 for badarg in OTHERSTUFF: 0934 self.assertEqual(t1 == badarg, False) 0935 self.assertEqual(t1 != badarg, True) 0936 self.assertEqual(badarg == t1, False) 0937 self.assertEqual(badarg != t1, True) 0938 0939 self.assertRaises(TypeError, lambda: t1 < badarg) 0940 self.assertRaises(TypeError, lambda: t1 > badarg) 0941 self.assertRaises(TypeError, lambda: t1 >= badarg) 0942 self.assertRaises(TypeError, lambda: badarg <= t1) 0943 self.assertRaises(TypeError, lambda: badarg < t1) 0944 self.assertRaises(TypeError, lambda: badarg > t1) 0945 self.assertRaises(TypeError, lambda: badarg >= t1) 0946 0947 def test_mixed_compare(self): 0948 our = self.theclass(2000, 4, 5) 0949 self.assertRaises(TypeError, cmp, our, 1) 0950 self.assertRaises(TypeError, cmp, 1, our) 0951 0952 class AnotherDateTimeClass(object): 0953 def __cmp__(self, other): 0954 # Return "equal" so calling this can't be confused with 0955 # compare-by-address (which never says "equal" for distinct 0956 # objects). 0957 return 0 0958 0959 # This still errors, because date and datetime comparison raise 0960 # TypeError instead of NotImplemented when they don't know what to 0961 # do, in order to stop comparison from falling back to the default 0962 # compare-by-address. 0963 their = AnotherDateTimeClass() 0964 self.assertRaises(TypeError, cmp, our, their) 0965 # Oops: The next stab raises TypeError in the C implementation, 0966 # but not in the Python implementation of datetime. The difference 0967 # is due to that the Python implementation defines __cmp__ but 0968 # the C implementation defines tp_richcompare. This is more pain 0969 # to fix than it's worth, so commenting out the test. 0970 # self.assertEqual(cmp(their, our), 0) 0971 0972 # But date and datetime comparison return NotImplemented instead if the 0973 # other object has a timetuple attr. This gives the other object a 0974 # chance to do the comparison. 0975 class Comparable(AnotherDateTimeClass): 0976 def timetuple(self): 0977 return () 0978 0979 their = Comparable() 0980 self.assertEqual(cmp(our, their), 0) 0981 self.assertEqual(cmp(their, our), 0) 0982 self.failUnless(our == their) 0983 self.failUnless(their == our) 0984 0985 def test_bool(self): 0986 # All dates are considered true. 0987 self.failUnless(self.theclass.min) 0988 self.failUnless(self.theclass.max) 0989 0990 def test_srftime_out_of_range(self): 0991 # For nasty technical reasons, we can't handle years before 1900. 0992 cls = self.theclass 0993 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900") 0994 for y in 1, 49, 51, 99, 100, 1000, 1899: 0995 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y") 0996 0997 def test_replace(self): 0998 cls = self.theclass 0999 args = [1, 2, 3] 1000 base = cls(*args) 1001 self.assertEqual(base, base.replace()) 1002 1003 i = 0 1004 for name, newval in (("year", 2), 1005 ("month", 3), 1006 ("day", 4)): 1007 newargs = args[:] 1008 newargs[i] = newval 1009 expected = cls(*newargs) 1010 got = base.replace(**{name: newval}) 1011 self.assertEqual(expected, got) 1012 i += 1 1013 1014 # Out of bounds. 1015 base = cls(2000, 2, 29) 1016 self.assertRaises(ValueError, base.replace, year=2001) 1017 1018 def test_subclass_date(self): 1019 1020 class C(self.theclass): 1021 theAnswer = 42 1022 1023 def __new__(cls, *args, **kws): 1024 temp = kws.copy() 1025 extra = temp.pop('extra') 1026 result = self.theclass.__new__(cls, *args, **temp) 1027 result.extra = extra 1028 return result 1029 1030 def newmeth(self, start): 1031 return start + self.year + self.month 1032 1033 args = 2003, 4, 14 1034 1035 dt1 = self.theclass(*args) 1036 dt2 = C(*args, **{'extra': 7}) 1037 1038 self.assertEqual(dt2.__class__, C) 1039 self.assertEqual(dt2.theAnswer, 42) 1040 self.assertEqual(dt2.extra, 7) 1041 self.assertEqual(dt1.toordinal(), dt2.toordinal()) 1042 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7) 1043 1044 def test_pickling_subclass_date(self): 1045 1046 args = 6, 7, 23 1047 orig = SubclassDate(*args) 1048 for pickler, unpickler, proto in pickle_choices: 1049 green = pickler.dumps(orig, proto) 1050 derived = unpickler.loads(green) 1051 self.assertEqual(orig, derived) 1052 1053 def test_backdoor_resistance(self): 1054 # For fast unpickling, the constructor accepts a pickle string. 1055 # This is a low-overhead backdoor. A user can (by intent or 1056 # mistake) pass a string directly, which (if it's the right length) 1057 # will get treated like a pickle, and bypass the normal sanity 1058 # checks in the constructor. This can create insane objects. 1059 # The constructor doesn't want to burn the time to validate all 1060 # fields, but does check the month field. This stops, e.g., 1061 # datetime.datetime('1995-03-25') from yielding an insane object. 1062 base = '1995-03-25' 1063 if not issubclass(self.theclass, datetime): 1064 base = base[:4] 1065 for month_byte in '9', chr(0), chr(13), '\xff': 1066 self.assertRaises(TypeError, self.theclass, 1067 base[:2] + month_byte + base[3:]) 1068 for ord_byte in range(1, 13): 1069 # This shouldn't blow up because of the month byte alone. If 1070 # the implementation changes to do more-careful checking, it may 1071 # blow up because other fields are insane. 1072 self.theclass(base[:2] + chr(ord_byte) + base[3:]) 1073 1074 ############################################################################# 1075 # datetime tests 1076 1077 class SubclassDatetime(datetime): 1078 sub_var = 1 1079 1080 class TestDateTime(TestDate): 1081 1082 theclass = datetime 1083 1084 def test_basic_attributes(self): 1085 dt = self.theclass(2002, 3, 1, 12, 0) 1086 self.assertEqual(dt.year, 2002) 1087 self.assertEqual(dt.month, 3) 1088 self.assertEqual(dt.day, 1) 1089 self.assertEqual(dt.hour, 12) 1090 self.assertEqual(dt.minute, 0) 1091 self.assertEqual(dt.second, 0) 1092 self.assertEqual(dt.microsecond, 0) 1093 1094 def test_basic_attributes_nonzero(self): 1095 # Make sure all attributes are non-zero so bugs in 1096 # bit-shifting access show up. 1097 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000) 1098 self.assertEqual(dt.year, 2002) 1099 self.assertEqual(dt.month, 3) 1100 self.assertEqual(dt.day, 1) 1101 self.assertEqual(dt.hour, 12) 1102 self.assertEqual(dt.minute, 59) 1103 self.assertEqual(dt.second, 59) 1104 self.assertEqual(dt.microsecond, 8000) 1105 1106 def test_roundtrip(self): 1107 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7), 1108 self.theclass.now()): 1109 # Verify dt -> string -> datetime identity. 1110 s = repr(dt) 1111 self.failUnless(s.startswith('datetime.')) 1112 s = s[9:] 1113 dt2 = eval(s) 1114 self.assertEqual(dt, dt2) 1115 1116 # Verify identity via reconstructing from pieces. 1117 dt2 = self.theclass(dt.year, dt.month, dt.day, 1118 dt.hour, dt.minute, dt.second, 1119 dt.microsecond) 1120 self.assertEqual(dt, dt2) 1121 1122 def test_isoformat(self): 1123 t = self.theclass(2, 3, 2, 4, 5, 1, 123) 1124 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123") 1125 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123") 1126 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123") 1127 # str is ISO format with the separator forced to a blank. 1128 self.assertEqual(str(t), "0002-03-02 04:05:01.000123") 1129 1130 t = self.theclass(2, 3, 2) 1131 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00") 1132 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00") 1133 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00") 1134 # str is ISO format with the separator forced to a blank. 1135 self.assertEqual(str(t), "0002-03-02 00:00:00") 1136 1137 def test_more_ctime(self): 1138 # Test fields that TestDate doesn't touch. 1139 import time 1140 1141 t = self.theclass(2002, 3, 2, 18, 3, 5, 123) 1142 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002") 1143 # Oops! The next line fails on Win2K under MSVC 6, so it's commented 1144 # out. The difference is that t.ctime() produces " 2" for the day, 1145 # but platform ctime() produces "02" for the day. According to 1146 # C99, t.ctime() is correct here. 1147 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple()))) 1148 1149 # So test a case where that difference doesn't matter. 1150 t = self.theclass(2002, 3, 22, 18, 3, 5, 123) 1151 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple()))) 1152 1153 def test_tz_independent_comparing(self): 1154 dt1 = self.theclass(2002, 3, 1, 9, 0, 0) 1155 dt2 = self.theclass(2002, 3, 1, 10, 0, 0) 1156 dt3 = self.theclass(2002, 3, 1, 9, 0, 0) 1157 self.assertEqual(dt1, dt3) 1158 self.assert_(dt2 > dt3) 1159 1160 # Make sure comparison doesn't forget microseconds, and isn't done 1161 # via comparing a float timestamp (an IEEE double doesn't have enough 1162 # precision to span microsecond resolution across years 1 thru 9999, 1163 # so comparing via timestamp necessarily calls some distinct values 1164 # equal). 1165 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998) 1166 us = timedelta(microseconds=1) 1167 dt2 = dt1 + us 1168 self.assertEqual(dt2 - dt1, us) 1169 self.assert_(dt1 < dt2) 1170 1171 def test_bad_constructor_arguments(self): 1172 # bad years 1173 self.theclass(MINYEAR, 1, 1) # no exception 1174 self.theclass(MAXYEAR, 1, 1) # no exception 1175 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1) 1176 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1) 1177 # bad months 1178 self.theclass(2000, 1, 1) # no exception 1179 self.theclass(2000, 12, 1) # no exception 1180 self.assertRaises(ValueError, self.theclass, 2000, 0, 1) 1181 self.assertRaises(ValueError, self.theclass, 2000, 13, 1) 1182 # bad days 1183 self.theclass(2000, 2, 29) # no exception 1184 self.theclass(2004, 2, 29) # no exception 1185 self.theclass(2400, 2, 29) # no exception 1186 self.assertRaises(ValueError, self.theclass, 2000, 2, 30) 1187 self.assertRaises(ValueError, self.theclass, 2001, 2, 29) 1188 self.assertRaises(ValueError, self.theclass, 2100, 2, 29) 1189 self.assertRaises(ValueError, self.theclass, 1900, 2, 29) 1190 self.assertRaises(ValueError, self.theclass, 2000, 1, 0) 1191 self.assertRaises(ValueError, self.theclass, 2000, 1, 32) 1192 # bad hours 1193 self.theclass(2000, 1, 31, 0) # no exception 1194 self.theclass(2000, 1, 31, 23) # no exception 1195 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1) 1196 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24) 1197 # bad minutes 1198 self.theclass(2000, 1, 31, 23, 0) # no exception 1199 self.theclass(2000, 1, 31, 23, 59) # no exception 1200 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1) 1201 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60) 1202 # bad seconds 1203 self.theclass(2000, 1, 31, 23, 59, 0) # no exception 1204 self.theclass(2000, 1, 31, 23, 59, 59) # no exception 1205 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1) 1206 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60) 1207 # bad microseconds 1208 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception 1209 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception 1210 self.assertRaises(ValueError, self.theclass, 1211 2000, 1, 31, 23, 59, 59, -1) 1212 self.assertRaises(ValueError, self.theclass, 1213 2000, 1, 31, 23, 59, 59, 1214 1000000) 1215 1216 def test_hash_equality(self): 1217 d = self.theclass(2000, 12, 31, 23, 30, 17) 1218 e = self.theclass(2000, 12, 31, 23, 30, 17) 1219 self.assertEqual(d, e) 1220 self.assertEqual(hash(d), hash(e)) 1221 1222 dic = {d: 1} 1223 dic[e] = 2 1224 self.assertEqual(len(dic), 1) 1225 self.assertEqual(dic[d], 2) 1226 self.assertEqual(dic[e], 2) 1227 1228 d = self.theclass(2001, 1, 1, 0, 5, 17) 1229 e = self.theclass(2001, 1, 1, 0, 5, 17) 1230 self.assertEqual(d, e) 1231 self.assertEqual(hash(d), hash(e)) 1232 1233 dic = {d: 1} 1234 dic[e] = 2 1235 self.assertEqual(len(dic), 1) 1236 self.assertEqual(dic[d], 2) 1237 self.assertEqual(dic[e], 2) 1238 1239 def test_computations(self): 1240 a = self.theclass(2002, 1, 31) 1241 b = self.theclass(1956, 1, 31) 1242 diff = a-b 1243 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4))) 1244 self.assertEqual(diff.seconds, 0) 1245 self.assertEqual(diff.microseconds, 0) 1246 a = self.theclass(2002, 3, 2, 17, 6) 1247 millisec = timedelta(0, 0, 1000) 1248 hour = timedelta(0, 3600) 1249 day = timedelta(1) 1250 week = timedelta(7) 1251 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6)) 1252 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6)) 1253 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6)) 1254 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6)) 1255 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6)) 1256 self.assertEqual(a - hour, a + -hour) 1257 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6)) 1258 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6)) 1259 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6)) 1260 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6)) 1261 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6)) 1262 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6)) 1263 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6)) 1264 self.assertEqual((a + week) - a, week) 1265 self.assertEqual((a + day) - a, day) 1266 self.assertEqual((a + hour) - a, hour) 1267 self.assertEqual((a + millisec) - a, millisec) 1268 self.assertEqual((a - week) - a, -week) 1269 self.assertEqual((a - day) - a, -day) 1270 self.assertEqual((a - hour) - a, -hour) 1271 self.assertEqual((a - millisec) - a, -millisec) 1272 self.assertEqual(a - (a + week), -week) 1273 self.assertEqual(a - (a + day), -day) 1274 self.assertEqual(a - (a + hour), -hour) 1275 self.assertEqual(a - (a + millisec), -millisec) 1276 self.assertEqual(a - (a - week), week) 1277 self.assertEqual(a - (a - day), day) 1278 self.assertEqual(a - (a - hour), hour) 1279 self.assertEqual(a - (a - millisec), millisec) 1280 self.assertEqual(a + (week + day + hour + millisec), 1281 self.theclass(2002, 3, 10, 18, 6, 0, 1000)) 1282 self.assertEqual(a + (week + day + hour + millisec), 1283 (((a + week) + day) + hour) + millisec) 1284 self.assertEqual(a - (week + day + hour + millisec), 1285 self.theclass(2002, 2, 22, 16, 5, 59, 999000)) 1286 self.assertEqual(a - (week + day + hour + millisec), 1287 (((a - week) - day) - hour) - millisec) 1288 # Add/sub ints, longs, floats should be illegal 1289 for i in 1, 1L, 1.0: 1290 self.assertRaises(TypeError, lambda: a+i) 1291 self.assertRaises(TypeError, lambda: a-i) 1292 self.assertRaises(TypeError, lambda: i+a) 1293 self.assertRaises(TypeError, lambda: i-a) 1294 1295 # delta - datetime is senseless. 1296 self.assertRaises(TypeError, lambda: day - a) 1297 # mixing datetime and (delta or datetime) via * or // is senseless 1298 self.assertRaises(TypeError, lambda: day * a) 1299 self.assertRaises(TypeError, lambda: a * day) 1300 self.assertRaises(TypeError, lambda: day // a) 1301 self.assertRaises(TypeError, lambda: a // day) 1302 self.assertRaises(TypeError, lambda: a * a) 1303 self.assertRaises(TypeError, lambda: a // a) 1304 # datetime + datetime is senseless 1305 self.assertRaises(TypeError, lambda: a + a) 1306 1307 def test_pickling(self): 1308 args = 6, 7, 23, 20, 59, 1, 64**2 1309 orig = self.theclass(*args) 1310 for pickler, unpickler, proto in pickle_choices: 1311 green = pickler.dumps(orig, proto) 1312 derived = unpickler.loads(green) 1313 self.assertEqual(orig, derived) 1314 1315 def test_more_pickling(self): 1316 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116) 1317 s = pickle.dumps(a) 1318 b = pickle.loads(s) 1319 self.assertEqual(b.year, 2003) 1320 self.assertEqual(b.month, 2) 1321 self.assertEqual(b.day, 7) 1322 1323 def test_pickling_subclass_datetime(self): 1324 args = 6, 7, 23, 20, 59, 1, 64**2 1325 orig = SubclassDatetime(*args) 1326 for pickler, unpickler, proto in pickle_choices: 1327 green = pickler.dumps(orig, proto) 1328 derived = unpickler.loads(green) 1329 self.assertEqual(orig, derived) 1330 1331 def test_more_compare(self): 1332 # The test_compare() inherited from TestDate covers the error cases. 1333 # We just want to test lexicographic ordering on the members datetime 1334 # has that date lacks. 1335 args = [2000, 11, 29, 20, 58, 16, 999998] 1336 t1 = self.theclass(*args) 1337 t2 = self.theclass(*args) 1338 self.failUnless(t1 == t2) 1339 self.failUnless(t1 <= t2) 1340 self.failUnless(t1 >= t2) 1341 self.failUnless(not t1 != t2) 1342 self.failUnless(not t1 < t2) 1343 self.failUnless(not t1 > t2) 1344 self.assertEqual(cmp(t1, t2), 0) 1345 self.assertEqual(cmp(t2, t1), 0) 1346 1347 for i in range(len(args)): 1348 newargs = args[:] 1349 newargs[i] = args[i] + 1 1350 t2 = self.theclass(*newargs) # this is larger than t1 1351 self.failUnless(t1 < t2) 1352 self.failUnless(t2 > t1) 1353 self.failUnless(t1 <= t2) 1354 self.failUnless(t2 >= t1) 1355 self.failUnless(t1 != t2) 1356 self.failUnless(t2 != t1) 1357 self.failUnless(not t1 == t2) 1358 self.failUnless(not t2 == t1) 1359 self.failUnless(not t1 > t2) 1360 self.failUnless(not t2 < t1) 1361 self.failUnless(not t1 >= t2) 1362 self.failUnless(not t2 <= t1) 1363 self.assertEqual(cmp(t1, t2), -1) 1364 self.assertEqual(cmp(t2, t1), 1) 1365 1366 1367 # A helper for timestamp constructor tests. 1368 def verify_field_equality(self, expected, got): 1369 self.assertEqual(expected.tm_year, got.year) 1370 self.assertEqual(expected.tm_mon, got.month) 1371 self.assertEqual(expected.tm_mday, got.day) 1372 self.assertEqual(expected.tm_hour, got.hour) 1373 self.assertEqual(expected.tm_min, got.minute) 1374 self.assertEqual(expected.tm_sec, got.second) 1375 1376 def test_fromtimestamp(self): 1377 import time 1378 1379 ts = time.time() 1380 expected = time.localtime(ts) 1381 got = self.theclass.fromtimestamp(ts) 1382 self.verify_field_equality(expected, got) 1383 1384 def test_utcfromtimestamp(self): 1385 import time 1386 1387 ts = time.time() 1388 expected = time.gmtime(ts) 1389 got = self.theclass.utcfromtimestamp(ts) 1390 self.verify_field_equality(expected, got) 1391 1392 def test_insane_fromtimestamp(self): 1393 # It's possible that some platform maps time_t to double, 1394 # and that this test will fail there. This test should 1395 # exempt such platforms (provided they return reasonable 1396 # results!). 1397 for insane in -1e200, 1e200: 1398 self.assertRaises(ValueError, self.theclass.fromtimestamp, 1399 insane) 1400 1401 def test_insane_utcfromtimestamp(self): 1402 # It's possible that some platform maps time_t to double, 1403 # and that this test will fail there. This test should 1404 # exempt such platforms (provided they return reasonable 1405 # results!). 1406 for insane in -1e200, 1e200: 1407 self.assertRaises(ValueError, self.theclass.utcfromtimestamp, 1408 insane) 1409 1410 def test_utcnow(self): 1411 import time 1412 1413 # Call it a success if utcnow() and utcfromtimestamp() are within 1414 # a second of each other. 1415 tolerance = timedelta(seconds=1) 1416 for dummy in range(3): 1417 from_now = self.theclass.utcnow() 1418 from_timestamp = self.theclass.utcfromtimestamp(time.time()) 1419 if abs(from_timestamp - from_now) <= tolerance: 1420 break 1421 # Else try again a few times. 1422 self.failUnless(abs(from_timestamp - from_now) <= tolerance) 1423 1424 def test_more_timetuple(self): 1425 # This tests fields beyond those tested by the TestDate.test_timetuple. 1426 t = self.theclass(2004, 12, 31, 6, 22, 33) 1427 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1)) 1428 self.assertEqual(t.timetuple(), 1429 (t.year, t.month, t.day, 1430 t.hour, t.minute, t.second, 1431 t.weekday(), 1432 t.toordinal() - date(t.year, 1, 1).toordinal() + 1, 1433 -1)) 1434 tt = t.timetuple() 1435 self.assertEqual(tt.tm_year, t.year) 1436 self.assertEqual(tt.tm_mon, t.month) 1437 self.assertEqual(tt.tm_mday, t.day) 1438 self.assertEqual(tt.tm_hour, t.hour) 1439 self.assertEqual(tt.tm_min, t.minute) 1440 self.assertEqual(tt.tm_sec, t.second) 1441 self.assertEqual(tt.tm_wday, t.weekday()) 1442 self.assertEqual(tt.tm_yday, t.toordinal() - 1443 date(t.year, 1, 1).toordinal() + 1) 1444 self.assertEqual(tt.tm_isdst, -1) 1445 1446 def test_more_strftime(self): 1447 # This tests fields beyond those tested by the TestDate.test_strftime. 1448 t = self.theclass(2004, 12, 31, 6, 22, 33) 1449 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"), 1450 "12 31 04 33 22 06 366") 1451 1452 def test_extract(self): 1453 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234) 1454 self.assertEqual(dt.date(), date(2002, 3, 4)) 1455 self.assertEqual(dt.time(), time(18, 45, 3, 1234)) 1456 1457 def test_combine(self): 1458 d = date(2002, 3, 4) 1459 t = time(18, 45, 3, 1234) 1460 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234) 1461 combine = self.theclass.combine 1462 dt = combine(d, t) 1463 self.assertEqual(dt, expected) 1464 1465 dt = combine(time=t, date=d) 1466 self.assertEqual(dt, expected) 1467 1468 self.assertEqual(d, dt.date()) 1469 self.assertEqual(t, dt.time()) 1470 self.assertEqual(dt, combine(dt.date(), dt.time())) 1471 1472 self.assertRaises(TypeError, combine) # need an arg 1473 self.assertRaises(TypeError, combine, d) # need two args 1474 self.assertRaises(TypeError, combine, t, d) # args reversed 1475 self.assertRaises(TypeError, combine, d, t, 1) # too many args 1476 self.assertRaises(TypeError, combine, "date", "time") # wrong types 1477 1478 def test_replace(self): 1479 cls = self.theclass 1480 args = [1, 2, 3, 4, 5, 6, 7] 1481 base = cls(*args) 1482 self.assertEqual(base, base.replace()) 1483 1484 i = 0 1485 for name, newval in (("year", 2), 1486 ("month", 3), 1487 ("day", 4), 1488 ("hour", 5), 1489 ("minute", 6), 1490 ("second", 7), 1491 ("microsecond", 8)): 1492 newargs = args[:] 1493 newargs[i] = newval 1494 expected = cls(*newargs) 1495 got = base.replace(**{name: newval}) 1496 self.assertEqual(expected, got) 1497 i += 1 1498 1499 # Out of bounds. 1500 base = cls(2000, 2, 29) 1501 self.assertRaises(ValueError, base.replace, year=2001) 1502 1503 def test_astimezone(self): 1504 # Pretty boring! The TZ test is more interesting here. astimezone() 1505 # simply can't be applied to a naive object. 1506 dt = self.theclass.now() 1507 f = FixedOffset(44, "") 1508 self.assertRaises(TypeError, dt.astimezone) # not enough args 1509 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args 1510 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type 1511 self.assertRaises(ValueError, dt.astimezone, f) # naive 1512 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive 1513 1514 class Bogus(tzinfo): 1515 def utcoffset(self, dt): return None 1516 def dst(self, dt): return timedelta(0) 1517 bog = Bogus() 1518 self.assertRaises(ValueError, dt.astimezone, bog) # naive 1519 1520 class AlsoBogus(tzinfo): 1521 def utcoffset(self, dt): return timedelta(0) 1522 def dst(self, dt): return None 1523 alsobog = AlsoBogus() 1524 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive 1525 1526 def test_subclass_datetime(self): 1527 1528 class C(self.theclass): 1529 theAnswer = 42 1530 1531 def __new__(cls, *args, **kws): 1532 temp = kws.copy() 1533 extra = temp.pop('extra') 1534 result = self.theclass.__new__(cls, *args, **temp) 1535 result.extra = extra 1536 return result 1537 1538 def newmeth(self, start): 1539 return start + self.year + self.month + self.second 1540 1541 args = 2003, 4, 14, 12, 13, 41 1542 1543 dt1 = self.theclass(*args) 1544 dt2 = C(*args, **{'extra': 7}) 1545 1546 self.assertEqual(dt2.__class__, C) 1547 self.assertEqual(dt2.theAnswer, 42) 1548 self.assertEqual(dt2.extra, 7) 1549 self.assertEqual(dt1.toordinal(), dt2.toordinal()) 1550 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month + 1551 dt1.second - 7) 1552 1553 class SubclassTime(time): 1554 sub_var = 1 1555 1556 class TestTime(HarmlessMixedComparison): 1557 1558 theclass = time 1559 1560 def test_basic_attributes(self): 1561 t = self.theclass(12, 0) 1562 self.assertEqual(t.hour, 12) 1563 self.assertEqual(t.minute, 0) 1564 self.assertEqual(t.second, 0) 1565 self.assertEqual(t.microsecond, 0) 1566 1567 def test_basic_attributes_nonzero(self): 1568 # Make sure all attributes are non-zero so bugs in 1569 # bit-shifting access show up. 1570 t = self.theclass(12, 59, 59, 8000) 1571 self.assertEqual(t.hour, 12) 1572 self.assertEqual(t.minute, 59) 1573 self.assertEqual(t.second, 59) 1574 self.assertEqual(t.microsecond, 8000) 1575 1576 def test_roundtrip(self): 1577 t = self.theclass(1, 2, 3, 4) 1578 1579 # Verify t -> string -> time identity. 1580 s = repr(t) 1581 self.failUnless(s.startswith('datetime.')) 1582 s = s[9:] 1583 t2 = eval(s) 1584 self.assertEqual(t, t2) 1585 1586 # Verify identity via reconstructing from pieces. 1587 t2 = self.theclass(t.hour, t.minute, t.second, 1588 t.microsecond) 1589 self.assertEqual(t, t2) 1590 1591 def test_comparing(self): 1592 args = [1, 2, 3, 4] 1593 t1 = self.theclass(*args) 1594 t2 = self.theclass(*args) 1595 self.failUnless(t1 == t2) 1596 self.failUnless(t1 <= t2) 1597 self.failUnless(t1 >= t2) 1598 self.failUnless(not t1 != t2) 1599 self.failUnless(not t1 < t2) 1600 self.failUnless(not t1 > t2) 1601 self.assertEqual(cmp(t1, t2), 0) 1602 self.assertEqual(cmp(t2, t1), 0) 1603 1604 for i in range(len(args)): 1605 newargs = args[:] 1606 newargs[i] = args[i] + 1 1607 t2 = self.theclass(*newargs) # this is larger than t1 1608 self.failUnless(t1 < t2) 1609 self.failUnless(t2 > t1) 1610 self.failUnless(t1 <= t2) 1611 self.failUnless(t2 >= t1) 1612 self.failUnless(t1 != t2) 1613 self.failUnless(t2 != t1) 1614 self.failUnless(not t1 == t2) 1615 self.failUnless(not t2 == t1) 1616 self.failUnless(not t1 > t2) 1617 self.failUnless(not t2 < t1) 1618 self.failUnless(not t1 >= t2) 1619 self.failUnless(not t2 <= t1) 1620 self.assertEqual(cmp(t1, t2), -1) 1621 self.assertEqual(cmp(t2, t1), 1) 1622 1623 for badarg in OTHERSTUFF: 1624 self.assertEqual(t1 == badarg, False) 1625 self.assertEqual(t1 != badarg, True) 1626 self.assertEqual(badarg == t1, False) 1627 self.assertEqual(badarg != t1, True) 1628 1629 self.assertRaises(TypeError, lambda: t1 <= badarg) 1630 self.assertRaises(TypeError, lambda: t1 < badarg) 1631 self.assertRaises(TypeError, lambda: t1 > badarg) 1632 self.assertRaises(TypeError, lambda: t1 >= badarg) 1633 self.assertRaises(TypeError, lambda: badarg <= t1) 1634 self.assertRaises(TypeError, lambda: badarg < t1) 1635 self.assertRaises(TypeError, lambda: badarg > t1) 1636 self.assertRaises(TypeError, lambda: badarg >= t1) 1637 1638 def test_bad_constructor_arguments(self): 1639 # bad hours 1640 self.theclass(0, 0) # no exception 1641 self.theclass(23, 0) # no exception 1642 self.assertRaises(ValueError, self.theclass, -1, 0) 1643 self.assertRaises(ValueError, self.theclass, 24, 0) 1644 # bad minutes 1645 self.theclass(23, 0) # no exception 1646 self.theclass(23, 59) # no exception 1647 self.assertRaises(ValueError, self.theclass, 23, -1) 1648 self.assertRaises(ValueError, self.theclass, 23, 60) 1649 # bad seconds 1650 self.theclass(23, 59, 0) # no exception 1651 self.theclass(23, 59, 59) # no exception 1652 self.assertRaises(ValueError, self.theclass, 23, 59, -1) 1653 self.assertRaises(ValueError, self.theclass, 23, 59, 60) 1654 # bad microseconds 1655 self.theclass(23, 59, 59, 0) # no exception 1656 self.theclass(23, 59, 59, 999999) # no exception 1657 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1) 1658 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000) 1659 1660 def test_hash_equality(self): 1661 d = self.theclass(23, 30, 17) 1662 e = self.theclass(23, 30, 17) 1663 self.assertEqual(d, e) 1664 self.assertEqual(hash(d), hash(e)) 1665 1666 dic = {d: 1} 1667 dic[e] = 2 1668 self.assertEqual(len(dic), 1) 1669 self.assertEqual(dic[d], 2) 1670 self.assertEqual(dic[e], 2) 1671 1672 d = self.theclass(0, 5, 17) 1673 e = self.theclass(0, 5, 17) 1674 self.assertEqual(d, e) 1675 self.assertEqual(hash(d), hash(e)) 1676 1677 dic = {d: 1} 1678 dic[e] = 2 1679 self.assertEqual(len(dic), 1) 1680 self.assertEqual(dic[d], 2) 1681 self.assertEqual(dic[e], 2) 1682 1683 def test_isoformat(self): 1684 t = self.theclass(4, 5, 1, 123) 1685 self.assertEqual(t.isoformat(), "04:05:01.000123") 1686 self.assertEqual(t.isoformat(), str(t)) 1687 1688 t = self.theclass() 1689 self.assertEqual(t.isoformat(), "00:00:00") 1690 self.assertEqual(t.isoformat(), str(t)) 1691 1692 t = self.theclass(microsecond=1) 1693 self.assertEqual(t.isoformat(), "00:00:00.000001") 1694 self.assertEqual(t.isoformat(), str(t)) 1695 1696 t = self.theclass(microsecond=10) 1697 self.assertEqual(t.isoformat(), "00:00:00.000010") 1698 self.assertEqual(t.isoformat(), str(t)) 1699 1700 t = self.theclass(microsecond=100) 1701 self.assertEqual(t.isoformat(), "00:00:00.000100") 1702 self.assertEqual(t.isoformat(), str(t)) 1703 1704 t = self.theclass(microsecond=1000) 1705 self.assertEqual(t.isoformat(), "00:00:00.001000") 1706 self.assertEqual(t.isoformat(), str(t)) 1707 1708 t = self.theclass(microsecond=10000) 1709 self.assertEqual(t.isoformat(), "00:00:00.010000") 1710 self.assertEqual(t.isoformat(), str(t)) 1711 1712 t = self.theclass(microsecond=100000) 1713 self.assertEqual(t.isoformat(), "00:00:00.100000") 1714 self.assertEqual(t.isoformat(), str(t)) 1715 1716 def test_strftime(self): 1717 t = self.theclass(1, 2, 3, 4) 1718 self.assertEqual(t.strftime('%H %M %S'), "01 02 03") 1719 # A naive object replaces %z and %Z with empty strings. 1720 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''") 1721 1722 def test_str(self): 1723 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004") 1724 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000") 1725 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000") 1726 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03") 1727 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00") 1728 1729 def test_repr(self): 1730 name = 'datetime.' + self.theclass.__name__ 1731 self.assertEqual(repr(self.theclass(1, 2, 3, 4)), 1732 "%s(1, 2, 3, 4)" % name) 1733 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)), 1734 "%s(10, 2, 3, 4000)" % name) 1735 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)), 1736 "%s(0, 2, 3, 400000)" % name) 1737 self.assertEqual(repr(self.theclass(12, 2, 3, 0)), 1738 "%s(12, 2, 3)" % name) 1739 self.assertEqual(repr(self.theclass(23, 15, 0, 0)), 1740 "%s(23, 15)" % name) 1741 1742 def test_resolution_info(self): 1743 self.assert_(isinstance(self.theclass.min, self.theclass)) 1744 self.assert_(isinstance(self.theclass.max, self.theclass)) 1745 self.assert_(isinstance(self.theclass.resolution, timedelta)) 1746 self.assert_(self.theclass.max > self.theclass.min) 1747 1748 def test_pickling(self): 1749 args = 20, 59, 16, 64**2 1750 orig = self.theclass(*args) 1751 for pickler, unpickler, proto in pickle_choices: 1752 green = pickler.dumps(orig, proto) 1753 derived = unpickler.loads(green) 1754 self.assertEqual(orig, derived) 1755 1756 def test_pickling_subclass_time(self): 1757 args = 20, 59, 16, 64**2 1758 orig = SubclassTime(*args) 1759 for pickler, unpickler, proto in pickle_choices: 1760 green = pickler.dumps(orig, proto) 1761 derived = unpickler.loads(green) 1762 self.assertEqual(orig, derived) 1763 1764 def test_bool(self): 1765 cls = self.theclass 1766 self.failUnless(cls(1)) 1767 self.failUnless(cls(0, 1)) 1768 self.failUnless(cls(0, 0, 1)) 1769 self.failUnless(cls(0, 0, 0, 1)) 1770 self.failUnless(not cls(0)) 1771 self.failUnless(not cls()) 1772 1773 def test_replace(self): 1774 cls = self.theclass 1775 args = [1, 2, 3, 4] 1776 base = cls(*args) 1777 self.assertEqual(base, base.replace()) 1778 1779 i = 0 1780 for name, newval in (("hour", 5), 1781 ("minute", 6), 1782 ("second", 7), 1783 ("microsecond", 8)): 1784 newargs = args[:] 1785 newargs[i] = newval 1786 expected = cls(*newargs) 1787 got = base.replace(**{name: newval}) 1788 self.assertEqual(expected, got) 1789 i += 1 1790 1791 # Out of bounds. 1792 base = cls(1) 1793 self.assertRaises(ValueError, base.replace, hour=24) 1794 self.assertRaises(ValueError, base.replace, minute=-1) 1795 self.assertRaises(ValueError, base.replace, second=100) 1796 self.assertRaises(ValueError, base.replace, microsecond=1000000) 1797 1798 def test_subclass_time(self): 1799 1800 class C(self.theclass): 1801 theAnswer = 42 1802 1803 def __new__(cls, *args, **kws): 1804 temp = kws.copy() 1805 extra = temp.pop('extra') 1806 result = self.theclass.__new__(cls, *args, **temp) 1807 result.extra = extra 1808 return result 1809 1810 def newmeth(self, start): 1811 return start + self.hour + self.second 1812 1813 args = 4, 5, 6 1814 1815 dt1 = self.theclass(*args) 1816 dt2 = C(*args, **{'extra': 7}) 1817 1818 self.assertEqual(dt2.__class__, C) 1819 self.assertEqual(dt2.theAnswer, 42) 1820 self.assertEqual(dt2.extra, 7) 1821 self.assertEqual(dt1.isoformat(), dt2.isoformat()) 1822 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7) 1823 1824 # A mixin for classes with a tzinfo= argument. Subclasses must define 1825 # theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever) 1826 # must be legit (which is true for time and datetime). 1827 class TZInfoBase(unittest.TestCase): 1828 1829 def test_argument_passing(self): 1830 cls = self.theclass 1831 # A datetime passes itself on, a time passes None. 1832 class introspective(tzinfo): 1833 def tzname(self, dt): return dt and "real" or "none" 1834 def utcoffset(self, dt): 1835 return timedelta(minutes = dt and 42 or -42) 1836 dst = utcoffset 1837 1838 obj = cls(1, 2, 3, tzinfo=introspective()) 1839 1840 expected = cls is time and "none" or "real" 1841 self.assertEqual(obj.tzname(), expected) 1842 1843 expected = timedelta(minutes=(cls is time and -42 or 42)) 1844 self.assertEqual(obj.utcoffset(), expected) 1845 self.assertEqual(obj.dst(), expected) 1846 1847 def test_bad_tzinfo_classes(self): 1848 cls = self.theclass 1849 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12) 1850 1851 class NiceTry(object): 1852 def __init__(self): pass 1853 def utcoffset(self, dt): pass 1854 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry) 1855 1856 class BetterTry(tzinfo): 1857 def __init__(self): pass 1858 def utcoffset(self, dt): pass 1859 b = BetterTry() 1860 t = cls(1, 1, 1, tzinfo=b) 1861 self.failUnless(t.tzinfo is b) 1862 1863 def test_utc_offset_out_of_bounds(self): 1864 class Edgy(tzinfo): 1865 def __init__(self, offset): 1866 self.offset = timedelta(minutes=offset) 1867 def utcoffset(self, dt): 1868 return self.offset 1869 1870 cls = self.theclass 1871 for offset, legit in ((-1440, False), 1872 (-1439, True), 1873 (1439, True), 1874 (1440, False)): 1875 if cls is time: 1876 t = cls(1, 2, 3, tzinfo=Edgy(offset)) 1877 elif cls is datetime: 1878 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset)) 1879 else: 1880 assert 0, "impossible" 1881 if legit: 1882 aofs = abs(offset) 1883 h, m = divmod(aofs, 60) 1884 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m) 1885 if isinstance(t, datetime): 1886 t = t.timetz() 1887 self.assertEqual(str(t), "01:02:03" + tag) 1888 else: 1889 self.assertRaises(ValueError, str, t) 1890 1891 def test_tzinfo_classes(self): 1892 cls = self.theclass 1893 class C1(tzinfo): 1894 def utcoffset(self, dt): return None 1895 def dst(self, dt): return None 1896 def tzname(self, dt): return None 1897 for t in (cls(1, 1, 1), 1898 cls(1, 1, 1, tzinfo=None), 1899 cls(1, 1, 1, tzinfo=C1())): 1900 self.failUnless(t.utcoffset() is None) 1901 self.failUnless(t.dst() is None) 1902 self.failUnless(t.tzname() is None) 1903 1904 class C3(tzinfo): 1905 def utcoffset(self, dt): return timedelta(minutes=-1439) 1906 def dst(self, dt): return timedelta(minutes=1439) 1907 def tzname(self, dt): return "aname" 1908 t = cls(1, 1, 1, tzinfo=C3()) 1909 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439)) 1910 self.assertEqual(t.dst(), timedelta(minutes=1439)) 1911 self.assertEqual(t.tzname(), "aname") 1912 1913 # Wrong types. 1914 class C4(tzinfo): 1915 def utcoffset(self, dt): return "aname" 1916 def dst(self, dt): return 7 1917 def tzname(self, dt): return 0 1918 t = cls(1, 1, 1, tzinfo=C4()) 1919 self.assertRaises(TypeError, t.utcoffset) 1920 self.assertRaises(TypeError, t.dst) 1921 self.assertRaises(TypeError, t.tzname) 1922 1923 # Offset out of range. 1924 class C6(tzinfo): 1925 def utcoffset(self, dt): return timedelta(hours=-24) 1926 def dst(self, dt): return timedelta(hours=24) 1927 t = cls(1, 1, 1, tzinfo=C6()) 1928 self.assertRaises(ValueError, t.utcoffset) 1929 self.assertRaises(ValueError, t.dst) 1930 1931 # Not a whole number of minutes. 1932 class C7(tzinfo): 1933 def utcoffset(self, dt): return timedelta(seconds=61) 1934 def dst(self, dt): return timedelta(microseconds=-81) 1935 t = cls(1, 1, 1, tzinfo=C7()) 1936 self.assertRaises(ValueError, t.utcoffset) 1937 self.assertRaises(ValueError, t.dst) 1938 1939 def test_aware_compare(self): 1940 cls = self.theclass 1941 1942 # Ensure that utcoffset() gets ignored if the comparands have 1943 # the same tzinfo member. 1944 class OperandDependentOffset(tzinfo): 1945 def utcoffset(self, t): 1946 if t.minute < 10: 1947 # d0 and d1 equal after adjustment 1948 return timedelta(minutes=t.minute) 1949 else: 1950 # d2 off in the weeds 1951 return timedelta(minutes=59) 1952 1953 base = cls(8, 9, 10, tzinfo=OperandDependentOffset()) 1954 d0 = base.replace(minute=3) 1955 d1 = base.replace(minute=9) 1956 d2 = base.replace(minute=11) 1957 for x in d0, d1, d2: 1958 for y in d0, d1, d2: 1959 got = cmp(x, y) 1960 expected = cmp(x.minute, y.minute) 1961 self.assertEqual(got, expected) 1962 1963 # However, if they're different members, uctoffset is not ignored. 1964 # Note that a time can't actually have an operand-depedent offset, 1965 # though (and time.utcoffset() passes None to tzinfo.utcoffset()), 1966 # so skip this test for time. 1967 if cls is not time: 1968 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset()) 1969 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset()) 1970 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset()) 1971 for x in d0, d1, d2: 1972 for y in d0, d1, d2: 1973 got = cmp(x, y) 1974 if (x is d0 or x is d1) and (y is d0 or y is d1): 1975 expected = 0 1976 elif x is y is d2: 1977 expected = 0 1978 elif x is d2: 1979 expected = -1 1980 else: 1981 assert y is d2 1982 expected = 1 1983 self.assertEqual(got, expected) 1984 1985 1986 # Testing time objects with a non-None tzinfo. 1987 class TestTimeTZ(TestTime, TZInfoBase): 1988 theclass = time 1989 1990 def test_empty(self): 1991 t = self.theclass() 1992 self.assertEqual(t.hour, 0) 1993 self.assertEqual(t.minute, 0) 1994 self.assertEqual(t.second, 0) 1995 self.assertEqual(t.microsecond, 0) 1996 self.failUnless(t.tzinfo is None) 1997 1998 def test_zones(self): 1999 est = FixedOffset(-300, "EST", 1) 2000 utc = FixedOffset(0, "UTC", -2) 2001 met = FixedOffset(60, "MET", 3) 2002 t1 = time( 7, 47, tzinfo=est) 2003 t2 = time(12, 47, tzinfo=utc) 2004 t3 = time(13, 47, tzinfo=met) 2005 t4 = time(microsecond=40) 2006 t5 = time(microsecond=40, tzinfo=utc) 2007 2008 self.assertEqual(t1.tzinfo, est) 2009 self.assertEqual(t2.tzinfo, utc) 2010 self.assertEqual(t3.tzinfo, met) 2011 self.failUnless(t4.tzinfo is None) 2012 self.assertEqual(t5.tzinfo, utc) 2013 2014 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300)) 2015 self.assertEqual(t2.utcoffset(), timedelta(minutes=0)) 2016 self.assertEqual(t3.utcoffset(), timedelta(minutes=60)) 2017 self.failUnless(t4.utcoffset() is None) 2018 self.assertRaises(TypeError, t1.utcoffset, "no args") 2019 2020 self.assertEqual(t1.tzname(), "EST") 2021 self.assertEqual(t2.tzname(), "UTC") 2022 self.assertEqual(t3.tzname(), "MET") 2023 self.failUnless(t4.tzname() is None) 2024 self.assertRaises(TypeError, t1.tzname, "no args") 2025 2026 self.assertEqual(t1.dst(), timedelta(minutes=1)) 2027 self.assertEqual(t2.dst(), timedelta(minutes=-2)) 2028 self.assertEqual(t3.dst(), timedelta(minutes=3)) 2029 self.failUnless(t4.dst() is None) 2030 self.assertRaises(TypeError, t1.dst, "no args") 2031 2032 self.assertEqual(hash(t1), hash(t2)) 2033 self.assertEqual(hash(t1), hash(t3)) 2034 self.assertEqual(hash(t2), hash(t3)) 2035 2036 self.assertEqual(t1, t2) 2037 self.assertEqual(t1, t3) 2038 self.assertEqual(t2, t3) 2039 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive 2040 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive 2041 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive 2042 2043 self.assertEqual(str(t1), "07:47:00-05:00") 2044 self.assertEqual(str(t2), "12:47:00+00:00") 2045 self.assertEqual(str(t3), "13:47:00+01:00") 2046 self.assertEqual(str(t4), "00:00:00.000040") 2047 self.assertEqual(str(t5), "00:00:00.000040+00:00") 2048 2049 self.assertEqual(t1.isoformat(), "07:47:00-05:00") 2050 self.assertEqual(t2.isoformat(), "12:47:00+00:00") 2051 self.assertEqual(t3.isoformat(), "13:47:00+01:00") 2052 self.assertEqual(t4.isoformat(), "00:00:00.000040") 2053 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00") 2054 2055 d = 'datetime.time' 2056 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)") 2057 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)") 2058 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)") 2059 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)") 2060 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)") 2061 2062 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"), 2063 "07:47:00 %Z=EST %z=-0500") 2064 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000") 2065 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100") 2066 2067 yuck = FixedOffset(-1439, "%z %Z %%z%%Z") 2068 t1 = time(23, 59, tzinfo=yuck) 2069 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"), 2070 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'") 2071 2072 # Check that an invalid tzname result raises an exception. 2073 class Badtzname(tzinfo): 2074 def tzname(self, dt): return 42 2075 t = time(2, 3, 4, tzinfo=Badtzname()) 2076 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04") 2077 self.assertRaises(TypeError, t.strftime, "%Z") 2078 2079 def test_hash_edge_cases(self): 2080 # Offsets that overflow a basic time. 2081 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, "")) 2082 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, "")) 2083 self.assertEqual(hash(t1), hash(t2)) 2084 2085 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, "")) 2086 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, "")) 2087 self.assertEqual(hash(t1), hash(t2)) 2088 2089 def test_pickling(self): 2090 # Try one without a tzinfo. 2091 args = 20, 59, 16, 64**2 2092 orig = self.theclass(*args) 2093 for pickler, unpickler, proto in pickle_choices: 2094 green = pickler.dumps(orig, proto) 2095 derived = unpickler.loads(green) 2096 self.assertEqual(orig, derived) 2097 2098 # Try one with a tzinfo. 2099 tinfo = PicklableFixedOffset(-300, 'cookie') 2100 orig = self.theclass(5, 6, 7, tzinfo=tinfo) 2101 for pickler, unpickler, proto in pickle_choices: 2102 green = pickler.dumps(orig, proto) 2103 derived = unpickler.loads(green) 2104 self.assertEqual(orig, derived) 2105 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset)) 2106 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) 2107 self.assertEqual(derived.tzname(), 'cookie') 2108 2109 def test_more_bool(self): 2110 # Test cases with non-None tzinfo. 2111 cls = self.theclass 2112 2113 t = cls(0, tzinfo=FixedOffset(-300, "")) 2114 self.failUnless(t) 2115 2116 t = cls(5, tzinfo=FixedOffset(-300, "")) 2117 self.failUnless(t) 2118 2119 t = cls(5, tzinfo=FixedOffset(300, "")) 2120 self.failUnless(not t) 2121 2122 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, "")) 2123 self.failUnless(not t) 2124 2125 # Mostly ensuring this doesn't overflow internally. 2126 t = cls(0, tzinfo=FixedOffset(23*60 + 59, "")) 2127 self.failUnless(t) 2128 2129 # But this should yield a value error -- the utcoffset is bogus. 2130 t = cls(0, tzinfo=FixedOffset(24*60, "")) 2131 self.assertRaises(ValueError, lambda: bool(t)) 2132 2133 # Likewise. 2134 t = cls(0, tzinfo=FixedOffset(-24*60, "")) 2135 self.assertRaises(ValueError, lambda: bool(t)) 2136 2137 def test_replace(self): 2138 cls = self.theclass 2139 z100 = FixedOffset(100, "+100") 2140 zm200 = FixedOffset(timedelta(minutes=-200), "-200") 2141 args = [1, 2, 3, 4, z100] 2142 base = cls(*args) 2143 self.assertEqual(base, base.replace()) 2144 2145 i = 0 2146 for name, newval in (("hour", 5), 2147 ("minute", 6), 2148 ("second", 7), 2149 ("microsecond", 8), 2150 ("tzinfo", zm200)): 2151 newargs = args[:] 2152 newargs[i] = newval 2153 expected = cls(*newargs) 2154 got = base.replace(**{name: newval}) 2155 self.assertEqual(expected, got) 2156 i += 1 2157 2158 # Ensure we can get rid of a tzinfo. 2159 self.assertEqual(base.tzname(), "+100") 2160 base2 = base.replace(tzinfo=None) 2161 self.failUnless(base2.tzinfo is None) 2162 self.failUnless(base2.tzname() is None) 2163 2164 # Ensure we can add one. 2165 base3 = base2.replace(tzinfo=z100) 2166 self.assertEqual(base, base3) 2167 self.failUnless(base.tzinfo is base3.tzinfo) 2168 2169 # Out of bounds. 2170 base = cls(1) 2171 self.assertRaises(ValueError, base.replace, hour=24) 2172 self.assertRaises(ValueError, base.replace, minute=-1) 2173 self.assertRaises(ValueError, base.replace, second=100) 2174 self.assertRaises(ValueError, base.replace, microsecond=1000000) 2175 2176 def test_mixed_compare(self): 2177 t1 = time(1, 2, 3) 2178 t2 = time(1, 2, 3) 2179 self.assertEqual(t1, t2) 2180 t2 = t2.replace(tzinfo=None) 2181 self.assertEqual(t1, t2) 2182 t2 = t2.replace(tzinfo=FixedOffset(None, "")) 2183 self.assertEqual(t1, t2) 2184 t2 = t2.replace(tzinfo=FixedOffset(0, "")) 2185 self.assertRaises(TypeError, lambda: t1 == t2) 2186 2187 # In time w/ identical tzinfo objects, utcoffset is ignored. 2188 class Varies(tzinfo): 2189 def __init__(self): 2190 self.offset = timedelta(minutes=22) 2191 def utcoffset(self, t): 2192 self.offset += timedelta(minutes=1) 2193 return self.offset 2194 2195 v = Varies() 2196 t1 = t2.replace(tzinfo=v) 2197 t2 = t2.replace(tzinfo=v) 2198 self.assertEqual(t1.utcoffset(), timedelta(minutes=23)) 2199 self.assertEqual(t2.utcoffset(), timedelta(minutes=24)) 2200 self.assertEqual(t1, t2) 2201 2202 # But if they're not identical, it isn't ignored. 2203 t2 = t2.replace(tzinfo=Varies()) 2204 self.failUnless(t1 < t2) # t1's offset counter still going up 2205 2206 def test_subclass_timetz(self): 2207 2208 class C(self.theclass): 2209 theAnswer = 42 2210 2211 def __new__(cls, *args, **kws): 2212 temp = kws.copy() 2213 extra = temp.pop('extra') 2214 result = self.theclass.__new__(cls, *args, **temp) 2215 result.extra = extra 2216 return result 2217 2218 def newmeth(self, start): 2219 return start + self.hour + self.second 2220 2221 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1) 2222 2223 dt1 = self.theclass(*args) 2224 dt2 = C(*args, **{'extra': 7}) 2225 2226 self.assertEqual(dt2.__class__, C) 2227 self.assertEqual(dt2.theAnswer, 42) 2228 self.assertEqual(dt2.extra, 7) 2229 self.assertEqual(dt1.utcoffset(), dt2.utcoffset()) 2230 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7) 2231 2232 2233 # Testing datetime objects with a non-None tzinfo. 2234 2235 class TestDateTimeTZ(TestDateTime, TZInfoBase): 2236 theclass = datetime 2237 2238 def test_trivial(self): 2239 dt = self.theclass(1, 2, 3, 4, 5, 6, 7) 2240 self.assertEqual(dt.year, 1) 2241 self.assertEqual(dt.month, 2) 2242 self.assertEqual(dt.day, 3) 2243 self.assertEqual(dt.hour, 4) 2244 self.assertEqual(dt.minute, 5) 2245 self.assertEqual(dt.second, 6) 2246 self.assertEqual(dt.microsecond, 7) 2247 self.assertEqual(dt.tzinfo, None) 2248 2249 def test_even_more_compare(self): 2250 # The test_compare() and test_more_compare() inherited from TestDate 2251 # and TestDateTime covered non-tzinfo cases. 2252 2253 # Smallest possible after UTC adjustment. 2254 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "")) 2255 # Largest possible after UTC adjustment. 2256 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999, 2257 tzinfo=FixedOffset(-1439, "")) 2258 2259 # Make sure those compare correctly, and w/o overflow. 2260 self.failUnless(t1 < t2) 2261 self.failUnless(t1 != t2) 2262 self.failUnless(t2 > t1) 2263 2264 self.failUnless(t1 == t1) 2265 self.failUnless(t2 == t2) 2266 2267 # Equal afer adjustment. 2268 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, "")) 2269 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, "")) 2270 self.assertEqual(t1, t2) 2271 2272 # Change t1 not to subtract a minute, and t1 should be larger. 2273 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, "")) 2274 self.failUnless(t1 > t2) 2275 2276 # Change t1 to subtract 2 minutes, and t1 should be smaller. 2277 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, "")) 2278 self.failUnless(t1 < t2) 2279 2280 # Back to the original t1, but make seconds resolve it. 2281 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""), 2282 second=1) 2283 self.failUnless(t1 > t2) 2284 2285 # Likewise, but make microseconds resolve it. 2286 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""), 2287 microsecond=1) 2288 self.failUnless(t1 > t2) 2289 2290 # Make t2 naive and it should fail. 2291 t2 = self.theclass.min 2292 self.assertRaises(TypeError, lambda: t1 == t2) 2293 self.assertEqual(t2, t2) 2294 2295 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None. 2296 class Naive(tzinfo): 2297 def utcoffset(self, dt): return None 2298 t2 = self.theclass(5, 6, 7, tzinfo=Naive()) 2299 self.assertRaises(TypeError, lambda: t1 == t2) 2300 self.assertEqual(t2, t2) 2301 2302 # OTOH, it's OK to compare two of these mixing the two ways of being 2303 # naive. 2304 t1 = self.theclass(5, 6, 7) 2305 self.assertEqual(t1, t2) 2306 2307 # Try a bogus uctoffset. 2308 class Bogus(tzinfo): 2309 def utcoffset(self, dt): 2310 return timedelta(minutes=1440) # out of bounds 2311 t1 = self.theclass(2, 2, 2, tzinfo=Bogus()) 2312 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, "")) 2313 self.assertRaises(ValueError, lambda: t1 == t2) 2314 2315 def test_pickling(self): 2316 # Try one without a tzinfo. 2317 args = 6, 7, 23, 20, 59, 1, 64**2 2318 orig = self.theclass(*args) 2319 for pickler, unpickler, proto in pickle_choices: 2320 green = pickler.dumps(orig, proto) 2321 derived = unpickler.loads(green) 2322 self.assertEqual(orig, derived) 2323 2324 # Try one with a tzinfo. 2325 tinfo = PicklableFixedOffset(-300, 'cookie') 2326 orig = self.theclass(*args, **{'tzinfo': tinfo}) 2327 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0)) 2328 for pickler, unpickler, proto in pickle_choices: 2329 green = pickler.dumps(orig, proto) 2330 derived = unpickler.loads(green) 2331 self.assertEqual(orig, derived) 2332 self.failUnless(isinstance(derived.tzinfo, 2333 PicklableFixedOffset)) 2334 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) 2335 self.assertEqual(derived.tzname(), 'cookie') 2336 2337 def test_extreme_hashes(self): 2338 # If an attempt is made to hash these via subtracting the offset 2339 # then hashing a datetime object, OverflowError results. The 2340 # Python implementation used to blow up here. 2341 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "")) 2342 hash(t) 2343 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999, 2344 tzinfo=FixedOffset(-1439, "")) 2345 hash(t) 2346 2347 # OTOH, an OOB offset should blow up. 2348 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, "")) 2349 self.assertRaises(ValueError, hash, t) 2350 2351 def test_zones(self): 2352 est = FixedOffset(-300, "EST") 2353 utc = FixedOffset(0, "UTC") 2354 met = FixedOffset(60, "MET") 2355 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est) 2356 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc) 2357 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met) 2358 self.assertEqual(t1.tzinfo, est) 2359 self.assertEqual(t2.tzinfo, utc) 2360 self.assertEqual(t3.tzinfo, met) 2361 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300)) 2362 self.assertEqual(t2.utcoffset(), timedelta(minutes=0)) 2363 self.assertEqual(t3.utcoffset(), timedelta(minutes=60)) 2364 self.assertEqual(t1.tzname(), "EST") 2365 self.assertEqual(t2.tzname(), "UTC") 2366 self.assertEqual(t3.tzname(), "MET") 2367 self.assertEqual(hash(t1), hash(t2)) 2368 self.assertEqual(hash(t1), hash(t3)) 2369 self.assertEqual(hash(t2), hash(t3)) 2370 self.assertEqual(t1, t2) 2371 self.assertEqual(t1, t3) 2372 self.assertEqual(t2, t3) 2373 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00") 2374 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00") 2375 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00") 2376 d = 'datetime.datetime(2002, 3, 19, ' 2377 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)") 2378 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)") 2379 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)") 2380 2381 def test_combine(self): 2382 met = FixedOffset(60, "MET") 2383 d = date(2002, 3, 4) 2384 tz = time(18, 45, 3, 1234, tzinfo=met) 2385 dt = datetime.combine(d, tz) 2386 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234, 2387 tzinfo=met)) 2388 2389 def test_extract(self): 2390 met = FixedOffset(60, "MET") 2391 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met) 2392 self.assertEqual(dt.date(), date(2002, 3, 4)) 2393 self.assertEqual(dt.time(), time(18, 45, 3, 1234)) 2394 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met)) 2395 2396 def test_tz_aware_arithmetic(self): 2397 import random 2398 2399 now = self.theclass.now() 2400 tz55 = FixedOffset(-330, "west 5:30") 2401 timeaware = now.time().replace(tzinfo=tz55) 2402 nowaware = self.theclass.combine(now.date(), timeaware) 2403 self.failUnless(nowaware.tzinfo is tz55) 2404 self.assertEqual(nowaware.timetz(), timeaware) 2405 2406 # Can't mix aware and non-aware. 2407 self.assertRaises(TypeError, lambda: now - nowaware) 2408 self.assertRaises(TypeError, lambda: nowaware - now) 2409 2410 # And adding datetime's doesn't make sense, aware or not. 2411 self.assertRaises(TypeError, lambda: now + nowaware) 2412 self.assertRaises(TypeError, lambda: nowaware + now) 2413 self.assertRaises(TypeError, lambda: nowaware + nowaware) 2414 2415 # Subtracting should yield 0. 2416 self.assertEqual(now - now, timedelta(0)) 2417 self.assertEqual(nowaware - nowaware, timedelta(0)) 2418 2419 # Adding a delta should preserve tzinfo. 2420 delta = timedelta(weeks=1, minutes=12, microseconds=5678) 2421 nowawareplus = nowaware + delta 2422 self.failUnless(nowaware.tzinfo is tz55) 2423 nowawareplus2 = delta + nowaware 2424 self.failUnless(nowawareplus2.tzinfo is tz55) 2425 self.assertEqual(nowawareplus, nowawareplus2) 2426 2427 # that - delta should be what we started with, and that - what we 2428 # started with should be delta. 2429 diff = nowawareplus - delta 2430 self.failUnless(diff.tzinfo is tz55) 2431 self.assertEqual(nowaware, diff) 2432 self.assertRaises(TypeError, lambda: delta - nowawareplus) 2433 self.assertEqual(nowawareplus - nowaware, delta) 2434 2435 # Make up a random timezone. 2436 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone") 2437 # Attach it to nowawareplus. 2438 nowawareplus = nowawareplus.replace(tzinfo=tzr) 2439 self.failUnless(nowawareplus.tzinfo is tzr) 2440 # Make sure the difference takes the timezone adjustments into account. 2441 got = nowaware - nowawareplus 2442 # Expected: (nowaware base - nowaware offset) - 2443 # (nowawareplus base - nowawareplus offset) = 2444 # (nowaware base - nowawareplus base) + 2445 # (nowawareplus offset - nowaware offset) = 2446 # -delta + nowawareplus offset - nowaware offset 2447 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta 2448 self.assertEqual(got, expected) 2449 2450 # Try max possible difference. 2451 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min")) 2452 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999, 2453 tzinfo=FixedOffset(-1439, "max")) 2454 maxdiff = max - min 2455 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min + 2456 timedelta(minutes=2*1439)) 2457 2458 def test_tzinfo_now(self): 2459 meth = self.theclass.now 2460 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up). 2461 base = meth() 2462 # Try with and without naming the keyword. 2463 off42 = FixedOffset(42, "42") 2464 another = meth(off42) 2465 again = meth(tz=off42) 2466 self.failUnless(another.tzinfo is again.tzinfo) 2467 self.assertEqual(another.utcoffset(), timedelta(minutes=42)) 2468 # Bad argument with and w/o naming the keyword. 2469 self.assertRaises(TypeError, meth, 16) 2470 self.assertRaises(TypeError, meth, tzinfo=16) 2471 # Bad keyword name. 2472 self.assertRaises(TypeError, meth, tinfo=off42) 2473 # Too many args. 2474 self.assertRaises(TypeError, meth, off42, off42) 2475 2476 # We don't know which time zone we're in, and don't have a tzinfo 2477 # class to represent it, so seeing whether a tz argument actually 2478 # does a conversion is tricky. 2479 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0) 2480 utc = FixedOffset(0, "utc", 0) 2481 for dummy in range(3): 2482 now = datetime.now(weirdtz) 2483 self.failUnless(now.tzinfo is weirdtz) 2484 utcnow = datetime.utcnow().replace(tzinfo=utc) 2485 now2 = utcnow.astimezone(weirdtz) 2486 if abs(now - now2) < timedelta(seconds=30): 2487 break 2488 # Else the code is broken, or more than 30 seconds passed between 2489 # calls; assuming the latter, just try again. 2490 else: 2491 # Three strikes and we're out. 2492 self.fail("utcnow(), now(tz), or astimezone() may be broken") 2493 2494 def test_tzinfo_fromtimestamp(self): 2495 import time 2496 meth = self.theclass.fromtimestamp 2497 ts = time.time() 2498 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up). 2499 base = meth(ts) 2500 # Try with and without naming the keyword. 2501 off42 = FixedOffset(42, "42") 2502 another = meth(ts, off42) 2503 again = meth(ts, tz=off42) 2504 self.failUnless(another.tzinfo is again.tzinfo) 2505 self.assertEqual(another.utcoffset(), timedelta(minutes=42)) 2506 # Bad argument with and w/o naming the keyword. 2507 self.assertRaises(TypeError, meth, ts, 16) 2508 self.assertRaises(TypeError, meth, ts, tzinfo=16) 2509 # Bad keyword name. 2510 self.assertRaises(TypeError, meth, ts, tinfo=off42) 2511 # Too many args. 2512 self.assertRaises(TypeError, meth, ts, off42, off42) 2513 # Too few args. 2514 self.assertRaises(TypeError, meth) 2515 2516 # Try to make sure tz= actually does some conversion. 2517 timestamp = 1000000000 2518 utcdatetime = datetime.utcfromtimestamp(timestamp) 2519 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take. 2520 # But on some flavor of Mac, it's nowhere near that. So we can't have 2521 # any idea here what time that actually is, we can only test that 2522 # relative changes match. 2523 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero 2524 tz = FixedOffset(utcoffset, "tz", 0) 2525 expected = utcdatetime + utcoffset 2526 got = datetime.fromtimestamp(timestamp, tz) 2527 self.assertEqual(expected, got.replace(tzinfo=None)) 2528 2529 def test_tzinfo_utcnow(self): 2530 meth = self.theclass.utcnow 2531 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up). 2532 base = meth() 2533 # Try with and without naming the keyword; for whatever reason, 2534 # utcnow() doesn't accept a tzinfo argument. 2535 off42 = FixedOffset(42, "42") 2536 self.assertRaises(TypeError, meth, off42) 2537 self.assertRaises(TypeError, meth, tzinfo=off42) 2538 2539 def test_tzinfo_utcfromtimestamp(self): 2540 import time 2541 meth = self.theclass.utcfromtimestamp 2542 ts = time.time() 2543 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up). 2544 base = meth(ts) 2545 # Try with and without naming the keyword; for whatever reason, 2546 # utcfromtimestamp() doesn't accept a tzinfo argument. 2547 off42 = FixedOffset(42, "42") 2548 self.assertRaises(TypeError, meth, ts, off42) 2549 self.assertRaises(TypeError, meth, ts, tzinfo=off42) 2550 2551 def test_tzinfo_timetuple(self): 2552 # TestDateTime tested most of this. datetime adds a twist to the 2553 # DST flag. 2554 class DST(tzinfo): 2555 def __init__(self, dstvalue): 2556 if isinstance(dstvalue, int): 2557 dstvalue = timedelta(minutes=dstvalue) 2558 self.dstvalue = dstvalue 2559 def dst(self, dt): 2560 return self.dstvalue 2561 2562 cls = self.theclass 2563 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1): 2564 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue)) 2565 t = d.timetuple() 2566 self.assertEqual(1, t.tm_year) 2567 self.assertEqual(1, t.tm_mon) 2568 self.assertEqual(1, t.tm_mday) 2569 self.assertEqual(10, t.tm_hour) 2570 self.assertEqual(20, t.tm_min) 2571 self.assertEqual(30, t.tm_sec) 2572 self.assertEqual(0, t.tm_wday) 2573 self.assertEqual(1, t.tm_yday) 2574 self.assertEqual(flag, t.tm_isdst) 2575 2576 # dst() returns wrong type. 2577 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple) 2578 2579 # dst() at the edge. 2580 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1) 2581 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1) 2582 2583 # dst() out of range. 2584 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple) 2585 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple) 2586 2587 def test_utctimetuple(self): 2588 class DST(tzinfo): 2589 def __init__(self, dstvalue): 2590 if isinstance(dstvalue, int): 2591 dstvalue = timedelta(minutes=dstvalue) 2592 self.dstvalue = dstvalue 2593 def dst(self, dt): 2594 return self.dstvalue 2595 2596 cls = self.theclass 2597 # This can't work: DST didn't implement utcoffset. 2598 self.assertRaises(NotImplementedError, 2599 cls(1, 1, 1, tzinfo=DST(0)).utcoffset) 2600 2601 class UOFS(DST): 2602 def __init__(self, uofs, dofs=None): 2603 DST.__init__(self, dofs) 2604 self.uofs = timedelta(minutes=uofs) 2605 def utcoffset(self, dt): 2606 return self.uofs 2607 2608 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never 2609 # in effect for a UTC time. 2610 for dstvalue in -33, 33, 0, None: 2611 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue)) 2612 t = d.utctimetuple() 2613 self.assertEqual(d.year, t.tm_year) 2614 self.assertEqual(d.month, t.tm_mon) 2615 self.assertEqual(d.day, t.tm_mday) 2616 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm 2617 self.assertEqual(13, t.tm_min) 2618 self.assertEqual(d.second, t.tm_sec) 2619 self.assertEqual(d.weekday(), t.tm_wday) 2620 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1, 2621 t.tm_yday) 2622 self.assertEqual(0, t.tm_isdst) 2623 2624 # At the edges, UTC adjustment can normalize into years out-of-range 2625 # for a datetime object. Ensure that a correct timetuple is 2626 # created anyway. 2627 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439)) 2628 # That goes back 1 minute less than a full day. 2629 t = tiny.utctimetuple() 2630 self.assertEqual(t.tm_year, MINYEAR-1) 2631 self.assertEqual(t.tm_mon, 12) 2632 self.assertEqual(t.tm_mday, 31) 2633 self.assertEqual(t.tm_hour, 0) 2634 self.assertEqual(t.tm_min, 1) 2635 self.assertEqual(t.tm_sec, 37) 2636 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year 2637 self.assertEqual(t.tm_isdst, 0) 2638 2639 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439)) 2640 # That goes forward 1 minute less than a full day. 2641 t = huge.utctimetuple() 2642 self.assertEqual(t.tm_year, MAXYEAR+1) 2643 self.assertEqual(t.tm_mon, 1) 2644 self.assertEqual(t.tm_mday, 1) 2645 self.assertEqual(t.tm_hour, 23) 2646 self.assertEqual(t.tm_min, 58) 2647 self.assertEqual(t.tm_sec, 37) 2648 self.assertEqual(t.tm_yday, 1) 2649 self.assertEqual(t.tm_isdst, 0) 2650 2651 def test_tzinfo_isoformat(self): 2652 zero = FixedOffset(0, "+00:00") 2653 plus = FixedOffset(220, "+03:40") 2654 minus = FixedOffset(-231, "-03:51") 2655 unknown = FixedOffset(None, "") 2656 2657 cls = self.theclass 2658 datestr = '0001-02-03' 2659 for ofs in None, zero, plus, minus, unknown: 2660 for us in 0, 987001: 2661 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs) 2662 timestr = '04:05:59' + (us and '.987001' or '') 2663 ofsstr = ofs is not None and d.tzname() or '' 2664 tailstr = timestr + ofsstr 2665 iso = d.isoformat() 2666 self.assertEqual(iso, datestr + 'T' + tailstr) 2667 self.assertEqual(iso, d.isoformat('T')) 2668 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr) 2669 self.assertEqual(str(d), datestr + ' ' + tailstr) 2670 2671 def test_replace(self): 2672 cls = self.theclass 2673 z100 = FixedOffset(100, "+100") 2674 zm200 = FixedOffset(timedelta(minutes=-200), "-200") 2675 args = [1, 2, 3, 4, 5, 6, 7, z100] 2676 base = cls(*args) 2677 self.assertEqual(base, base.replace()) 2678 2679 i = 0 2680 for name, newval in (("year", 2), 2681 ("month", 3), 2682 ("day", 4), 2683 ("hour", 5), 2684 ("minute", 6), 2685 ("second", 7), 2686 ("microsecond", 8), 2687 ("tzinfo", zm200)): 2688 newargs = args[:] 2689 newargs[i] = newval 2690 expected = cls(*newargs) 2691 got = base.replace(**{name: newval}) 2692 self.assertEqual(expected, got) 2693 i += 1 2694 2695 # Ensure we can get rid of a tzinfo. 2696 self.assertEqual(base.tzname(), "+100") 2697 base2 = base.replace(tzinfo=None) 2698 self.failUnless(base2.tzinfo is None) 2699 self.failUnless(base2.tzname() is None) 2700 2701 # Ensure we can add one. 2702 base3 = base2.replace(tzinfo=z100) 2703 self.assertEqual(base, base3) 2704 self.failUnless(base.tzinfo is base3.tzinfo) 2705 2706 # Out of bounds. 2707 base = cls(2000, 2, 29) 2708 self.assertRaises(ValueError, base.replace, year=2001) 2709 2710 def test_more_astimezone(self): 2711 # The inherited test_astimezone covered some trivial and error cases. 2712 fnone = FixedOffset(None, "None") 2713 f44m = FixedOffset(44, "44") 2714 fm5h = FixedOffset(-timedelta(hours=5), "m300") 2715 2716 dt = self.theclass.now(tz=f44m) 2717 self.failUnless(dt.tzinfo is f44m) 2718 # Replacing with degenerate tzinfo raises an exception. 2719 self.assertRaises(ValueError, dt.astimezone, fnone) 2720 # Ditto with None tz. 2721 self.assertRaises(TypeError, dt.astimezone, None) 2722 # Replacing with same tzinfo makes no change. 2723 x = dt.astimezone(dt.tzinfo) 2724 self.failUnless(x.tzinfo is f44m) 2725 self.assertEqual(x.date(), dt.date()) 2726 self.assertEqual(x.time(), dt.time()) 2727 2728 # Replacing with different tzinfo does adjust. 2729 got = dt.astimezone(fm5h) 2730 self.failUnless(got.tzinfo is fm5h) 2731 self.assertEqual(got.utcoffset(), timedelta(hours=-5)) 2732 expected = dt - dt.utcoffset() # in effect, convert to UTC 2733 expected += fm5h.utcoffset(dt) # and from there to local time 2734 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo 2735 self.assertEqual(got.date(), expected.date()) 2736 self.assertEqual(got.time(), expected.time()) 2737 self.assertEqual(got.timetz(), expected.timetz()) 2738 self.failUnless(got.tzinfo is expected.tzinfo) 2739 self.assertEqual(got, expected) 2740 2741 def test_aware_subtract(self): 2742 cls = self.theclass 2743 2744 # Ensure that utcoffset() is ignored when the operands have the 2745 # same tzinfo member. 2746 class OperandDependentOffset(tzinfo): 2747 def utcoffset(self, t): 2748 if t.minute < 10: 2749 # d0 and d1 equal after adjustment 2750 return timedelta(minutes=t.minute) 2751 else: 2752 # d2 off in the weeds 2753 return timedelta(minutes=59) 2754 2755 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset()) 2756 d0 = base.replace(minute=3) 2757 d1 = base.replace(minute=9) 2758 d2 = base.replace(minute=11) 2759 for x in d0, d1, d2: 2760 for y in d0, d1, d2: 2761 got = x - y 2762 expected = timedelta(minutes=x.minute - y.minute) 2763 self.assertEqual(got, expected) 2764 2765 # OTOH, if the tzinfo members are distinct, utcoffsets aren't 2766 # ignored. 2767 base = cls(8, 9, 10, 11, 12, 13, 14) 2768 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset()) 2769 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset()) 2770 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset()) 2771 for x in d0, d1, d2: 2772 for y in d0, d1, d2: 2773 got = x - y 2774 if (x is d0 or x is d1) and (y is d0 or y is d1): 2775 expected = timedelta(0) 2776 elif x is y is d2: 2777 expected = timedelta(0) 2778 elif x is d2: 2779 expected = timedelta(minutes=(11-59)-0) 2780 else: 2781 assert y is d2 2782 expected = timedelta(minutes=0-(11-59)) 2783 self.assertEqual(got, expected) 2784 2785 def test_mixed_compare(self): 2786 t1 = datetime(1, 2, 3, 4, 5, 6, 7) 2787 t2 = datetime(1, 2, 3, 4, 5, 6, 7) 2788 self.assertEqual(t1, t2) 2789 t2 = t2.replace(tzinfo=None) 2790 self.assertEqual(t1, t2) 2791 t2 = t2.replace(tzinfo=FixedOffset(None, "")) 2792 self.assertEqual(t1, t2) 2793 t2 = t2.replace(tzinfo=FixedOffset(0, "")) 2794 self.assertRaises(TypeError, lambda: t1 == t2) 2795 2796 # In datetime w/ identical tzinfo objects, utcoffset is ignored. 2797 class Varies(tzinfo): 2798 def __init__(self): 2799 self.offset = timedelta(minutes=22) 2800 def utcoffset(self, t): 2801 self.offset += timedelta(minutes=1) 2802 return self.offset 2803 2804 v = Varies() 2805 t1 = t2.replace(tzinfo=v) 2806 t2 = t2.replace(tzinfo=v) 2807 self.assertEqual(t1.utcoffset(), timedelta(minutes=23)) 2808 self.assertEqual(t2.utcoffset(), timedelta(minutes=24)) 2809 self.assertEqual(t1, t2) 2810 2811 # But if they're not identical, it isn't ignored. 2812 t2 = t2.replace(tzinfo=Varies()) 2813 self.failUnless(t1 < t2) # t1's offset counter still going up 2814 2815 def test_subclass_datetimetz(self): 2816 2817 class C(self.theclass): 2818 theAnswer = 42 2819 2820 def __new__(cls, *args, **kws): 2821 temp = kws.copy() 2822 extra = temp.pop('extra') 2823 result = self.theclass.__new__(cls, *args, **temp) 2824 result.extra = extra 2825 return result 2826 2827 def newmeth(self, start): 2828 return start + self.hour + self.year 2829 2830 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1) 2831 2832 dt1 = self.theclass(*args) 2833 dt2 = C(*args, **{'extra': 7}) 2834 2835 self.assertEqual(dt2.__class__, C) 2836 self.assertEqual(dt2.theAnswer, 42) 2837 self.assertEqual(dt2.extra, 7) 2838 self.assertEqual(dt1.utcoffset(), dt2.utcoffset()) 2839 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7) 2840 2841 # Pain to set up DST-aware tzinfo classes. 2842 2843 def first_sunday_on_or_after(dt): 2844 days_to_go = 6 - dt.weekday() 2845 if days_to_go: 2846 dt += timedelta(days_to_go) 2847 return dt 2848 2849 ZERO = timedelta(0) 2850 HOUR = timedelta(hours=1) 2851 DAY = timedelta(days=1) 2852 # In the US, DST starts at 2am (standard time) on the first Sunday in April. 2853 DSTSTART = datetime(1, 4, 1, 2) 2854 # and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct, 2855 # which is the first Sunday on or after Oct 25. Because we view 1:MM as 2856 # being standard time on that day, there is no spelling in local time of 2857 # the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time). 2858 DSTEND = datetime(1, 10, 25, 1) 2859 2860 class USTimeZone(tzinfo): 2861 2862 def __init__(self, hours, reprname, stdname, dstname): 2863 self.stdoffset = timedelta(hours=hours) 2864 self.reprname = reprname 2865 self.stdname = stdname 2866 self.dstname = dstname 2867 2868 def __repr__(self): 2869 return self.reprname 2870 2871 def tzname(self, dt): 2872 if self.dst(dt): 2873 return self.dstname 2874 else: 2875 return self.stdname 2876 2877 def utcoffset(self, dt): 2878 return self.stdoffset + self.dst(dt) 2879 2880 def dst(self, dt): 2881 if dt is None or dt.tzinfo is None: 2882 # An exception instead may be sensible here, in one or more of 2883 # the cases. 2884 return ZERO 2885 assert dt.tzinfo is self 2886 2887 # Find first Sunday in April. 2888 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year)) 2889 assert start.weekday() == 6 and start.month == 4 and start.day <= 7 2890 2891 # Find last Sunday in October. 2892 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year)) 2893 assert end.weekday() == 6 and end.month == 10 and end.day >= 25 2894 2895 # Can't compare naive to aware objects, so strip the timezone from 2896 # dt first. 2897 if start <= dt.replace(tzinfo=None) < end: 2898 return HOUR 2899 else: 2900 return ZERO 2901 2902 Eastern = USTimeZone(-5, "Eastern", "EST", "EDT") 2903 Central = USTimeZone(-6, "Central", "CST", "CDT") 2904 Mountain = USTimeZone(-7, "Mountain", "MST", "MDT") 2905 Pacific = USTimeZone(-8, "Pacific", "PST", "PDT") 2906 utc_real = FixedOffset(0, "UTC", 0) 2907 # For better test coverage, we want another flavor of UTC that's west of 2908 # the Eastern and Pacific timezones. 2909 utc_fake = FixedOffset(-12*60, "UTCfake", 0) 2910 2911 class TestTimezoneConversions(unittest.TestCase): 2912 # The DST switch times for 2002, in std time. 2913 dston = datetime(2002, 4, 7, 2) 2914 dstoff = datetime(2002, 10, 27, 1) 2915 2916 theclass = datetime 2917 2918 # Check a time that's inside DST. 2919 def checkinside(self, dt, tz, utc, dston, dstoff): 2920 self.assertEqual(dt.dst(), HOUR) 2921 2922 # Conversion to our own timezone is always an identity. 2923 self.assertEqual(dt.astimezone(tz), dt) 2924 2925 asutc = dt.astimezone(utc) 2926 there_and_back = asutc.astimezone(tz) 2927 2928 # Conversion to UTC and back isn't always an identity here, 2929 # because there are redundant spellings (in local time) of 2930 # UTC time when DST begins: the clock jumps from 1:59:59 2931 # to 3:00:00, and a local time of 2:MM:SS doesn't really 2932 # make sense then. The classes above treat 2:MM:SS as 2933 # daylight time then (it's "after 2am"), really an alias 2934 # for 1:MM:SS standard time. The latter form is what 2935 # conversion back from UTC produces. 2936 if dt.date() == dston.date() and dt.hour == 2: 2937 # We're in the redundant hour, and coming back from 2938 # UTC gives the 1:MM:SS standard-time spelling. 2939 self.assertEqual(there_and_back + HOUR, dt) 2940 # Although during was considered to be in daylight 2941 # time, there_and_back is not. 2942 self.assertEqual(there_and_back.dst(), ZERO) 2943 # They're the same times in UTC. 2944 self.assertEqual(there_and_back.astimezone(utc), 2945 dt.astimezone(utc)) 2946 else: 2947 # We're not in the redundant hour. 2948 self.assertEqual(dt, there_and_back) 2949 2950 # Because we have a redundant spelling when DST begins, there is 2951 # (unforunately) an hour when DST ends that can't be spelled at all in 2952 # local time. When DST ends, the clock jumps from 1:59 back to 1:00 2953 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be 2954 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be 2955 # daylight time. The hour 1:MM daylight == 0:MM standard can't be 2956 # expressed in local time. Nevertheless, we want conversion back 2957 # from UTC to mimic the local clock's "repeat an hour" behavior. 2958 nexthour_utc = asutc + HOUR 2959 nexthour_tz = nexthour_utc.astimezone(tz) 2960 if dt.date() == dstoff.date() and dt.hour == 0: 2961 # We're in the hour before the last DST hour. The last DST hour 2962 # is ineffable. We want the conversion back to repeat 1:MM. 2963 self.assertEqual(nexthour_tz, dt.replace(hour=1)) 2964 nexthour_utc += HOUR 2965 nexthour_tz = nexthour_utc.astimezone(tz) 2966 self.assertEqual(nexthour_tz, dt.replace(hour=1)) 2967 else: 2968 self.assertEqual(nexthour_tz - dt, HOUR) 2969 2970 # Check a time that's outside DST. 2971 def checkoutside(self, dt, tz, utc): 2972 self.assertEqual(dt.dst(), ZERO) 2973 2974 # Conversion to our own timezone is always an identity. 2975 self.assertEqual(dt.astimezone(tz), dt) 2976 2977 # Converting to UTC and back is an identity too. 2978 asutc = dt.astimezone(utc) 2979 there_and_back = asutc.astimezone(tz) 2980 self.assertEqual(dt, there_and_back) 2981 2982 def convert_between_tz_and_utc(self, tz, utc): 2983 dston = self.dston.replace(tzinfo=tz) 2984 # Because 1:MM on the day DST ends is taken as being standard time, 2985 # there is no spelling in tz for the last hour of daylight time. 2986 # For purposes of the test, the last hour of DST is 0:MM, which is 2987 # taken as being daylight time (and 1:MM is taken as being standard 2988 # time). 2989 dstoff = self.dstoff.replace(tzinfo=tz) 2990 for delta in (timedelta(weeks=13), 2991 DAY, 2992 HOUR, 2993 timedelta(minutes=1), 2994 timedelta(microseconds=1)): 2995 2996 self.checkinside(dston, tz, utc, dston, dstoff) 2997 for during in dston + delta, dstoff - delta: 2998 self.checkinside(during, tz, utc, dston, dstoff) 2999 3000 self.checkoutside(dstoff, tz, utc) 3001 for outside in dston - delta, dstoff + delta: 3002 self.checkoutside(outside, tz, utc) 3003 3004 def test_easy(self): 3005 # Despite the name of this test, the endcases are excruciating. 3006 self.convert_between_tz_and_utc(Eastern, utc_real) 3007 self.convert_between_tz_and_utc(Pacific, utc_real) 3008 self.convert_between_tz_and_utc(Eastern, utc_fake) 3009 self.convert_between_tz_and_utc(Pacific, utc_fake) 3010 # The next is really dancing near the edge. It works because 3011 # Pacific and Eastern are far enough apart that their "problem 3012 # hours" don't overlap. 3013 self.convert_between_tz_and_utc(Eastern, Pacific) 3014 self.convert_between_tz_and_utc(Pacific, Eastern) 3015 # OTOH, these fail! Don't enable them. The difficulty is that 3016 # the edge case tests assume that every hour is representable in 3017 # the "utc" class. This is always true for a fixed-offset tzinfo 3018 # class (lke utc_real and utc_fake), but not for Eastern or Central. 3019 # For these adjacent DST-aware time zones, the range of time offsets 3020 # tested ends up creating hours in the one that aren't representable 3021 # in the other. For the same reason, we would see failures in the 3022 # Eastern vs Pacific tests too if we added 3*HOUR to the list of 3023 # offset deltas in convert_between_tz_and_utc(). 3024 # 3025 # self.convert_between_tz_and_utc(Eastern, Central) # can't work 3026 # self.convert_between_tz_and_utc(Central, Eastern) # can't work 3027 3028 def test_tricky(self): 3029 # 22:00 on day before daylight starts. 3030 fourback = self.dston - timedelta(hours=4) 3031 ninewest = FixedOffset(-9*60, "-0900", 0) 3032 fourback = fourback.replace(tzinfo=ninewest) 3033 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after 3034 # 2", we should get the 3 spelling. 3035 # If we plug 22:00 the day before into Eastern, it "looks like std 3036 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4 3037 # to 22:00 lands on 2:00, which makes no sense in local time (the 3038 # local clock jumps from 1 to 3). The point here is to make sure we 3039 # get the 3 spelling. 3040 expected = self.dston.replace(hour=3) 3041 got = fourback.astimezone(Eastern).replace(tzinfo=None) 3042 self.assertEqual(expected, got) 3043 3044 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that 3045 # case we want the 1:00 spelling. 3046 sixutc = self.dston.replace(hour=6, tzinfo=utc_real) 3047 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4, 3048 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST 3049 # spelling. 3050 expected = self.dston.replace(hour=1) 3051 got = sixutc.astimezone(Eastern).replace(tzinfo=None) 3052 self.assertEqual(expected, got) 3053 3054 # Now on the day DST ends, we want "repeat an hour" behavior. 3055 # UTC 4:MM 5:MM 6:MM 7:MM checking these 3056 # EST 23:MM 0:MM 1:MM 2:MM 3057 # EDT 0:MM 1:MM 2:MM 3:MM 3058 # wall 0:MM 1:MM 1:MM 2:MM against these 3059 for utc in utc_real, utc_fake: 3060 for tz in Eastern, Pacific: 3061 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM 3062 # Convert that to UTC. 3063 first_std_hour -= tz.utcoffset(None) 3064 # Adjust for possibly fake UTC. 3065 asutc = first_std_hour + utc.utcoffset(None) 3066 # First UTC hour to convert; this is 4:00 when utc=utc_real & 3067 # tz=Eastern. 3068 asutcbase = asutc.replace(tzinfo=utc) 3069 for tzhour in (0, 1, 1, 2): 3070 expectedbase = self.dstoff.replace(hour=tzhour) 3071 for minute in 0, 30, 59: 3072 expected = expectedbase.replace(minute=minute) 3073 asutc = asutcbase.replace(minute=minute) 3074 astz = asutc.astimezone(tz) 3075 self.assertEqual(astz.replace(tzinfo=None), expected) 3076 asutcbase += HOUR 3077 3078 3079 def test_bogus_dst(self): 3080 class ok(tzinfo): 3081 def utcoffset(self, dt): return HOUR 3082 def dst(self, dt): return HOUR 3083 3084 now = self.theclass.now().replace(tzinfo=utc_real) 3085 # Doesn't blow up. 3086 now.astimezone(ok()) 3087 3088 # Does blow up. 3089 class notok(ok): 3090 def dst(self, dt): return None 3091 self.assertRaises(ValueError, now.astimezone, notok()) 3092 3093 def test_fromutc(self): 3094 self.assertRaises(TypeError, Eastern.fromutc) # not enough args 3095 now = datetime.utcnow().replace(tzinfo=utc_real) 3096 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo 3097 now = now.replace(tzinfo=Eastern) # insert correct tzinfo 3098 enow = Eastern.fromutc(now) # doesn't blow up 3099 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member 3100 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args 3101 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type 3102 3103 # Always converts UTC to standard time. 3104 class FauxUSTimeZone(USTimeZone): 3105 def fromutc(self, dt): 3106 return dt + self.stdoffset 3107 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT") 3108 3109 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM 3110 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM 3111 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM 3112 3113 # Check around DST start. 3114 start = self.dston.replace(hour=4, tzinfo=Eastern) 3115 fstart = start.replace(tzinfo=FEastern) 3116 for wall in 23, 0, 1, 3, 4, 5: 3117 expected = start.replace(hour=wall) 3118 if wall == 23: 3119 expected -= timedelta(days=1) 3120 got = Eastern.fromutc(start) 3121 self.assertEqual(expected, got) 3122 3123 expected = fstart + FEastern.stdoffset 3124 got = FEastern.fromutc(fstart) 3125 self.assertEqual(expected, got) 3126 3127 # Ensure astimezone() calls fromutc() too. 3128 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern) 3129 self.assertEqual(expected, got) 3130 3131 start += HOUR 3132 fstart += HOUR 3133 3134 # Check around DST end. 3135 start = self.dstoff.replace(hour=4, tzinfo=Eastern) 3136 fstart = start.replace(tzinfo=FEastern) 3137 for wall in 0, 1, 1, 2, 3, 4: 3138 expected = start.replace(hour=wall) 3139 got = Eastern.fromutc(start) 3140 self.assertEqual(expected, got) 3141 3142 expected = fstart + FEastern.stdoffset 3143 got = FEastern.fromutc(fstart) 3144 self.assertEqual(expected, got) 3145 3146 # Ensure astimezone() calls fromutc() too. 3147 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern) 3148 self.assertEqual(expected, got) 3149 3150 start += HOUR 3151 fstart += HOUR 3152 3153 3154 ############################################################################# 3155 # oddballs 3156 3157 class Oddballs(unittest.TestCase): 3158 3159 def test_bug_1028306(self): 3160 # Trying to compare a date to a datetime should act like a mixed- 3161 # type comparison, despite that datetime is a subclass of date. 3162 as_date = date.today() 3163 as_datetime = datetime.combine(as_date, time()) 3164 self.assert_(as_date != as_datetime) 3165 self.assert_(as_datetime != as_date) 3166 self.assert_(not as_date == as_datetime) 3167 self.assert_(not as_datetime == as_date) 3168 self.assertRaises(TypeError, lambda: as_date < as_datetime) 3169 self.assertRaises(TypeError, lambda: as_datetime < as_date) 3170 self.assertRaises(TypeError, lambda: as_date <= as_datetime) 3171 self.assertRaises(TypeError, lambda: as_datetime <= as_date) 3172 self.assertRaises(TypeError, lambda: as_date > as_datetime) 3173 self.assertRaises(TypeError, lambda: as_datetime > as_date) 3174 self.assertRaises(TypeError, lambda: as_date >= as_datetime) 3175 self.assertRaises(TypeError, lambda: as_datetime >= as_date) 3176 3177 # Neverthelss, comparison should work with the base-class (date) 3178 # projection if use of a date method is forced. 3179 self.assert_(as_date.__eq__(as_datetime)) 3180 different_day = (as_date.day + 1) % 20 + 1 3181 self.assert_(not as_date.__eq__(as_datetime.replace(day= 3182 different_day))) 3183 3184 # And date should compare with other subclasses of date. If a 3185 # subclass wants to stop this, it's up to the subclass to do so. 3186 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day) 3187 self.assertEqual(as_date, date_sc) 3188 self.assertEqual(date_sc, as_date) 3189 3190 # Ditto for datetimes. 3191 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month, 3192 as_date.day, 0, 0, 0) 3193 self.assertEqual(as_datetime, datetime_sc) 3194 self.assertEqual(datetime_sc, as_datetime) 3195 3196 def test_suite(): 3197 allsuites = [unittest.makeSuite(klass, 'test') 3198 for klass in (TestModule, 3199 TestTZInfo, 3200 TestTimeDelta, 3201 TestDateOnly, 3202 TestDate, 3203 TestDateTime, 3204 TestTime, 3205 TestTimeTZ, 3206 TestDateTimeTZ, 3207 TestTimezoneConversions, 3208 Oddballs, 3209 ) 3210 ] 3211 return unittest.TestSuite(allsuites) 3212 3213 def test_main(): 3214 import gc 3215 import sys 3216 3217 thesuite = test_suite() 3218 lastrc = None 3219 while True: 3220 test_support.run_suite(thesuite) 3221 if 1: # change to 0, under a debug build, for some leak detection 3222 break 3223 gc.collect() 3224 if gc.garbage: 3225 raise SystemError("gc.garbage not empty after test run: %r" % 3226 gc.garbage) 3227 if hasattr(sys, 'gettotalrefcount'): 3228 thisrc = sys.gettotalrefcount() 3229 print >> sys.stderr, '*' * 10, 'total refs:', thisrc, 3230 if lastrc: 3231 print >> sys.stderr, 'delta:', thisrc - lastrc 3232 else: 3233 print >> sys.stderr 3234 lastrc = thisrc 3235 3236 if __name__ == "__main__": 3237 test_main() 3238
Generated by PyXR 0.9.4