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