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