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