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