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