blob: 058fb900bc769295d75ba625b2856094842dd1ac [file] [log] [blame]
Tim Peters2a799bf2002-12-16 20:18:38 +00001"""Test date/time type."""
2
3import sys
4import unittest
5
6from test import test_support
7
8from datetime import MINYEAR, MAXYEAR
9from datetime import timedelta
10from datetime import tzinfo
11from datetime import time, timetz
12from datetime import date, datetime, datetimetz
13
14#############################################################################
15# module tests
16
17class TestModule(unittest.TestCase):
18
19 def test_constants(self):
20 import datetime
21 self.assertEqual(datetime.MINYEAR, 1)
22 self.assertEqual(datetime.MAXYEAR, 9999)
23
24#############################################################################
25# tzinfo tests
26
27class FixedOffset(tzinfo):
28 def __init__(self, offset, name, dstoffset=42):
29 self.__offset = offset
30 self.__name = name
31 self.__dstoffset = dstoffset
32 def __repr__(self):
33 return self.__name.lower()
34 def utcoffset(self, dt):
35 return self.__offset
36 def tzname(self, dt):
37 return self.__name
38 def dst(self, dt):
39 return self.__dstoffset
40
41class TestTZInfo(unittest.TestCase):
42
43 def test_non_abstractness(self):
44 # In order to allow subclasses to get pickled, the C implementation
45 # wasn't able to get away with having __init__ raise
46 # NotImplementedError.
47 useless = tzinfo()
48 dt = datetime.max
49 self.assertRaises(NotImplementedError, useless.tzname, dt)
50 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
51 self.assertRaises(NotImplementedError, useless.dst, dt)
52
53 def test_subclass_must_override(self):
54 class NotEnough(tzinfo):
55 def __init__(self, offset, name):
56 self.__offset = offset
57 self.__name = name
58 self.failUnless(issubclass(NotEnough, tzinfo))
59 ne = NotEnough(3, "NotByALongShot")
60 self.failUnless(isinstance(ne, tzinfo))
61
62 dt = datetime.now()
63 self.assertRaises(NotImplementedError, ne.tzname, dt)
64 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
65 self.assertRaises(NotImplementedError, ne.dst, dt)
66
67 def test_normal(self):
68 fo = FixedOffset(3, "Three")
69 self.failUnless(isinstance(fo, tzinfo))
70 for dt in datetime.now(), None:
71 self.assertEqual(fo.utcoffset(dt), 3)
72 self.assertEqual(fo.tzname(dt), "Three")
73 self.assertEqual(fo.dst(dt), 42)
74
75 def test_pickling_base(self):
76 import pickle, cPickle
77
78 # There's no point to pickling tzinfo objects on their own (they
79 # carry no data), but they need to be picklable anyway else
80 # concrete subclasses can't be pickled.
81 orig = tzinfo.__new__(tzinfo)
82 self.failUnless(type(orig) is tzinfo)
83 for pickler in pickle, cPickle:
84 for binary in 0, 1:
85 green = pickler.dumps(orig, binary)
86 derived = pickler.loads(green)
87 self.failUnless(type(derived) is tzinfo)
88
89 def test_pickling_subclass(self):
90 import pickle, cPickle
91
92 # Make sure we can pickle/unpickle an instance of a subclass.
93 orig = FixedOffset(-300, 'cookie')
94 self.failUnless(isinstance(orig, tzinfo))
95 self.failUnless(type(orig) is FixedOffset)
96 self.assertEqual(orig.utcoffset(None), -300)
97 self.assertEqual(orig.tzname(None), 'cookie')
98 for pickler in pickle, cPickle:
99 for binary in 0, 1:
100 green = pickler.dumps(orig, binary)
101 derived = pickler.loads(green)
102 self.failUnless(isinstance(derived, tzinfo))
103 self.failUnless(type(derived) is FixedOffset)
104 self.assertEqual(derived.utcoffset(None), -300)
105 self.assertEqual(derived.tzname(None), 'cookie')
106
107#############################################################################
108# timedelta tests
109
110class TestTimeDelta(unittest.TestCase):
111
112 def test_constructor(self):
113 eq = self.assertEqual
114 td = timedelta
115
116 # Check keyword args to constructor
117 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
118 milliseconds=0, microseconds=0))
119 eq(td(1), td(days=1))
120 eq(td(0, 1), td(seconds=1))
121 eq(td(0, 0, 1), td(microseconds=1))
122 eq(td(weeks=1), td(days=7))
123 eq(td(days=1), td(hours=24))
124 eq(td(hours=1), td(minutes=60))
125 eq(td(minutes=1), td(seconds=60))
126 eq(td(seconds=1), td(milliseconds=1000))
127 eq(td(milliseconds=1), td(microseconds=1000))
128
129 # Check float args to constructor
130 eq(td(weeks=1.0/7), td(days=1))
131 eq(td(days=1.0/24), td(hours=1))
132 eq(td(hours=1.0/60), td(minutes=1))
133 eq(td(minutes=1.0/60), td(seconds=1))
134 eq(td(seconds=0.001), td(milliseconds=1))
135 eq(td(milliseconds=0.001), td(microseconds=1))
136
137 def test_computations(self):
138 eq = self.assertEqual
139 td = timedelta
140
141 a = td(7) # One week
142 b = td(0, 60) # One minute
143 c = td(0, 0, 1000) # One millisecond
144 eq(a+b+c, td(7, 60, 1000))
145 eq(a-b, td(6, 24*3600 - 60))
146 eq(-a, td(-7))
147 eq(+a, td(7))
148 eq(-b, td(-1, 24*3600 - 60))
149 eq(-c, td(-1, 24*3600 - 1, 999000))
150 eq(abs(a), a)
151 eq(abs(-a), a)
152 eq(td(6, 24*3600), a)
153 eq(td(0, 0, 60*1000000), b)
154 eq(a*10, td(70))
155 eq(a*10, 10*a)
156 eq(a*10L, 10*a)
157 eq(b*10, td(0, 600))
158 eq(10*b, td(0, 600))
159 eq(b*10L, td(0, 600))
160 eq(c*10, td(0, 0, 10000))
161 eq(10*c, td(0, 0, 10000))
162 eq(c*10L, td(0, 0, 10000))
163 eq(a*-1, -a)
164 eq(b*-2, -b-b)
165 eq(c*-2, -c+-c)
166 eq(b*(60*24), (b*60)*24)
167 eq(b*(60*24), (60*b)*24)
168 eq(c*1000, td(0, 1))
169 eq(1000*c, td(0, 1))
170 eq(a//7, td(1))
171 eq(b//10, td(0, 6))
172 eq(c//1000, td(0, 0, 1))
173 eq(a//10, td(0, 7*24*360))
174 eq(a//3600000, td(0, 0, 7*24*1000))
175
176 def test_disallowed_computations(self):
177 a = timedelta(42)
178
179 # Add/sub ints, longs, floats should be illegal
180 for i in 1, 1L, 1.0:
181 self.assertRaises(TypeError, lambda: a+i)
182 self.assertRaises(TypeError, lambda: a-i)
183 self.assertRaises(TypeError, lambda: i+a)
184 self.assertRaises(TypeError, lambda: i-a)
185
186 # Mul/div by float isn't supported.
187 x = 2.3
188 self.assertRaises(TypeError, lambda: a*x)
189 self.assertRaises(TypeError, lambda: x*a)
190 self.assertRaises(TypeError, lambda: a/x)
191 self.assertRaises(TypeError, lambda: x/a)
192 self.assertRaises(TypeError, lambda: a // x)
193 self.assertRaises(TypeError, lambda: x // a)
194
195 # Divison of int by timedelta doesn't make sense.
196 # Division by zero doesn't make sense.
197 for zero in 0, 0L:
198 self.assertRaises(TypeError, lambda: zero // a)
199 self.assertRaises(ZeroDivisionError, lambda: a // zero)
200
201 def test_basic_attributes(self):
202 days, seconds, us = 1, 7, 31
203 td = timedelta(days, seconds, us)
204 self.assertEqual(td.days, days)
205 self.assertEqual(td.seconds, seconds)
206 self.assertEqual(td.microseconds, us)
207
208 def test_carries(self):
209 t1 = timedelta(days=100,
210 weeks=-7,
211 hours=-24*(100-49),
212 minutes=-3,
213 seconds=12,
214 microseconds=(3*60 - 12) * 1e6 + 1)
215 t2 = timedelta(microseconds=1)
216 self.assertEqual(t1, t2)
217
218 def test_hash_equality(self):
219 t1 = timedelta(days=100,
220 weeks=-7,
221 hours=-24*(100-49),
222 minutes=-3,
223 seconds=12,
224 microseconds=(3*60 - 12) * 1000000)
225 t2 = timedelta()
226 self.assertEqual(hash(t1), hash(t2))
227
228 t1 += timedelta(weeks=7)
229 t2 += timedelta(days=7*7)
230 self.assertEqual(t1, t2)
231 self.assertEqual(hash(t1), hash(t2))
232
233 d = {t1: 1}
234 d[t2] = 2
235 self.assertEqual(len(d), 1)
236 self.assertEqual(d[t1], 2)
237
238 def test_pickling(self):
239 import pickle, cPickle
240 args = 12, 34, 56
241 orig = timedelta(*args)
242 state = orig.__getstate__()
243 self.assertEqual(args, state)
244 derived = timedelta()
245 derived.__setstate__(state)
246 self.assertEqual(orig, derived)
247 for pickler in pickle, cPickle:
248 for binary in 0, 1:
249 green = pickler.dumps(orig, binary)
250 derived = pickler.loads(green)
251 self.assertEqual(orig, derived)
252
253 def test_compare(self):
254 t1 = timedelta(2, 3, 4)
255 t2 = timedelta(2, 3, 4)
256 self.failUnless(t1 == t2)
257 self.failUnless(t1 <= t2)
258 self.failUnless(t1 >= t2)
259 self.failUnless(not t1 != t2)
260 self.failUnless(not t1 < t2)
261 self.failUnless(not t1 > t2)
262 self.assertEqual(cmp(t1, t2), 0)
263 self.assertEqual(cmp(t2, t1), 0)
264
265 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
266 t2 = timedelta(*args) # this is larger than t1
267 self.failUnless(t1 < t2)
268 self.failUnless(t2 > t1)
269 self.failUnless(t1 <= t2)
270 self.failUnless(t2 >= t1)
271 self.failUnless(t1 != t2)
272 self.failUnless(t2 != t1)
273 self.failUnless(not t1 == t2)
274 self.failUnless(not t2 == t1)
275 self.failUnless(not t1 > t2)
276 self.failUnless(not t2 < t1)
277 self.failUnless(not t1 >= t2)
278 self.failUnless(not t2 <= t1)
279 self.assertEqual(cmp(t1, t2), -1)
280 self.assertEqual(cmp(t2, t1), 1)
281
282 for badarg in 10, 10L, 34.5, "abc", {}, [], ():
283 self.assertRaises(TypeError, lambda: t1 == badarg)
284 self.assertRaises(TypeError, lambda: t1 != badarg)
285 self.assertRaises(TypeError, lambda: t1 <= badarg)
286 self.assertRaises(TypeError, lambda: t1 < badarg)
287 self.assertRaises(TypeError, lambda: t1 > badarg)
288 self.assertRaises(TypeError, lambda: t1 >= badarg)
289 self.assertRaises(TypeError, lambda: badarg == t1)
290 self.assertRaises(TypeError, lambda: badarg != t1)
291 self.assertRaises(TypeError, lambda: badarg <= t1)
292 self.assertRaises(TypeError, lambda: badarg < t1)
293 self.assertRaises(TypeError, lambda: badarg > t1)
294 self.assertRaises(TypeError, lambda: badarg >= t1)
295
296 def test_str(self):
297 td = timedelta
298 eq = self.assertEqual
299
300 eq(str(td(1)), "1 day, 0:00:00")
301 eq(str(td(-1)), "-1 day, 0:00:00")
302 eq(str(td(2)), "2 days, 0:00:00")
303 eq(str(td(-2)), "-2 days, 0:00:00")
304
305 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
306 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
307 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
308 "-210 days, 23:12:34")
309
310 eq(str(td(milliseconds=1)), "0:00:00.001000")
311 eq(str(td(microseconds=3)), "0:00:00.000003")
312
313 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
314 microseconds=999999)),
315 "999999999 days, 23:59:59.999999")
316
317 def test_roundtrip(self):
318 for td in (timedelta(days=999999999, hours=23, minutes=59,
319 seconds=59, microseconds=999999),
320 timedelta(days=-999999999),
321 timedelta(days=1, seconds=2, microseconds=3)):
322
323 # Verify td -> string -> td identity.
324 s = repr(td)
325 self.failUnless(s.startswith('datetime.'))
326 s = s[9:]
327 td2 = eval(s)
328 self.assertEqual(td, td2)
329
330 # Verify identity via reconstructing from pieces.
331 td2 = timedelta(td.days, td.seconds, td.microseconds)
332 self.assertEqual(td, td2)
333
334 def test_resolution_info(self):
335 self.assert_(isinstance(timedelta.min, timedelta))
336 self.assert_(isinstance(timedelta.max, timedelta))
337 self.assert_(isinstance(timedelta.resolution, timedelta))
338 self.assert_(timedelta.max > timedelta.min)
339 self.assertEqual(timedelta.min, timedelta(-999999999))
340 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
341 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
342
343 def test_overflow(self):
344 tiny = timedelta.resolution
345
346 td = timedelta.min + tiny
347 td -= tiny # no problem
348 self.assertRaises(OverflowError, td.__sub__, tiny)
349 self.assertRaises(OverflowError, td.__add__, -tiny)
350
351 td = timedelta.max - tiny
352 td += tiny # no problem
353 self.assertRaises(OverflowError, td.__add__, tiny)
354 self.assertRaises(OverflowError, td.__sub__, -tiny)
355
356 self.assertRaises(OverflowError, lambda: -timedelta.max)
357
358 def test_microsecond_rounding(self):
359 td = timedelta
360 eq = self.assertEqual
361
362 # Single-field rounding.
363 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
364 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
365 eq(td(milliseconds=0.6/1000), td(microseconds=1))
366 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
367
368 # Rounding due to contributions from more than one field.
369 us_per_hour = 3600e6
370 us_per_day = us_per_hour * 24
371 eq(td(days=.4/us_per_day), td(0))
372 eq(td(hours=.2/us_per_hour), td(0))
373 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
374
375 eq(td(days=-.4/us_per_day), td(0))
376 eq(td(hours=-.2/us_per_hour), td(0))
377 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
378
379 def test_massive_normalization(self):
380 td = timedelta(microseconds=-1)
381 self.assertEqual((td.days, td.seconds, td.microseconds),
382 (-1, 24*3600-1, 999999))
383
384 def test_bool(self):
385 self.failUnless(timedelta(1))
386 self.failUnless(timedelta(0, 1))
387 self.failUnless(timedelta(0, 0, 1))
388 self.failUnless(timedelta(microseconds=1))
389 self.failUnless(not timedelta(0))
390
391#############################################################################
392# date tests
393
394class TestDateOnly(unittest.TestCase):
395 # Tests here won't pass if also run on datetime objects, so don't
396 # subclass this to test datetimes too.
397
398 def test_delta_non_days_ignored(self):
399 dt = date(2000, 1, 2)
400 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
401 microseconds=5)
402 days = timedelta(delta.days)
403 self.assertEqual(days, timedelta(1))
404
405 dt2 = dt + delta
406 self.assertEqual(dt2, dt + days)
407
408 dt2 = delta + dt
409 self.assertEqual(dt2, dt + days)
410
411 dt2 = dt - delta
412 self.assertEqual(dt2, dt - days)
413
414 delta = -delta
415 days = timedelta(delta.days)
416 self.assertEqual(days, timedelta(-2))
417
418 dt2 = dt + delta
419 self.assertEqual(dt2, dt + days)
420
421 dt2 = delta + dt
422 self.assertEqual(dt2, dt + days)
423
424 dt2 = dt - delta
425 self.assertEqual(dt2, dt - days)
426
427class TestDate(unittest.TestCase):
428 # Tests here should pass for both dates and datetimes, except for a
429 # few tests that TestDateTime overrides.
430
431 theclass = date
432
433 def test_basic_attributes(self):
434 dt = self.theclass(2002, 3, 1)
435 self.assertEqual(dt.year, 2002)
436 self.assertEqual(dt.month, 3)
437 self.assertEqual(dt.day, 1)
438
439 def test_roundtrip(self):
440 for dt in (self.theclass(1, 2, 3),
441 self.theclass.today()):
442 # Verify dt -> string -> date identity.
443 s = repr(dt)
444 self.failUnless(s.startswith('datetime.'))
445 s = s[9:]
446 dt2 = eval(s)
447 self.assertEqual(dt, dt2)
448
449 # Verify identity via reconstructing from pieces.
450 dt2 = self.theclass(dt.year, dt.month, dt.day)
451 self.assertEqual(dt, dt2)
452
453 def test_ordinal_conversions(self):
454 # Check some fixed values.
455 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
456 (1, 12, 31, 365),
457 (2, 1, 1, 366),
458 # first example from "Calendrical Calculations"
459 (1945, 11, 12, 710347)]:
460 d = self.theclass(y, m, d)
461 self.assertEqual(n, d.toordinal())
462 fromord = self.theclass.fromordinal(n)
463 self.assertEqual(d, fromord)
464 if hasattr(fromord, "hour"):
465 # if we're checking something fancier than a date, verify
466 # the extra fields have been zeroed out
467 self.assertEqual(fromord.hour, 0)
468 self.assertEqual(fromord.minute, 0)
469 self.assertEqual(fromord.second, 0)
470 self.assertEqual(fromord.microsecond, 0)
471
472 # Check first and last days of year spottily across the whole
473 # range of years supported.
474 for year in xrange(MINYEAR, MAXYEAR+1, 7):
475 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
476 d = self.theclass(year, 1, 1)
477 n = d.toordinal()
478 d2 = self.theclass.fromordinal(n)
479 self.assertEqual(d, d2)
480 # Verify that moving back a day gets to the end of year-1.
481 if year > 1:
482 d = self.theclass.fromordinal(n-1)
483 d2 = self.theclass(year-1, 12, 31)
484 self.assertEqual(d, d2)
485 self.assertEqual(d2.toordinal(), n-1)
486
487 # Test every day in a leap-year and a non-leap year.
488 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
489 for year, isleap in (2000, True), (2002, False):
490 n = self.theclass(year, 1, 1).toordinal()
491 for month, maxday in zip(range(1, 13), dim):
492 if month == 2 and isleap:
493 maxday += 1
494 for day in range(1, maxday+1):
495 d = self.theclass(year, month, day)
496 self.assertEqual(d.toordinal(), n)
497 self.assertEqual(d, self.theclass.fromordinal(n))
498 n += 1
499
500 def test_extreme_ordinals(self):
501 a = self.theclass.min
502 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
503 aord = a.toordinal()
504 b = a.fromordinal(aord)
505 self.assertEqual(a, b)
506
507 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
508
509 b = a + timedelta(days=1)
510 self.assertEqual(b.toordinal(), aord + 1)
511 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
512
513 a = self.theclass.max
514 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
515 aord = a.toordinal()
516 b = a.fromordinal(aord)
517 self.assertEqual(a, b)
518
519 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
520
521 b = a - timedelta(days=1)
522 self.assertEqual(b.toordinal(), aord - 1)
523 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
524
525 def test_bad_constructor_arguments(self):
526 # bad years
527 self.theclass(MINYEAR, 1, 1) # no exception
528 self.theclass(MAXYEAR, 1, 1) # no exception
529 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
530 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
531 # bad months
532 self.theclass(2000, 1, 1) # no exception
533 self.theclass(2000, 12, 1) # no exception
534 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
535 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
536 # bad days
537 self.theclass(2000, 2, 29) # no exception
538 self.theclass(2004, 2, 29) # no exception
539 self.theclass(2400, 2, 29) # no exception
540 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
541 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
542 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
543 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
544 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
545 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
546
547 def test_hash_equality(self):
548 d = self.theclass(2000, 12, 31)
549 # same thing
550 e = self.theclass(2000, 12, 31)
551 self.assertEqual(d, e)
552 self.assertEqual(hash(d), hash(e))
553
554 dic = {d: 1}
555 dic[e] = 2
556 self.assertEqual(len(dic), 1)
557 self.assertEqual(dic[d], 2)
558 self.assertEqual(dic[e], 2)
559
560 d = self.theclass(2001, 1, 1)
561 # same thing
562 e = self.theclass(2001, 1, 1)
563 self.assertEqual(d, e)
564 self.assertEqual(hash(d), hash(e))
565
566 dic = {d: 1}
567 dic[e] = 2
568 self.assertEqual(len(dic), 1)
569 self.assertEqual(dic[d], 2)
570 self.assertEqual(dic[e], 2)
571
572 def test_computations(self):
573 a = self.theclass(2002, 1, 31)
574 b = self.theclass(1956, 1, 31)
575
576 diff = a-b
577 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
578 self.assertEqual(diff.seconds, 0)
579 self.assertEqual(diff.microseconds, 0)
580
581 day = timedelta(1)
582 week = timedelta(7)
583 a = self.theclass(2002, 3, 2)
584 self.assertEqual(a + day, self.theclass(2002, 3, 3))
585 self.assertEqual(day + a, self.theclass(2002, 3, 3))
586 self.assertEqual(a - day, self.theclass(2002, 3, 1))
587 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
588 self.assertEqual(a + week, self.theclass(2002, 3, 9))
589 self.assertEqual(a - week, self.theclass(2002, 2, 23))
590 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
591 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
592 self.assertEqual((a + week) - a, week)
593 self.assertEqual((a + day) - a, day)
594 self.assertEqual((a - week) - a, -week)
595 self.assertEqual((a - day) - a, -day)
596 self.assertEqual(a - (a + week), -week)
597 self.assertEqual(a - (a + day), -day)
598 self.assertEqual(a - (a - week), week)
599 self.assertEqual(a - (a - day), day)
600
601 # Add/sub ints, longs, floats should be illegal
602 for i in 1, 1L, 1.0:
603 self.assertRaises(TypeError, lambda: a+i)
604 self.assertRaises(TypeError, lambda: a-i)
605 self.assertRaises(TypeError, lambda: i+a)
606 self.assertRaises(TypeError, lambda: i-a)
607
608 # delta - date is senseless.
609 self.assertRaises(TypeError, lambda: day - a)
610 # mixing date and (delta or date) via * or // is senseless
611 self.assertRaises(TypeError, lambda: day * a)
612 self.assertRaises(TypeError, lambda: a * day)
613 self.assertRaises(TypeError, lambda: day // a)
614 self.assertRaises(TypeError, lambda: a // day)
615 self.assertRaises(TypeError, lambda: a * a)
616 self.assertRaises(TypeError, lambda: a // a)
617 # date + date is senseless
618 self.assertRaises(TypeError, lambda: a + a)
619
620 def test_overflow(self):
621 tiny = self.theclass.resolution
622
623 dt = self.theclass.min + tiny
624 dt -= tiny # no problem
625 self.assertRaises(OverflowError, dt.__sub__, tiny)
626 self.assertRaises(OverflowError, dt.__add__, -tiny)
627
628 dt = self.theclass.max - tiny
629 dt += tiny # no problem
630 self.assertRaises(OverflowError, dt.__add__, tiny)
631 self.assertRaises(OverflowError, dt.__sub__, -tiny)
632
633 def test_fromtimestamp(self):
634 import time
635
636 # Try an arbitrary fixed value.
637 year, month, day = 1999, 9, 19
638 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
639 d = self.theclass.fromtimestamp(ts)
640 self.assertEqual(d.year, year)
641 self.assertEqual(d.month, month)
642 self.assertEqual(d.day, day)
643
644 def test_today(self):
645 import time
646
647 # We claim that today() is like fromtimestamp(time.time()), so
648 # prove it.
649 for dummy in range(3):
650 today = self.theclass.today()
651 ts = time.time()
652 todayagain = self.theclass.fromtimestamp(ts)
653 if today == todayagain:
654 break
655 # There are several legit reasons that could fail:
656 # 1. It recently became midnight, between the today() and the
657 # time() calls.
658 # 2. The platform time() has such fine resolution that we'll
659 # never get the same value twice.
660 # 3. The platform time() has poor resolution, and we just
661 # happened to call today() right before a resolution quantum
662 # boundary.
663 # 4. The system clock got fiddled between calls.
664 # In any case, wait a little while and try again.
665 time.sleep(0.1)
666
667 # It worked or it didn't. If it didn't, assume it's reason #2, and
668 # let the test pass if they're within half a second of each other.
669 self.failUnless(today == todayagain or
670 abs(todayagain - today) < timedelta(seconds=0.5))
671
672 def test_weekday(self):
673 for i in range(7):
674 # March 4, 2002 is a Monday
675 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
676 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
677 # January 2, 1956 is a Monday
678 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
679 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
680
681 def test_isocalendar(self):
682 # Check examples from
683 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
684 for i in range(7):
685 d = self.theclass(2003, 12, 22+i)
686 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
687 d = self.theclass(2003, 12, 29) + timedelta(i)
688 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
689 d = self.theclass(2004, 1, 5+i)
690 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
691 d = self.theclass(2009, 12, 21+i)
692 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
693 d = self.theclass(2009, 12, 28) + timedelta(i)
694 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
695 d = self.theclass(2010, 1, 4+i)
696 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
697
698 def test_iso_long_years(self):
699 # Calculate long ISO years and compare to table from
700 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
701 ISO_LONG_YEARS_TABLE = """
702 4 32 60 88
703 9 37 65 93
704 15 43 71 99
705 20 48 76
706 26 54 82
707
708 105 133 161 189
709 111 139 167 195
710 116 144 172
711 122 150 178
712 128 156 184
713
714 201 229 257 285
715 207 235 263 291
716 212 240 268 296
717 218 246 274
718 224 252 280
719
720 303 331 359 387
721 308 336 364 392
722 314 342 370 398
723 320 348 376
724 325 353 381
725 """
726 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
727 iso_long_years.sort()
728 L = []
729 for i in range(400):
730 d = self.theclass(2000+i, 12, 31)
731 d1 = self.theclass(1600+i, 12, 31)
732 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
733 if d.isocalendar()[1] == 53:
734 L.append(i)
735 self.assertEqual(L, iso_long_years)
736
737 def test_isoformat(self):
738 t = self.theclass(2, 3, 2)
739 self.assertEqual(t.isoformat(), "0002-03-02")
740
741 def test_ctime(self):
742 t = self.theclass(2002, 3, 2)
743 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
744
745 def test_strftime(self):
746 t = self.theclass(2005, 3, 2)
747 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
748
749 self.assertRaises(TypeError, t.strftime) # needs an arg
750 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
751 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
752
753 # A naive object replaces %z and %Z w/ empty strings.
754 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
755
756 def test_resolution_info(self):
757 self.assert_(isinstance(self.theclass.min, self.theclass))
758 self.assert_(isinstance(self.theclass.max, self.theclass))
759 self.assert_(isinstance(self.theclass.resolution, timedelta))
760 self.assert_(self.theclass.max > self.theclass.min)
761
762 def test_extreme_timedelta(self):
763 big = self.theclass.max - self.theclass.min
764 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
765 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
766 # n == 315537897599999999 ~= 2**58.13
767 justasbig = timedelta(0, 0, n)
768 self.assertEqual(big, justasbig)
769 self.assertEqual(self.theclass.min + big, self.theclass.max)
770 self.assertEqual(self.theclass.max - big, self.theclass.min)
771
772 def test_timetuple(self):
773 for i in range(7):
774 # January 2, 1956 is a Monday (0)
775 d = self.theclass(1956, 1, 2+i)
776 t = d.timetuple()
777 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
778 # February 1, 1956 is a Wednesday (2)
779 d = self.theclass(1956, 2, 1+i)
780 t = d.timetuple()
781 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
782 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
783 # of the year.
784 d = self.theclass(1956, 3, 1+i)
785 t = d.timetuple()
786 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
787 self.assertEqual(t.tm_year, 1956)
788 self.assertEqual(t.tm_mon, 3)
789 self.assertEqual(t.tm_mday, 1+i)
790 self.assertEqual(t.tm_hour, 0)
791 self.assertEqual(t.tm_min, 0)
792 self.assertEqual(t.tm_sec, 0)
793 self.assertEqual(t.tm_wday, (3+i)%7)
794 self.assertEqual(t.tm_yday, 61+i)
795 self.assertEqual(t.tm_isdst, -1)
796
797 def test_pickling(self):
798 import pickle, cPickle
799 args = 6, 7, 23
800 orig = self.theclass(*args)
801 state = orig.__getstate__()
802 self.assertEqual(state, '\x00\x06\x07\x17')
803 derived = self.theclass(1, 1, 1)
804 derived.__setstate__(state)
805 self.assertEqual(orig, derived)
806 for pickler in pickle, cPickle:
807 for binary in 0, 1:
808 green = pickler.dumps(orig, binary)
809 derived = pickler.loads(green)
810 self.assertEqual(orig, derived)
811
812 def test_compare(self):
813 t1 = self.theclass(2, 3, 4)
814 t2 = self.theclass(2, 3, 4)
815 self.failUnless(t1 == t2)
816 self.failUnless(t1 <= t2)
817 self.failUnless(t1 >= t2)
818 self.failUnless(not t1 != t2)
819 self.failUnless(not t1 < t2)
820 self.failUnless(not t1 > t2)
821 self.assertEqual(cmp(t1, t2), 0)
822 self.assertEqual(cmp(t2, t1), 0)
823
824 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
825 t2 = self.theclass(*args) # this is larger than t1
826 self.failUnless(t1 < t2)
827 self.failUnless(t2 > t1)
828 self.failUnless(t1 <= t2)
829 self.failUnless(t2 >= t1)
830 self.failUnless(t1 != t2)
831 self.failUnless(t2 != t1)
832 self.failUnless(not t1 == t2)
833 self.failUnless(not t2 == t1)
834 self.failUnless(not t1 > t2)
835 self.failUnless(not t2 < t1)
836 self.failUnless(not t1 >= t2)
837 self.failUnless(not t2 <= t1)
838 self.assertEqual(cmp(t1, t2), -1)
839 self.assertEqual(cmp(t2, t1), 1)
840
841 for badarg in 10, 10L, 34.5, "abc", {}, [], ():
842 self.assertRaises(TypeError, lambda: t1 == badarg)
843 self.assertRaises(TypeError, lambda: t1 != badarg)
844 self.assertRaises(TypeError, lambda: t1 <= badarg)
845 self.assertRaises(TypeError, lambda: t1 < badarg)
846 self.assertRaises(TypeError, lambda: t1 > badarg)
847 self.assertRaises(TypeError, lambda: t1 >= badarg)
848 self.assertRaises(TypeError, lambda: badarg == t1)
849 self.assertRaises(TypeError, lambda: badarg != t1)
850 self.assertRaises(TypeError, lambda: badarg <= t1)
851 self.assertRaises(TypeError, lambda: badarg < t1)
852 self.assertRaises(TypeError, lambda: badarg > t1)
853 self.assertRaises(TypeError, lambda: badarg >= t1)
854
855 def test_bool(self):
856 # All dates are considered true.
857 self.failUnless(self.theclass.min)
858 self.failUnless(self.theclass.max)
859
860#############################################################################
861# datetime tests
862
863class TestDateTime(TestDate):
864
865 theclass = datetime
866
867 def test_basic_attributes(self):
868 dt = self.theclass(2002, 3, 1, 12, 0)
869 self.assertEqual(dt.year, 2002)
870 self.assertEqual(dt.month, 3)
871 self.assertEqual(dt.day, 1)
872 self.assertEqual(dt.hour, 12)
873 self.assertEqual(dt.minute, 0)
874 self.assertEqual(dt.second, 0)
875 self.assertEqual(dt.microsecond, 0)
876
877 def test_basic_attributes_nonzero(self):
878 # Make sure all attributes are non-zero so bugs in
879 # bit-shifting access show up.
880 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
881 self.assertEqual(dt.year, 2002)
882 self.assertEqual(dt.month, 3)
883 self.assertEqual(dt.day, 1)
884 self.assertEqual(dt.hour, 12)
885 self.assertEqual(dt.minute, 59)
886 self.assertEqual(dt.second, 59)
887 self.assertEqual(dt.microsecond, 8000)
888
889 def test_roundtrip(self):
890 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
891 self.theclass.now()):
892 # Verify dt -> string -> datetime identity.
893 s = repr(dt)
894 self.failUnless(s.startswith('datetime.'))
895 s = s[9:]
896 dt2 = eval(s)
897 self.assertEqual(dt, dt2)
898
899 # Verify identity via reconstructing from pieces.
900 dt2 = self.theclass(dt.year, dt.month, dt.day,
901 dt.hour, dt.minute, dt.second,
902 dt.microsecond)
903 self.assertEqual(dt, dt2)
904
905 def test_isoformat(self):
906 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
907 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
908 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
909 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
910 # str is ISO format with the separator forced to a blank.
911 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
912
913 t = self.theclass(2, 3, 2)
914 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
915 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
916 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
917 # str is ISO format with the separator forced to a blank.
918 self.assertEqual(str(t), "0002-03-02 00:00:00")
919
920 def test_more_ctime(self):
921 # Test fields that TestDate doesn't touch.
922 import time
923
924 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
925 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
926 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
927 # out. The difference is that t.ctime() produces " 2" for the day,
928 # but platform ctime() produces "02" for the day. According to
929 # C99, t.ctime() is correct here.
930 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
931
932 # So test a case where that difference doesn't matter.
933 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
934 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
935
936 def test_tz_independent_comparing(self):
937 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
938 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
939 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
940 self.assertEqual(dt1, dt3)
941 self.assert_(dt2 > dt3)
942
943 # Make sure comparison doesn't forget microseconds, and isn't done
944 # via comparing a float timestamp (an IEEE double doesn't have enough
945 # precision to span microsecond resolution across years 1 thru 9999,
946 # so comparing via timestamp necessarily calls some distinct values
947 # equal).
948 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
949 us = timedelta(microseconds=1)
950 dt2 = dt1 + us
951 self.assertEqual(dt2 - dt1, us)
952 self.assert_(dt1 < dt2)
953
954 def test_bad_constructor_arguments(self):
955 # bad years
956 self.theclass(MINYEAR, 1, 1) # no exception
957 self.theclass(MAXYEAR, 1, 1) # no exception
958 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
959 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
960 # bad months
961 self.theclass(2000, 1, 1) # no exception
962 self.theclass(2000, 12, 1) # no exception
963 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
964 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
965 # bad days
966 self.theclass(2000, 2, 29) # no exception
967 self.theclass(2004, 2, 29) # no exception
968 self.theclass(2400, 2, 29) # no exception
969 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
970 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
971 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
972 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
973 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
974 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
975 # bad hours
976 self.theclass(2000, 1, 31, 0) # no exception
977 self.theclass(2000, 1, 31, 23) # no exception
978 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
979 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
980 # bad minutes
981 self.theclass(2000, 1, 31, 23, 0) # no exception
982 self.theclass(2000, 1, 31, 23, 59) # no exception
983 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
984 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
985 # bad seconds
986 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
987 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
988 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
989 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
990 # bad microseconds
991 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
992 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
993 self.assertRaises(ValueError, self.theclass,
994 2000, 1, 31, 23, 59, 59, -1)
995 self.assertRaises(ValueError, self.theclass,
996 2000, 1, 31, 23, 59, 59,
997 1000000)
998
999 def test_hash_equality(self):
1000 d = self.theclass(2000, 12, 31, 23, 30, 17)
1001 e = self.theclass(2000, 12, 31, 23, 30, 17)
1002 self.assertEqual(d, e)
1003 self.assertEqual(hash(d), hash(e))
1004
1005 dic = {d: 1}
1006 dic[e] = 2
1007 self.assertEqual(len(dic), 1)
1008 self.assertEqual(dic[d], 2)
1009 self.assertEqual(dic[e], 2)
1010
1011 d = self.theclass(2001, 1, 1, 0, 5, 17)
1012 e = self.theclass(2001, 1, 1, 0, 5, 17)
1013 self.assertEqual(d, e)
1014 self.assertEqual(hash(d), hash(e))
1015
1016 dic = {d: 1}
1017 dic[e] = 2
1018 self.assertEqual(len(dic), 1)
1019 self.assertEqual(dic[d], 2)
1020 self.assertEqual(dic[e], 2)
1021
1022 def test_computations(self):
1023 a = self.theclass(2002, 1, 31)
1024 b = self.theclass(1956, 1, 31)
1025 diff = a-b
1026 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1027 self.assertEqual(diff.seconds, 0)
1028 self.assertEqual(diff.microseconds, 0)
1029 a = self.theclass(2002, 3, 2, 17, 6)
1030 millisec = timedelta(0, 0, 1000)
1031 hour = timedelta(0, 3600)
1032 day = timedelta(1)
1033 week = timedelta(7)
1034 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1035 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1036 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1037 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1038 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1039 self.assertEqual(a - hour, a + -hour)
1040 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1041 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1042 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1043 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1044 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1045 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1046 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1047 self.assertEqual((a + week) - a, week)
1048 self.assertEqual((a + day) - a, day)
1049 self.assertEqual((a + hour) - a, hour)
1050 self.assertEqual((a + millisec) - a, millisec)
1051 self.assertEqual((a - week) - a, -week)
1052 self.assertEqual((a - day) - a, -day)
1053 self.assertEqual((a - hour) - a, -hour)
1054 self.assertEqual((a - millisec) - a, -millisec)
1055 self.assertEqual(a - (a + week), -week)
1056 self.assertEqual(a - (a + day), -day)
1057 self.assertEqual(a - (a + hour), -hour)
1058 self.assertEqual(a - (a + millisec), -millisec)
1059 self.assertEqual(a - (a - week), week)
1060 self.assertEqual(a - (a - day), day)
1061 self.assertEqual(a - (a - hour), hour)
1062 self.assertEqual(a - (a - millisec), millisec)
1063 self.assertEqual(a + (week + day + hour + millisec),
1064 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1065 self.assertEqual(a + (week + day + hour + millisec),
1066 (((a + week) + day) + hour) + millisec)
1067 self.assertEqual(a - (week + day + hour + millisec),
1068 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1069 self.assertEqual(a - (week + day + hour + millisec),
1070 (((a - week) - day) - hour) - millisec)
1071 # Add/sub ints, longs, floats should be illegal
1072 for i in 1, 1L, 1.0:
1073 self.assertRaises(TypeError, lambda: a+i)
1074 self.assertRaises(TypeError, lambda: a-i)
1075 self.assertRaises(TypeError, lambda: i+a)
1076 self.assertRaises(TypeError, lambda: i-a)
1077
1078 # delta - datetime is senseless.
1079 self.assertRaises(TypeError, lambda: day - a)
1080 # mixing datetime and (delta or datetime) via * or // is senseless
1081 self.assertRaises(TypeError, lambda: day * a)
1082 self.assertRaises(TypeError, lambda: a * day)
1083 self.assertRaises(TypeError, lambda: day // a)
1084 self.assertRaises(TypeError, lambda: a // day)
1085 self.assertRaises(TypeError, lambda: a * a)
1086 self.assertRaises(TypeError, lambda: a // a)
1087 # datetime + datetime is senseless
1088 self.assertRaises(TypeError, lambda: a + a)
1089
1090 def test_pickling(self):
1091 import pickle, cPickle
1092 args = 6, 7, 23, 20, 59, 1, 64**2
1093 orig = self.theclass(*args)
1094 state = orig.__getstate__()
1095 self.assertEqual(state, '\x00\x06\x07\x17\x14\x3b\x01\x00\x10\x00')
1096 derived = self.theclass(1, 1, 1)
1097 derived.__setstate__(state)
1098 self.assertEqual(orig, derived)
1099 for pickler in pickle, cPickle:
1100 for binary in 0, 1:
1101 green = pickler.dumps(orig, binary)
1102 derived = pickler.loads(green)
1103 self.assertEqual(orig, derived)
1104
1105 def test_more_compare(self):
1106 # The test_compare() inherited from TestDate covers the error cases.
1107 # We just want to test lexicographic ordering on the members datetime
1108 # has that date lacks.
1109 args = [2000, 11, 29, 20, 58, 16, 999998]
1110 t1 = self.theclass(*args)
1111 t2 = self.theclass(*args)
1112 self.failUnless(t1 == t2)
1113 self.failUnless(t1 <= t2)
1114 self.failUnless(t1 >= t2)
1115 self.failUnless(not t1 != t2)
1116 self.failUnless(not t1 < t2)
1117 self.failUnless(not t1 > t2)
1118 self.assertEqual(cmp(t1, t2), 0)
1119 self.assertEqual(cmp(t2, t1), 0)
1120
1121 for i in range(len(args)):
1122 newargs = args[:]
1123 newargs[i] = args[i] + 1
1124 t2 = self.theclass(*newargs) # this is larger than t1
1125 self.failUnless(t1 < t2)
1126 self.failUnless(t2 > t1)
1127 self.failUnless(t1 <= t2)
1128 self.failUnless(t2 >= t1)
1129 self.failUnless(t1 != t2)
1130 self.failUnless(t2 != t1)
1131 self.failUnless(not t1 == t2)
1132 self.failUnless(not t2 == t1)
1133 self.failUnless(not t1 > t2)
1134 self.failUnless(not t2 < t1)
1135 self.failUnless(not t1 >= t2)
1136 self.failUnless(not t2 <= t1)
1137 self.assertEqual(cmp(t1, t2), -1)
1138 self.assertEqual(cmp(t2, t1), 1)
1139
1140
1141 # A helper for timestamp constructor tests.
1142 def verify_field_equality(self, expected, got):
1143 self.assertEqual(expected.tm_year, got.year)
1144 self.assertEqual(expected.tm_mon, got.month)
1145 self.assertEqual(expected.tm_mday, got.day)
1146 self.assertEqual(expected.tm_hour, got.hour)
1147 self.assertEqual(expected.tm_min, got.minute)
1148 self.assertEqual(expected.tm_sec, got.second)
1149
1150 def test_fromtimestamp(self):
1151 import time
1152
1153 ts = time.time()
1154 expected = time.localtime(ts)
1155 got = self.theclass.fromtimestamp(ts)
1156 self.verify_field_equality(expected, got)
1157
1158 def test_utcfromtimestamp(self):
1159 import time
1160
1161 ts = time.time()
1162 expected = time.gmtime(ts)
1163 got = self.theclass.utcfromtimestamp(ts)
1164 self.verify_field_equality(expected, got)
1165
1166 def test_utcnow(self):
1167 import time
1168
1169 # Call it a success if utcnow() and utcfromtimestamp() are within
1170 # a second of each other.
1171 tolerance = timedelta(seconds=1)
1172 for dummy in range(3):
1173 from_now = self.theclass.utcnow()
1174 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1175 if abs(from_timestamp - from_now) <= tolerance:
1176 break
1177 # Else try again a few times.
1178 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1179
1180 def test_more_timetuple(self):
1181 # This tests fields beyond those tested by the TestDate.test_timetuple.
1182 t = self.theclass(2004, 12, 31, 6, 22, 33)
1183 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1184 self.assertEqual(t.timetuple(),
1185 (t.year, t.month, t.day,
1186 t.hour, t.minute, t.second,
1187 t.weekday(),
1188 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1189 -1))
1190 tt = t.timetuple()
1191 self.assertEqual(tt.tm_year, t.year)
1192 self.assertEqual(tt.tm_mon, t.month)
1193 self.assertEqual(tt.tm_mday, t.day)
1194 self.assertEqual(tt.tm_hour, t.hour)
1195 self.assertEqual(tt.tm_min, t.minute)
1196 self.assertEqual(tt.tm_sec, t.second)
1197 self.assertEqual(tt.tm_wday, t.weekday())
1198 self.assertEqual(tt.tm_yday, t.toordinal() -
1199 date(t.year, 1, 1).toordinal() + 1)
1200 self.assertEqual(tt.tm_isdst, -1)
1201
1202 def test_more_strftime(self):
1203 # This tests fields beyond those tested by the TestDate.test_strftime.
1204 t = self.theclass(2004, 12, 31, 6, 22, 33)
1205 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1206 "12 31 04 33 22 06 366")
1207
1208 def test_extract(self):
1209 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1210 self.assertEqual(dt.date(), date(2002, 3, 4))
1211 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1212
1213 def test_combine(self):
1214 d = date(2002, 3, 4)
1215 t = time(18, 45, 3, 1234)
1216 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1217 combine = self.theclass.combine
1218 dt = combine(d, t)
1219 self.assertEqual(dt, expected)
1220
1221 dt = combine(time=t, date=d)
1222 self.assertEqual(dt, expected)
1223
1224 self.assertEqual(d, dt.date())
1225 self.assertEqual(t, dt.time())
1226 self.assertEqual(dt, combine(dt.date(), dt.time()))
1227
1228 self.assertRaises(TypeError, combine) # need an arg
1229 self.assertRaises(TypeError, combine, d) # need two args
1230 self.assertRaises(TypeError, combine, t, d) # args reversed
1231 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1232 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1233
1234class TestTime(unittest.TestCase):
1235
1236 theclass = time
1237
1238 def test_basic_attributes(self):
1239 t = self.theclass(12, 0)
1240 self.assertEqual(t.hour, 12)
1241 self.assertEqual(t.minute, 0)
1242 self.assertEqual(t.second, 0)
1243 self.assertEqual(t.microsecond, 0)
1244
1245 def test_basic_attributes_nonzero(self):
1246 # Make sure all attributes are non-zero so bugs in
1247 # bit-shifting access show up.
1248 t = self.theclass(12, 59, 59, 8000)
1249 self.assertEqual(t.hour, 12)
1250 self.assertEqual(t.minute, 59)
1251 self.assertEqual(t.second, 59)
1252 self.assertEqual(t.microsecond, 8000)
1253
1254 def test_roundtrip(self):
1255 t = self.theclass(1, 2, 3, 4)
1256
1257 # Verify t -> string -> time identity.
1258 s = repr(t)
1259 self.failUnless(s.startswith('datetime.'))
1260 s = s[9:]
1261 t2 = eval(s)
1262 self.assertEqual(t, t2)
1263
1264 # Verify identity via reconstructing from pieces.
1265 t2 = self.theclass(t.hour, t.minute, t.second,
1266 t.microsecond)
1267 self.assertEqual(t, t2)
1268
1269 def test_comparing(self):
1270 args = [1, 2, 3, 4]
1271 t1 = self.theclass(*args)
1272 t2 = self.theclass(*args)
1273 self.failUnless(t1 == t2)
1274 self.failUnless(t1 <= t2)
1275 self.failUnless(t1 >= t2)
1276 self.failUnless(not t1 != t2)
1277 self.failUnless(not t1 < t2)
1278 self.failUnless(not t1 > t2)
1279 self.assertEqual(cmp(t1, t2), 0)
1280 self.assertEqual(cmp(t2, t1), 0)
1281
1282 for i in range(len(args)):
1283 newargs = args[:]
1284 newargs[i] = args[i] + 1
1285 t2 = self.theclass(*newargs) # this is larger than t1
1286 self.failUnless(t1 < t2)
1287 self.failUnless(t2 > t1)
1288 self.failUnless(t1 <= t2)
1289 self.failUnless(t2 >= t1)
1290 self.failUnless(t1 != t2)
1291 self.failUnless(t2 != t1)
1292 self.failUnless(not t1 == t2)
1293 self.failUnless(not t2 == t1)
1294 self.failUnless(not t1 > t2)
1295 self.failUnless(not t2 < t1)
1296 self.failUnless(not t1 >= t2)
1297 self.failUnless(not t2 <= t1)
1298 self.assertEqual(cmp(t1, t2), -1)
1299 self.assertEqual(cmp(t2, t1), 1)
1300
1301 for badarg in (10, 10L, 34.5, "abc", {}, [], (), date(1, 1, 1),
1302 datetime(1, 1, 1, 1, 1), timedelta(9)):
1303 self.assertRaises(TypeError, lambda: t1 == badarg)
1304 self.assertRaises(TypeError, lambda: t1 != badarg)
1305 self.assertRaises(TypeError, lambda: t1 <= badarg)
1306 self.assertRaises(TypeError, lambda: t1 < badarg)
1307 self.assertRaises(TypeError, lambda: t1 > badarg)
1308 self.assertRaises(TypeError, lambda: t1 >= badarg)
1309 self.assertRaises(TypeError, lambda: badarg == t1)
1310 self.assertRaises(TypeError, lambda: badarg != t1)
1311 self.assertRaises(TypeError, lambda: badarg <= t1)
1312 self.assertRaises(TypeError, lambda: badarg < t1)
1313 self.assertRaises(TypeError, lambda: badarg > t1)
1314 self.assertRaises(TypeError, lambda: badarg >= t1)
1315
1316 def test_bad_constructor_arguments(self):
1317 # bad hours
1318 self.theclass(0, 0) # no exception
1319 self.theclass(23, 0) # no exception
1320 self.assertRaises(ValueError, self.theclass, -1, 0)
1321 self.assertRaises(ValueError, self.theclass, 24, 0)
1322 # bad minutes
1323 self.theclass(23, 0) # no exception
1324 self.theclass(23, 59) # no exception
1325 self.assertRaises(ValueError, self.theclass, 23, -1)
1326 self.assertRaises(ValueError, self.theclass, 23, 60)
1327 # bad seconds
1328 self.theclass(23, 59, 0) # no exception
1329 self.theclass(23, 59, 59) # no exception
1330 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1331 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1332 # bad microseconds
1333 self.theclass(23, 59, 59, 0) # no exception
1334 self.theclass(23, 59, 59, 999999) # no exception
1335 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1336 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1337
1338 def test_hash_equality(self):
1339 d = self.theclass(23, 30, 17)
1340 e = self.theclass(23, 30, 17)
1341 self.assertEqual(d, e)
1342 self.assertEqual(hash(d), hash(e))
1343
1344 dic = {d: 1}
1345 dic[e] = 2
1346 self.assertEqual(len(dic), 1)
1347 self.assertEqual(dic[d], 2)
1348 self.assertEqual(dic[e], 2)
1349
1350 d = self.theclass(0, 5, 17)
1351 e = self.theclass(0, 5, 17)
1352 self.assertEqual(d, e)
1353 self.assertEqual(hash(d), hash(e))
1354
1355 dic = {d: 1}
1356 dic[e] = 2
1357 self.assertEqual(len(dic), 1)
1358 self.assertEqual(dic[d], 2)
1359 self.assertEqual(dic[e], 2)
1360
1361 def test_isoformat(self):
1362 t = self.theclass(4, 5, 1, 123)
1363 self.assertEqual(t.isoformat(), "04:05:01.000123")
1364 self.assertEqual(t.isoformat(), str(t))
1365
1366 t = self.theclass()
1367 self.assertEqual(t.isoformat(), "00:00:00")
1368 self.assertEqual(t.isoformat(), str(t))
1369
1370 t = self.theclass(microsecond=1)
1371 self.assertEqual(t.isoformat(), "00:00:00.000001")
1372 self.assertEqual(t.isoformat(), str(t))
1373
1374 t = self.theclass(microsecond=10)
1375 self.assertEqual(t.isoformat(), "00:00:00.000010")
1376 self.assertEqual(t.isoformat(), str(t))
1377
1378 t = self.theclass(microsecond=100)
1379 self.assertEqual(t.isoformat(), "00:00:00.000100")
1380 self.assertEqual(t.isoformat(), str(t))
1381
1382 t = self.theclass(microsecond=1000)
1383 self.assertEqual(t.isoformat(), "00:00:00.001000")
1384 self.assertEqual(t.isoformat(), str(t))
1385
1386 t = self.theclass(microsecond=10000)
1387 self.assertEqual(t.isoformat(), "00:00:00.010000")
1388 self.assertEqual(t.isoformat(), str(t))
1389
1390 t = self.theclass(microsecond=100000)
1391 self.assertEqual(t.isoformat(), "00:00:00.100000")
1392 self.assertEqual(t.isoformat(), str(t))
1393
1394 def test_strftime(self):
1395 t = self.theclass(1, 2, 3, 4)
1396 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1397 # A naive object replaces %z and %Z with empty strings.
1398 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1399
1400 def test_str(self):
1401 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1402 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1403 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1404 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1405 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1406
1407 def test_repr(self):
1408 name = 'datetime.' + self.theclass.__name__
1409 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1410 "%s(1, 2, 3, 4)" % name)
1411 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1412 "%s(10, 2, 3, 4000)" % name)
1413 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1414 "%s(0, 2, 3, 400000)" % name)
1415 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1416 "%s(12, 2, 3)" % name)
1417 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1418 "%s(23, 15)" % name)
1419
1420 def test_resolution_info(self):
1421 self.assert_(isinstance(self.theclass.min, self.theclass))
1422 self.assert_(isinstance(self.theclass.max, self.theclass))
1423 self.assert_(isinstance(self.theclass.resolution, timedelta))
1424 self.assert_(self.theclass.max > self.theclass.min)
1425
1426 def test_pickling(self):
1427 import pickle, cPickle
1428 args = 20, 59, 16, 64**2
1429 orig = self.theclass(*args)
1430 state = orig.__getstate__()
1431 self.assertEqual(state, '\x14\x3b\x10\x00\x10\x00')
1432 derived = self.theclass()
1433 derived.__setstate__(state)
1434 self.assertEqual(orig, derived)
1435 for pickler in pickle, cPickle:
1436 for binary in 0, 1:
1437 green = pickler.dumps(orig, binary)
1438 derived = pickler.loads(green)
1439 self.assertEqual(orig, derived)
1440
1441 def test_bool(self):
1442 cls = self.theclass
1443 self.failUnless(cls(1))
1444 self.failUnless(cls(0, 1))
1445 self.failUnless(cls(0, 0, 1))
1446 self.failUnless(cls(0, 0, 0, 1))
1447 self.failUnless(not cls(0))
1448 self.failUnless(not cls())
1449
1450
1451class TestTimeTZ(TestTime):
1452
1453 theclass = timetz
1454
1455 def test_empty(self):
1456 t = self.theclass()
1457 self.assertEqual(t.hour, 0)
1458 self.assertEqual(t.minute, 0)
1459 self.assertEqual(t.second, 0)
1460 self.assertEqual(t.microsecond, 0)
1461 self.failUnless(t.tzinfo is None)
1462
1463 def test_bad_tzinfo_classes(self):
1464 tz = self.theclass
1465 self.assertRaises(TypeError, tz, tzinfo=12)
1466
1467 class NiceTry(object):
1468 def __init__(self): pass
1469 def utcoffset(self, dt): pass
1470 self.assertRaises(TypeError, tz, tzinfo=NiceTry)
1471
1472 class BetterTry(tzinfo):
1473 def __init__(self): pass
1474 def utcoffset(self, dt): pass
1475 b = BetterTry()
1476 t = tz(tzinfo=b)
1477 self.failUnless(t.tzinfo is b)
1478
1479 def test_zones(self):
1480 est = FixedOffset(-300, "EST", 1)
1481 utc = FixedOffset(0, "UTC", -2)
1482 met = FixedOffset(60, "MET", 3)
1483 t1 = timetz( 7, 47, tzinfo=est)
1484 t2 = timetz(12, 47, tzinfo=utc)
1485 t3 = timetz(13, 47, tzinfo=met)
1486 t4 = timetz(microsecond=40)
1487 t5 = timetz(microsecond=40, tzinfo=utc)
1488
1489 self.assertEqual(t1.tzinfo, est)
1490 self.assertEqual(t2.tzinfo, utc)
1491 self.assertEqual(t3.tzinfo, met)
1492 self.failUnless(t4.tzinfo is None)
1493 self.assertEqual(t5.tzinfo, utc)
1494
1495 self.assertEqual(t1.utcoffset(), -300)
1496 self.assertEqual(t2.utcoffset(), 0)
1497 self.assertEqual(t3.utcoffset(), 60)
1498 self.failUnless(t4.utcoffset() is None)
1499 self.assertRaises(TypeError, t1.utcoffset, "no args")
1500
1501 self.assertEqual(t1.tzname(), "EST")
1502 self.assertEqual(t2.tzname(), "UTC")
1503 self.assertEqual(t3.tzname(), "MET")
1504 self.failUnless(t4.tzname() is None)
1505 self.assertRaises(TypeError, t1.tzname, "no args")
1506
1507 self.assertEqual(t1.dst(), 1)
1508 self.assertEqual(t2.dst(), -2)
1509 self.assertEqual(t3.dst(), 3)
1510 self.failUnless(t4.dst() is None)
1511 self.assertRaises(TypeError, t1.dst, "no args")
1512
1513 self.assertEqual(hash(t1), hash(t2))
1514 self.assertEqual(hash(t1), hash(t3))
1515 self.assertEqual(hash(t2), hash(t3))
1516
1517 self.assertEqual(t1, t2)
1518 self.assertEqual(t1, t3)
1519 self.assertEqual(t2, t3)
1520 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
1521 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
1522 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
1523
1524 self.assertEqual(str(t1), "07:47:00-05:00")
1525 self.assertEqual(str(t2), "12:47:00+00:00")
1526 self.assertEqual(str(t3), "13:47:00+01:00")
1527 self.assertEqual(str(t4), "00:00:00.000040")
1528 self.assertEqual(str(t5), "00:00:00.000040+00:00")
1529
1530 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
1531 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
1532 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
1533 self.assertEqual(t4.isoformat(), "00:00:00.000040")
1534 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
1535
1536 d = 'datetime.timetz'
1537 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
1538 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
1539 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
1540 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
1541 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
1542
1543 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
1544 "07:47:00 %Z=EST %z=-0500")
1545 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
1546 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
1547
1548 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
1549 t1 = timetz(23, 59, tzinfo=yuck)
1550 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
1551 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
1552
1553
1554 def test_hash_edge_cases(self):
1555 # Offsets that overflow a basic time.
1556 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
1557 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
1558 self.assertEqual(hash(t1), hash(t2))
1559
1560 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
1561 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
1562 self.assertEqual(hash(t1), hash(t2))
1563
1564 def test_utc_offset_out_of_bounds(self):
1565 class Edgy(tzinfo):
1566 def __init__(self, offset):
1567 self.offset = offset
1568 def utcoffset(self, dt):
1569 return self.offset
1570
1571 for offset, legit in ((-1440, False),
1572 (-1439, True),
1573 (1439, True),
1574 (1440, False)):
1575 t = timetz(1, 2, 3, tzinfo=Edgy(offset))
1576 if legit:
1577 aofs = abs(offset)
1578 h, m = divmod(aofs, 60)
1579 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
1580 self.assertEqual(str(t), "01:02:03" + tag)
1581 else:
1582 self.assertRaises(ValueError, str, t)
1583
1584 def test_pickling(self):
1585 import pickle, cPickle
1586
1587 # Try one without a tzinfo.
1588 args = 20, 59, 16, 64**2
1589 orig = self.theclass(*args)
1590 state = orig.__getstate__()
1591 self.assertEqual(state, ('\x14\x3b\x10\x00\x10\x00',))
1592 derived = self.theclass()
1593 derived.__setstate__(state)
1594 self.assertEqual(orig, derived)
1595 for pickler in pickle, cPickle:
1596 for binary in 0, 1:
1597 green = pickler.dumps(orig, binary)
1598 derived = pickler.loads(green)
1599 self.assertEqual(orig, derived)
1600
1601 # Try one with a tzinfo.
1602 tinfo = FixedOffset(-300, 'cookie')
1603 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
1604 state = orig.__getstate__()
1605 derived = self.theclass()
1606 derived.__setstate__(state)
1607 self.assertEqual(orig, derived)
1608 self.failUnless(isinstance(derived.tzinfo, FixedOffset))
1609 self.assertEqual(derived.utcoffset(), -300)
1610 self.assertEqual(derived.tzname(), 'cookie')
1611
1612 for pickler in pickle, cPickle:
1613 for binary in 0, 1:
1614 green = pickler.dumps(orig, binary)
1615 derived = pickler.loads(green)
1616 self.assertEqual(orig, derived)
1617 self.failUnless(isinstance(derived.tzinfo, FixedOffset))
1618 self.assertEqual(derived.utcoffset(), -300)
1619 self.assertEqual(derived.tzname(), 'cookie')
1620
1621 def test_more_bool(self):
1622 # Test cases with non-None tzinfo.
1623 cls = self.theclass
1624
1625 t = cls(0, tzinfo=FixedOffset(-300, ""))
1626 self.failUnless(t)
1627
1628 t = cls(5, tzinfo=FixedOffset(-300, ""))
1629 self.failUnless(t)
1630
1631 t = cls(5, tzinfo=FixedOffset(300, ""))
1632 self.failUnless(not t)
1633
1634 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
1635 self.failUnless(not t)
1636
1637 # Mostly ensuring this doesn't overflow internally.
1638 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
1639 self.failUnless(t)
1640
1641 # But this should yield a value error -- the utcoffset is bogus.
1642 t = cls(0, tzinfo=FixedOffset(24*60, ""))
1643 self.assertRaises(ValueError, lambda: bool(t))
1644
1645 # Likewise.
1646 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
1647 self.assertRaises(ValueError, lambda: bool(t))
1648
1649class TestDateTimeTZ(TestDateTime):
1650
1651 theclass = datetimetz
1652
1653 def test_trivial(self):
1654 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
1655 self.assertEqual(dt.year, 1)
1656 self.assertEqual(dt.month, 2)
1657 self.assertEqual(dt.day, 3)
1658 self.assertEqual(dt.hour, 4)
1659 self.assertEqual(dt.minute, 5)
1660 self.assertEqual(dt.second, 6)
1661 self.assertEqual(dt.microsecond, 7)
1662 self.assertEqual(dt.tzinfo, None)
1663
1664 def test_even_more_compare(self):
1665 # The test_compare() and test_more_compare() inherited from TestDate
1666 # and TestDateTime covered non-tzinfo cases.
1667
1668 # Smallest possible after UTC adjustment.
1669 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
1670 # Largest possible after UTC adjustment.
1671 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
1672 tzinfo=FixedOffset(-1439, ""))
1673
1674 # Make sure those compare correctly, and w/o overflow.
1675 self.failUnless(t1 < t2)
1676 self.failUnless(t1 != t2)
1677 self.failUnless(t2 > t1)
1678
1679 self.failUnless(t1 == t1)
1680 self.failUnless(t2 == t2)
1681
1682 # Equal afer adjustment.
1683 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
1684 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
1685 self.assertEqual(t1, t2)
1686
1687 # Change t1 not to subtract a minute, and t1 should be larger.
1688 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
1689 self.failUnless(t1 > t2)
1690
1691 # Change t1 to subtract 2 minutes, and t1 should be smaller.
1692 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
1693 self.failUnless(t1 < t2)
1694
1695 # Back to the original t1, but make seconds resolve it.
1696 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
1697 second=1)
1698 self.failUnless(t1 > t2)
1699
1700 # Likewise, but make microseconds resolve it.
1701 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
1702 microsecond=1)
1703 self.failUnless(t1 > t2)
1704
1705 # Make t2 naive and it should fail.
1706 t2 = self.theclass.min
1707 self.assertRaises(TypeError, lambda: t1 == t2)
1708 self.assertEqual(t2, t2)
1709
1710 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
1711 class Naive(tzinfo):
1712 def utcoffset(self, dt): return None
1713 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
1714 self.assertRaises(TypeError, lambda: t1 == t2)
1715 self.assertEqual(t2, t2)
1716
1717 # OTOH, it's OK to compare two of these mixing the two ways of being
1718 # naive.
1719 t1 = self.theclass(5, 6, 7)
1720 self.assertEqual(t1, t2)
1721
1722 # Try a bogus uctoffset.
1723 class Bogus(tzinfo):
1724 def utcoffset(self, dt): return 1440 # out of bounds
1725 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
1726 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
1727 self.assertRaises(ValueError, lambda: t1 == t1)
1728
1729 def test_bad_tzinfo_classes(self):
1730 tz = self.theclass
1731 self.assertRaises(TypeError, tz, 1, 2, 3, tzinfo=12)
1732
1733 class NiceTry(object):
1734 def __init__(self): pass
1735 def utcoffset(self, dt): pass
1736 self.assertRaises(TypeError, tz, 1, 2, 3, tzinfo=NiceTry)
1737
1738 class BetterTry(tzinfo):
1739 def __init__(self): pass
1740 def utcoffset(self, dt): pass
1741 b = BetterTry()
1742 t = tz(1, 2, 3, tzinfo=b)
1743 self.failUnless(t.tzinfo is b)
1744
1745 def test_pickling(self):
1746 import pickle, cPickle
1747
1748 # Try one without a tzinfo.
1749 args = 6, 7, 23, 20, 59, 1, 64**2
1750 orig = self.theclass(*args)
1751 state = orig.__getstate__()
1752 self.assertEqual(state, ('\x00\x06\x07\x17\x14\x3b\x01\x00\x10\x00',))
1753 derived = self.theclass(1, 1, 1)
1754 derived.__setstate__(state)
1755 self.assertEqual(orig, derived)
1756 for pickler in pickle, cPickle:
1757 for binary in 0, 1:
1758 green = pickler.dumps(orig, binary)
1759 derived = pickler.loads(green)
1760 self.assertEqual(orig, derived)
1761
1762 # Try one with a tzinfo.
1763 tinfo = FixedOffset(-300, 'cookie')
1764 orig = self.theclass(*args, **{'tzinfo': tinfo})
1765 state = orig.__getstate__()
1766 derived = self.theclass(1, 1, 1)
1767 derived.__setstate__(state)
1768 self.assertEqual(orig, derived)
1769 self.failUnless(isinstance(derived.tzinfo, FixedOffset))
1770 self.assertEqual(derived.utcoffset(), -300)
1771 self.assertEqual(derived.tzname(), 'cookie')
1772
1773 for pickler in pickle, cPickle:
1774 for binary in 0, 1:
1775 green = pickler.dumps(orig, binary)
1776 derived = pickler.loads(green)
1777 self.assertEqual(orig, derived)
1778 self.failUnless(isinstance(derived.tzinfo, FixedOffset))
1779 self.assertEqual(derived.utcoffset(), -300)
1780 self.assertEqual(derived.tzname(), 'cookie')
1781
1782 def test_extreme_hashes(self):
1783 # If an attempt is made to hash these via subtracting the offset
1784 # then hashing a datetime object, OverflowError results. The
1785 # Python implementation used to blow up here.
1786 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
1787 hash(t)
1788 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
1789 tzinfo=FixedOffset(-1439, ""))
1790 hash(t)
1791
1792 # OTOH, an OOB offset should blow up.
1793 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
1794 self.assertRaises(ValueError, hash, t)
1795
1796 def test_zones(self):
1797 est = FixedOffset(-300, "EST")
1798 utc = FixedOffset(0, "UTC")
1799 met = FixedOffset(60, "MET")
1800 t1 = datetimetz(2002, 3, 19, 7, 47, tzinfo=est)
1801 t2 = datetimetz(2002, 3, 19, 12, 47, tzinfo=utc)
1802 t3 = datetimetz(2002, 3, 19, 13, 47, tzinfo=met)
1803 self.assertEqual(t1.tzinfo, est)
1804 self.assertEqual(t2.tzinfo, utc)
1805 self.assertEqual(t3.tzinfo, met)
1806 self.assertEqual(t1.utcoffset(), -300)
1807 self.assertEqual(t2.utcoffset(), 0)
1808 self.assertEqual(t3.utcoffset(), 60)
1809 self.assertEqual(t1.tzname(), "EST")
1810 self.assertEqual(t2.tzname(), "UTC")
1811 self.assertEqual(t3.tzname(), "MET")
1812 self.assertEqual(hash(t1), hash(t2))
1813 self.assertEqual(hash(t1), hash(t3))
1814 self.assertEqual(hash(t2), hash(t3))
1815 self.assertEqual(t1, t2)
1816 self.assertEqual(t1, t3)
1817 self.assertEqual(t2, t3)
1818 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
1819 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
1820 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
1821 d = 'datetime.datetimetz(2002, 3, 19, '
1822 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
1823 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
1824 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
1825
1826 def test_combine(self):
1827 met = FixedOffset(60, "MET")
1828 d = date(2002, 3, 4)
1829 tz = timetz(18, 45, 3, 1234, tzinfo=met)
1830 dt = datetimetz.combine(d, tz)
1831 self.assertEqual(dt, datetimetz(2002, 3, 4, 18, 45, 3, 1234,
1832 tzinfo=met))
1833
1834 def test_extract(self):
1835 met = FixedOffset(60, "MET")
1836 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
1837 self.assertEqual(dt.date(), date(2002, 3, 4))
1838 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1839 self.assertEqual(dt.timetz(), timetz(18, 45, 3, 1234, tzinfo=met))
1840
1841 def test_tz_aware_arithmetic(self):
1842 import random
1843
1844 now = self.theclass.now()
1845 tz55 = FixedOffset(-330, "west 5:30")
1846 timeaware = timetz(now.hour, now.minute, now.second,
1847 now.microsecond, tzinfo=tz55)
1848 nowaware = self.theclass.combine(now.date(), timeaware)
1849 self.failUnless(nowaware.tzinfo is tz55)
1850 self.assertEqual(nowaware.timetz(), timeaware)
1851
1852 # Can't mix aware and non-aware.
1853 self.assertRaises(TypeError, lambda: now - nowaware)
1854 self.assertRaises(TypeError, lambda: nowaware - now)
1855
1856 # And adding datetimetz's doesn't make sense, aware or not.
1857 self.assertRaises(TypeError, lambda: now + nowaware)
1858 self.assertRaises(TypeError, lambda: nowaware + now)
1859 self.assertRaises(TypeError, lambda: nowaware + nowaware)
1860
1861 # Subtracting should yield 0.
1862 self.assertEqual(now - now, timedelta(0))
1863 self.assertEqual(nowaware - nowaware, timedelta(0))
1864
1865 # Adding a delta should preserve tzinfo.
1866 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
1867 nowawareplus = nowaware + delta
1868 self.failUnless(nowaware.tzinfo is tz55)
1869 nowawareplus2 = delta + nowaware
1870 self.failUnless(nowawareplus2.tzinfo is tz55)
1871 self.assertEqual(nowawareplus, nowawareplus2)
1872
1873 # that - delta should be what we started with, and that - what we
1874 # started with should be delta.
1875 diff = nowawareplus - delta
1876 self.failUnless(diff.tzinfo is tz55)
1877 self.assertEqual(nowaware, diff)
1878 self.assertRaises(TypeError, lambda: delta - nowawareplus)
1879 self.assertEqual(nowawareplus - nowaware, delta)
1880
1881 # Make up a random timezone.
1882 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
1883 # Attach it to nowawareplus -- this is clumsy.
1884 nowawareplus = self.theclass.combine(nowawareplus.date(),
1885 timetz(nowawareplus.hour,
1886 nowawareplus.minute,
1887 nowawareplus.second,
1888 nowawareplus.microsecond,
1889 tzinfo=tzr))
1890 self.failUnless(nowawareplus.tzinfo is tzr)
1891 # Make sure the difference takes the timezone adjustments into account.
1892 got = nowaware - nowawareplus
1893 # Expected: (nowaware base - nowaware offset) -
1894 # (nowawareplus base - nowawareplus offset) =
1895 # (nowaware base - nowawareplus base) +
1896 # (nowawareplus offset - nowaware offset) =
1897 # -delta + nowawareplus offset - nowaware offset
1898 expected = timedelta(minutes=nowawareplus.utcoffset() -
1899 nowaware.utcoffset()) - delta
1900 self.assertEqual(got, expected)
1901
1902 # Try max possible difference.
1903 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
1904 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
1905 tzinfo=FixedOffset(-1439, "max"))
1906 maxdiff = max - min
1907 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
1908 timedelta(minutes=2*1439))
1909
1910 def test_tzinfo_now(self):
1911 meth = self.theclass.now
1912 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
1913 base = meth()
1914 # Try with and without naming the keyword.
1915 off42 = FixedOffset(42, "42")
1916 another = meth(off42)
1917 again = meth(tzinfo=off42)
1918 self.failUnless(another.tzinfo is again.tzinfo)
1919 self.assertEqual(another.utcoffset(), 42)
1920 # Bad argument with and w/o naming the keyword.
1921 self.assertRaises(TypeError, meth, 16)
1922 self.assertRaises(TypeError, meth, tzinfo=16)
1923 # Bad keyword name.
1924 self.assertRaises(TypeError, meth, tinfo=off42)
1925 # Too many args.
1926 self.assertRaises(TypeError, meth, off42, off42)
1927
1928 def test_tzinfo_fromtimestamp(self):
1929 import time
1930 meth = self.theclass.fromtimestamp
1931 ts = time.time()
1932 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
1933 base = meth(ts)
1934 # Try with and without naming the keyword.
1935 off42 = FixedOffset(42, "42")
1936 another = meth(ts, off42)
1937 again = meth(ts, tzinfo=off42)
1938 self.failUnless(another.tzinfo is again.tzinfo)
1939 self.assertEqual(another.utcoffset(), 42)
1940 # Bad argument with and w/o naming the keyword.
1941 self.assertRaises(TypeError, meth, ts, 16)
1942 self.assertRaises(TypeError, meth, ts, tzinfo=16)
1943 # Bad keyword name.
1944 self.assertRaises(TypeError, meth, ts, tinfo=off42)
1945 # Too many args.
1946 self.assertRaises(TypeError, meth, ts, off42, off42)
1947 # Too few args.
1948 self.assertRaises(TypeError, meth)
1949
1950 def test_tzinfo_utcnow(self):
1951 meth = self.theclass.utcnow
1952 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
1953 base = meth()
1954 # Try with and without naming the keyword; for whatever reason,
1955 # utcnow() doesn't accept a tzinfo argument.
1956 off42 = FixedOffset(42, "42")
1957 self.assertRaises(TypeError, meth, off42)
1958 self.assertRaises(TypeError, meth, tzinfo=off42)
1959
1960 def test_tzinfo_utcfromtimestamp(self):
1961 import time
1962 meth = self.theclass.utcfromtimestamp
1963 ts = time.time()
1964 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
1965 base = meth(ts)
1966 # Try with and without naming the keyword; for whatever reason,
1967 # utcfromtimestamp() doesn't accept a tzinfo argument.
1968 off42 = FixedOffset(42, "42")
1969 self.assertRaises(TypeError, meth, ts, off42)
1970 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
1971
1972 def test_tzinfo_timetuple(self):
1973 # TestDateTime tested most of this. datetimetz adds a twist to the
1974 # DST flag.
1975 class DST(tzinfo):
1976 def __init__(self, dstvalue):
1977 self.dstvalue = dstvalue
1978 def dst(self, dt):
1979 return self.dstvalue
1980
1981 cls = self.theclass
1982 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
1983 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
1984 t = d.timetuple()
1985 self.assertEqual(1, t.tm_year)
1986 self.assertEqual(1, t.tm_mon)
1987 self.assertEqual(1, t.tm_mday)
1988 self.assertEqual(10, t.tm_hour)
1989 self.assertEqual(20, t.tm_min)
1990 self.assertEqual(30, t.tm_sec)
1991 self.assertEqual(0, t.tm_wday)
1992 self.assertEqual(1, t.tm_yday)
1993 self.assertEqual(flag, t.tm_isdst)
1994
1995 # dst() returns wrong type.
1996 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
1997
1998 # dst() at the edge.
1999 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2000 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2001
2002 # dst() out of range.
2003 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2004 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2005
2006 def test_utctimetuple(self):
2007 class DST(tzinfo):
2008 def __init__(self, dstvalue):
2009 self.dstvalue = dstvalue
2010 def dst(self, dt):
2011 return self.dstvalue
2012
2013 cls = self.theclass
2014 # This can't work: DST didn't implement utcoffset.
2015 self.assertRaises(NotImplementedError,
2016 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2017
2018 class UOFS(DST):
2019 def __init__(self, uofs, dofs=None):
2020 DST.__init__(self, dofs)
2021 self.uofs = uofs
2022 def utcoffset(self, dt):
2023 return self.uofs
2024
2025 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2026 # in effect for a UTC time.
2027 for dstvalue in -33, 33, 0, None:
2028 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2029 t = d.utctimetuple()
2030 self.assertEqual(d.year, t.tm_year)
2031 self.assertEqual(d.month, t.tm_mon)
2032 self.assertEqual(d.day, t.tm_mday)
2033 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2034 self.assertEqual(13, t.tm_min)
2035 self.assertEqual(d.second, t.tm_sec)
2036 self.assertEqual(d.weekday(), t.tm_wday)
2037 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2038 t.tm_yday)
2039 self.assertEqual(0, t.tm_isdst)
2040
2041 # At the edges, UTC adjustment can normalize into years out-of-range
2042 # for a datetime object. Ensure that a correct timetuple is
2043 # created anyway.
2044 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2045 # That goes back 1 minute less than a full day.
2046 t = tiny.utctimetuple()
2047 self.assertEqual(t.tm_year, MINYEAR-1)
2048 self.assertEqual(t.tm_mon, 12)
2049 self.assertEqual(t.tm_mday, 31)
2050 self.assertEqual(t.tm_hour, 0)
2051 self.assertEqual(t.tm_min, 1)
2052 self.assertEqual(t.tm_sec, 37)
2053 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2054 self.assertEqual(t.tm_isdst, 0)
2055
2056 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2057 # That goes forward 1 minute less than a full day.
2058 t = huge.utctimetuple()
2059 self.assertEqual(t.tm_year, MAXYEAR+1)
2060 self.assertEqual(t.tm_mon, 1)
2061 self.assertEqual(t.tm_mday, 1)
2062 self.assertEqual(t.tm_hour, 23)
2063 self.assertEqual(t.tm_min, 58)
2064 self.assertEqual(t.tm_sec, 37)
2065 self.assertEqual(t.tm_yday, 1)
2066 self.assertEqual(t.tm_isdst, 0)
2067
2068 def test_tzinfo_isoformat(self):
2069 zero = FixedOffset(0, "+00:00")
2070 plus = FixedOffset(220, "+03:40")
2071 minus = FixedOffset(-231, "-03:51")
2072 unknown = FixedOffset(None, "")
2073
2074 cls = self.theclass
2075 datestr = '0001-02-03'
2076 for ofs in None, zero, plus, minus, unknown:
2077 for us in 0, 987001:
2078 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2079 timestr = '04:05:59' + (us and '.987001' or '')
2080 ofsstr = ofs is not None and d.tzname() or ''
2081 tailstr = timestr + ofsstr
2082 iso = d.isoformat()
2083 self.assertEqual(iso, datestr + 'T' + tailstr)
2084 self.assertEqual(iso, d.isoformat('T'))
2085 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2086 self.assertEqual(str(d), datestr + ' ' + tailstr)
2087
2088
2089def suite():
2090 allsuites = [unittest.makeSuite(klass, 'test')
2091 for klass in (TestModule,
2092 TestTZInfo,
2093 TestTimeDelta,
2094 TestDateOnly,
2095 TestDate,
2096 TestDateTime,
2097 TestTime,
2098 TestTimeTZ,
2099 TestDateTimeTZ,
2100 )
2101 ]
2102 return unittest.TestSuite(allsuites)
2103
2104def test_main():
2105 import gc
2106 import sys
2107
2108 thesuite = suite()
2109 lastrc = None
2110 while True:
2111 test_support.run_suite(thesuite)
2112 if 1: # change to 0, under a debug build, for some leak detection
2113 break
2114 gc.collect()
2115 if gc.garbage:
2116 raise SystemError("gc.garbage not empty after test run: %r" %
2117 gc.garbage)
2118 if hasattr(sys, 'gettotalrefcount'):
2119 thisrc = sys.gettotalrefcount()
2120 print >> sys.stderr, '*' * 10, 'total refs:', thisrc,
2121 if lastrc:
2122 print >> sys.stderr, 'delta:', thisrc - lastrc
2123 else:
2124 print >> sys.stderr
2125 lastrc = thisrc
2126
2127if __name__ == "__main__":
2128 test_main()