blob: 6838e4748f1fc45d7c3d52aa38dd43f168ec8502 [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
Tim Petersd6844152002-12-22 20:58:42 +0000871 def test_srftime_out_of_range(self):
872 # For nasty technical reasons, we can't handle years before 1900.
873 cls = self.theclass
874 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
875 for y in 1, 49, 51, 99, 100, 1000, 1899:
876 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters2a799bf2002-12-16 20:18:38 +0000877#############################################################################
878# datetime tests
879
880class TestDateTime(TestDate):
881
882 theclass = datetime
883
884 def test_basic_attributes(self):
885 dt = self.theclass(2002, 3, 1, 12, 0)
886 self.assertEqual(dt.year, 2002)
887 self.assertEqual(dt.month, 3)
888 self.assertEqual(dt.day, 1)
889 self.assertEqual(dt.hour, 12)
890 self.assertEqual(dt.minute, 0)
891 self.assertEqual(dt.second, 0)
892 self.assertEqual(dt.microsecond, 0)
893
894 def test_basic_attributes_nonzero(self):
895 # Make sure all attributes are non-zero so bugs in
896 # bit-shifting access show up.
897 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
898 self.assertEqual(dt.year, 2002)
899 self.assertEqual(dt.month, 3)
900 self.assertEqual(dt.day, 1)
901 self.assertEqual(dt.hour, 12)
902 self.assertEqual(dt.minute, 59)
903 self.assertEqual(dt.second, 59)
904 self.assertEqual(dt.microsecond, 8000)
905
906 def test_roundtrip(self):
907 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
908 self.theclass.now()):
909 # Verify dt -> string -> datetime identity.
910 s = repr(dt)
911 self.failUnless(s.startswith('datetime.'))
912 s = s[9:]
913 dt2 = eval(s)
914 self.assertEqual(dt, dt2)
915
916 # Verify identity via reconstructing from pieces.
917 dt2 = self.theclass(dt.year, dt.month, dt.day,
918 dt.hour, dt.minute, dt.second,
919 dt.microsecond)
920 self.assertEqual(dt, dt2)
921
922 def test_isoformat(self):
923 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
924 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
925 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
926 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
927 # str is ISO format with the separator forced to a blank.
928 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
929
930 t = self.theclass(2, 3, 2)
931 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
932 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
933 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
934 # str is ISO format with the separator forced to a blank.
935 self.assertEqual(str(t), "0002-03-02 00:00:00")
936
937 def test_more_ctime(self):
938 # Test fields that TestDate doesn't touch.
939 import time
940
941 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
942 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
943 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
944 # out. The difference is that t.ctime() produces " 2" for the day,
945 # but platform ctime() produces "02" for the day. According to
946 # C99, t.ctime() is correct here.
947 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
948
949 # So test a case where that difference doesn't matter.
950 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
951 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
952
953 def test_tz_independent_comparing(self):
954 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
955 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
956 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
957 self.assertEqual(dt1, dt3)
958 self.assert_(dt2 > dt3)
959
960 # Make sure comparison doesn't forget microseconds, and isn't done
961 # via comparing a float timestamp (an IEEE double doesn't have enough
962 # precision to span microsecond resolution across years 1 thru 9999,
963 # so comparing via timestamp necessarily calls some distinct values
964 # equal).
965 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
966 us = timedelta(microseconds=1)
967 dt2 = dt1 + us
968 self.assertEqual(dt2 - dt1, us)
969 self.assert_(dt1 < dt2)
970
971 def test_bad_constructor_arguments(self):
972 # bad years
973 self.theclass(MINYEAR, 1, 1) # no exception
974 self.theclass(MAXYEAR, 1, 1) # no exception
975 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
976 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
977 # bad months
978 self.theclass(2000, 1, 1) # no exception
979 self.theclass(2000, 12, 1) # no exception
980 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
981 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
982 # bad days
983 self.theclass(2000, 2, 29) # no exception
984 self.theclass(2004, 2, 29) # no exception
985 self.theclass(2400, 2, 29) # no exception
986 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
987 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
988 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
989 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
990 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
991 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
992 # bad hours
993 self.theclass(2000, 1, 31, 0) # no exception
994 self.theclass(2000, 1, 31, 23) # no exception
995 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
996 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
997 # bad minutes
998 self.theclass(2000, 1, 31, 23, 0) # no exception
999 self.theclass(2000, 1, 31, 23, 59) # no exception
1000 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1001 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1002 # bad seconds
1003 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1004 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1005 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1006 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1007 # bad microseconds
1008 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1009 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1010 self.assertRaises(ValueError, self.theclass,
1011 2000, 1, 31, 23, 59, 59, -1)
1012 self.assertRaises(ValueError, self.theclass,
1013 2000, 1, 31, 23, 59, 59,
1014 1000000)
1015
1016 def test_hash_equality(self):
1017 d = self.theclass(2000, 12, 31, 23, 30, 17)
1018 e = self.theclass(2000, 12, 31, 23, 30, 17)
1019 self.assertEqual(d, e)
1020 self.assertEqual(hash(d), hash(e))
1021
1022 dic = {d: 1}
1023 dic[e] = 2
1024 self.assertEqual(len(dic), 1)
1025 self.assertEqual(dic[d], 2)
1026 self.assertEqual(dic[e], 2)
1027
1028 d = self.theclass(2001, 1, 1, 0, 5, 17)
1029 e = self.theclass(2001, 1, 1, 0, 5, 17)
1030 self.assertEqual(d, e)
1031 self.assertEqual(hash(d), hash(e))
1032
1033 dic = {d: 1}
1034 dic[e] = 2
1035 self.assertEqual(len(dic), 1)
1036 self.assertEqual(dic[d], 2)
1037 self.assertEqual(dic[e], 2)
1038
1039 def test_computations(self):
1040 a = self.theclass(2002, 1, 31)
1041 b = self.theclass(1956, 1, 31)
1042 diff = a-b
1043 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1044 self.assertEqual(diff.seconds, 0)
1045 self.assertEqual(diff.microseconds, 0)
1046 a = self.theclass(2002, 3, 2, 17, 6)
1047 millisec = timedelta(0, 0, 1000)
1048 hour = timedelta(0, 3600)
1049 day = timedelta(1)
1050 week = timedelta(7)
1051 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1052 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1053 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1054 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1055 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1056 self.assertEqual(a - hour, a + -hour)
1057 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1058 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1059 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1060 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1061 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1062 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1063 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1064 self.assertEqual((a + week) - a, week)
1065 self.assertEqual((a + day) - a, day)
1066 self.assertEqual((a + hour) - a, hour)
1067 self.assertEqual((a + millisec) - a, millisec)
1068 self.assertEqual((a - week) - a, -week)
1069 self.assertEqual((a - day) - a, -day)
1070 self.assertEqual((a - hour) - a, -hour)
1071 self.assertEqual((a - millisec) - a, -millisec)
1072 self.assertEqual(a - (a + week), -week)
1073 self.assertEqual(a - (a + day), -day)
1074 self.assertEqual(a - (a + hour), -hour)
1075 self.assertEqual(a - (a + millisec), -millisec)
1076 self.assertEqual(a - (a - week), week)
1077 self.assertEqual(a - (a - day), day)
1078 self.assertEqual(a - (a - hour), hour)
1079 self.assertEqual(a - (a - millisec), millisec)
1080 self.assertEqual(a + (week + day + hour + millisec),
1081 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1082 self.assertEqual(a + (week + day + hour + millisec),
1083 (((a + week) + day) + hour) + millisec)
1084 self.assertEqual(a - (week + day + hour + millisec),
1085 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1086 self.assertEqual(a - (week + day + hour + millisec),
1087 (((a - week) - day) - hour) - millisec)
1088 # Add/sub ints, longs, floats should be illegal
1089 for i in 1, 1L, 1.0:
1090 self.assertRaises(TypeError, lambda: a+i)
1091 self.assertRaises(TypeError, lambda: a-i)
1092 self.assertRaises(TypeError, lambda: i+a)
1093 self.assertRaises(TypeError, lambda: i-a)
1094
1095 # delta - datetime is senseless.
1096 self.assertRaises(TypeError, lambda: day - a)
1097 # mixing datetime and (delta or datetime) via * or // is senseless
1098 self.assertRaises(TypeError, lambda: day * a)
1099 self.assertRaises(TypeError, lambda: a * day)
1100 self.assertRaises(TypeError, lambda: day // a)
1101 self.assertRaises(TypeError, lambda: a // day)
1102 self.assertRaises(TypeError, lambda: a * a)
1103 self.assertRaises(TypeError, lambda: a // a)
1104 # datetime + datetime is senseless
1105 self.assertRaises(TypeError, lambda: a + a)
1106
1107 def test_pickling(self):
1108 import pickle, cPickle
1109 args = 6, 7, 23, 20, 59, 1, 64**2
1110 orig = self.theclass(*args)
1111 state = orig.__getstate__()
1112 self.assertEqual(state, '\x00\x06\x07\x17\x14\x3b\x01\x00\x10\x00')
1113 derived = self.theclass(1, 1, 1)
1114 derived.__setstate__(state)
1115 self.assertEqual(orig, derived)
1116 for pickler in pickle, cPickle:
1117 for binary in 0, 1:
1118 green = pickler.dumps(orig, binary)
1119 derived = pickler.loads(green)
1120 self.assertEqual(orig, derived)
1121
1122 def test_more_compare(self):
1123 # The test_compare() inherited from TestDate covers the error cases.
1124 # We just want to test lexicographic ordering on the members datetime
1125 # has that date lacks.
1126 args = [2000, 11, 29, 20, 58, 16, 999998]
1127 t1 = self.theclass(*args)
1128 t2 = self.theclass(*args)
1129 self.failUnless(t1 == t2)
1130 self.failUnless(t1 <= t2)
1131 self.failUnless(t1 >= t2)
1132 self.failUnless(not t1 != t2)
1133 self.failUnless(not t1 < t2)
1134 self.failUnless(not t1 > t2)
1135 self.assertEqual(cmp(t1, t2), 0)
1136 self.assertEqual(cmp(t2, t1), 0)
1137
1138 for i in range(len(args)):
1139 newargs = args[:]
1140 newargs[i] = args[i] + 1
1141 t2 = self.theclass(*newargs) # this is larger than t1
1142 self.failUnless(t1 < t2)
1143 self.failUnless(t2 > t1)
1144 self.failUnless(t1 <= t2)
1145 self.failUnless(t2 >= t1)
1146 self.failUnless(t1 != t2)
1147 self.failUnless(t2 != t1)
1148 self.failUnless(not t1 == t2)
1149 self.failUnless(not t2 == t1)
1150 self.failUnless(not t1 > t2)
1151 self.failUnless(not t2 < t1)
1152 self.failUnless(not t1 >= t2)
1153 self.failUnless(not t2 <= t1)
1154 self.assertEqual(cmp(t1, t2), -1)
1155 self.assertEqual(cmp(t2, t1), 1)
1156
1157
1158 # A helper for timestamp constructor tests.
1159 def verify_field_equality(self, expected, got):
1160 self.assertEqual(expected.tm_year, got.year)
1161 self.assertEqual(expected.tm_mon, got.month)
1162 self.assertEqual(expected.tm_mday, got.day)
1163 self.assertEqual(expected.tm_hour, got.hour)
1164 self.assertEqual(expected.tm_min, got.minute)
1165 self.assertEqual(expected.tm_sec, got.second)
1166
1167 def test_fromtimestamp(self):
1168 import time
1169
1170 ts = time.time()
1171 expected = time.localtime(ts)
1172 got = self.theclass.fromtimestamp(ts)
1173 self.verify_field_equality(expected, got)
1174
1175 def test_utcfromtimestamp(self):
1176 import time
1177
1178 ts = time.time()
1179 expected = time.gmtime(ts)
1180 got = self.theclass.utcfromtimestamp(ts)
1181 self.verify_field_equality(expected, got)
1182
1183 def test_utcnow(self):
1184 import time
1185
1186 # Call it a success if utcnow() and utcfromtimestamp() are within
1187 # a second of each other.
1188 tolerance = timedelta(seconds=1)
1189 for dummy in range(3):
1190 from_now = self.theclass.utcnow()
1191 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1192 if abs(from_timestamp - from_now) <= tolerance:
1193 break
1194 # Else try again a few times.
1195 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1196
1197 def test_more_timetuple(self):
1198 # This tests fields beyond those tested by the TestDate.test_timetuple.
1199 t = self.theclass(2004, 12, 31, 6, 22, 33)
1200 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1201 self.assertEqual(t.timetuple(),
1202 (t.year, t.month, t.day,
1203 t.hour, t.minute, t.second,
1204 t.weekday(),
1205 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1206 -1))
1207 tt = t.timetuple()
1208 self.assertEqual(tt.tm_year, t.year)
1209 self.assertEqual(tt.tm_mon, t.month)
1210 self.assertEqual(tt.tm_mday, t.day)
1211 self.assertEqual(tt.tm_hour, t.hour)
1212 self.assertEqual(tt.tm_min, t.minute)
1213 self.assertEqual(tt.tm_sec, t.second)
1214 self.assertEqual(tt.tm_wday, t.weekday())
1215 self.assertEqual(tt.tm_yday, t.toordinal() -
1216 date(t.year, 1, 1).toordinal() + 1)
1217 self.assertEqual(tt.tm_isdst, -1)
1218
1219 def test_more_strftime(self):
1220 # This tests fields beyond those tested by the TestDate.test_strftime.
1221 t = self.theclass(2004, 12, 31, 6, 22, 33)
1222 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1223 "12 31 04 33 22 06 366")
1224
1225 def test_extract(self):
1226 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1227 self.assertEqual(dt.date(), date(2002, 3, 4))
1228 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1229
1230 def test_combine(self):
1231 d = date(2002, 3, 4)
1232 t = time(18, 45, 3, 1234)
1233 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1234 combine = self.theclass.combine
1235 dt = combine(d, t)
1236 self.assertEqual(dt, expected)
1237
1238 dt = combine(time=t, date=d)
1239 self.assertEqual(dt, expected)
1240
1241 self.assertEqual(d, dt.date())
1242 self.assertEqual(t, dt.time())
1243 self.assertEqual(dt, combine(dt.date(), dt.time()))
1244
1245 self.assertRaises(TypeError, combine) # need an arg
1246 self.assertRaises(TypeError, combine, d) # need two args
1247 self.assertRaises(TypeError, combine, t, d) # args reversed
1248 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1249 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1250
1251class TestTime(unittest.TestCase):
1252
1253 theclass = time
1254
1255 def test_basic_attributes(self):
1256 t = self.theclass(12, 0)
1257 self.assertEqual(t.hour, 12)
1258 self.assertEqual(t.minute, 0)
1259 self.assertEqual(t.second, 0)
1260 self.assertEqual(t.microsecond, 0)
1261
1262 def test_basic_attributes_nonzero(self):
1263 # Make sure all attributes are non-zero so bugs in
1264 # bit-shifting access show up.
1265 t = self.theclass(12, 59, 59, 8000)
1266 self.assertEqual(t.hour, 12)
1267 self.assertEqual(t.minute, 59)
1268 self.assertEqual(t.second, 59)
1269 self.assertEqual(t.microsecond, 8000)
1270
1271 def test_roundtrip(self):
1272 t = self.theclass(1, 2, 3, 4)
1273
1274 # Verify t -> string -> time identity.
1275 s = repr(t)
1276 self.failUnless(s.startswith('datetime.'))
1277 s = s[9:]
1278 t2 = eval(s)
1279 self.assertEqual(t, t2)
1280
1281 # Verify identity via reconstructing from pieces.
1282 t2 = self.theclass(t.hour, t.minute, t.second,
1283 t.microsecond)
1284 self.assertEqual(t, t2)
1285
1286 def test_comparing(self):
1287 args = [1, 2, 3, 4]
1288 t1 = self.theclass(*args)
1289 t2 = self.theclass(*args)
1290 self.failUnless(t1 == t2)
1291 self.failUnless(t1 <= t2)
1292 self.failUnless(t1 >= t2)
1293 self.failUnless(not t1 != t2)
1294 self.failUnless(not t1 < t2)
1295 self.failUnless(not t1 > t2)
1296 self.assertEqual(cmp(t1, t2), 0)
1297 self.assertEqual(cmp(t2, t1), 0)
1298
1299 for i in range(len(args)):
1300 newargs = args[:]
1301 newargs[i] = args[i] + 1
1302 t2 = self.theclass(*newargs) # this is larger than t1
1303 self.failUnless(t1 < t2)
1304 self.failUnless(t2 > t1)
1305 self.failUnless(t1 <= t2)
1306 self.failUnless(t2 >= t1)
1307 self.failUnless(t1 != t2)
1308 self.failUnless(t2 != t1)
1309 self.failUnless(not t1 == t2)
1310 self.failUnless(not t2 == t1)
1311 self.failUnless(not t1 > t2)
1312 self.failUnless(not t2 < t1)
1313 self.failUnless(not t1 >= t2)
1314 self.failUnless(not t2 <= t1)
1315 self.assertEqual(cmp(t1, t2), -1)
1316 self.assertEqual(cmp(t2, t1), 1)
1317
1318 for badarg in (10, 10L, 34.5, "abc", {}, [], (), date(1, 1, 1),
1319 datetime(1, 1, 1, 1, 1), timedelta(9)):
1320 self.assertRaises(TypeError, lambda: t1 == badarg)
1321 self.assertRaises(TypeError, lambda: t1 != badarg)
1322 self.assertRaises(TypeError, lambda: t1 <= badarg)
1323 self.assertRaises(TypeError, lambda: t1 < badarg)
1324 self.assertRaises(TypeError, lambda: t1 > badarg)
1325 self.assertRaises(TypeError, lambda: t1 >= badarg)
1326 self.assertRaises(TypeError, lambda: badarg == t1)
1327 self.assertRaises(TypeError, lambda: badarg != t1)
1328 self.assertRaises(TypeError, lambda: badarg <= t1)
1329 self.assertRaises(TypeError, lambda: badarg < t1)
1330 self.assertRaises(TypeError, lambda: badarg > t1)
1331 self.assertRaises(TypeError, lambda: badarg >= t1)
1332
1333 def test_bad_constructor_arguments(self):
1334 # bad hours
1335 self.theclass(0, 0) # no exception
1336 self.theclass(23, 0) # no exception
1337 self.assertRaises(ValueError, self.theclass, -1, 0)
1338 self.assertRaises(ValueError, self.theclass, 24, 0)
1339 # bad minutes
1340 self.theclass(23, 0) # no exception
1341 self.theclass(23, 59) # no exception
1342 self.assertRaises(ValueError, self.theclass, 23, -1)
1343 self.assertRaises(ValueError, self.theclass, 23, 60)
1344 # bad seconds
1345 self.theclass(23, 59, 0) # no exception
1346 self.theclass(23, 59, 59) # no exception
1347 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1348 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1349 # bad microseconds
1350 self.theclass(23, 59, 59, 0) # no exception
1351 self.theclass(23, 59, 59, 999999) # no exception
1352 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1353 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1354
1355 def test_hash_equality(self):
1356 d = self.theclass(23, 30, 17)
1357 e = self.theclass(23, 30, 17)
1358 self.assertEqual(d, e)
1359 self.assertEqual(hash(d), hash(e))
1360
1361 dic = {d: 1}
1362 dic[e] = 2
1363 self.assertEqual(len(dic), 1)
1364 self.assertEqual(dic[d], 2)
1365 self.assertEqual(dic[e], 2)
1366
1367 d = self.theclass(0, 5, 17)
1368 e = self.theclass(0, 5, 17)
1369 self.assertEqual(d, e)
1370 self.assertEqual(hash(d), hash(e))
1371
1372 dic = {d: 1}
1373 dic[e] = 2
1374 self.assertEqual(len(dic), 1)
1375 self.assertEqual(dic[d], 2)
1376 self.assertEqual(dic[e], 2)
1377
1378 def test_isoformat(self):
1379 t = self.theclass(4, 5, 1, 123)
1380 self.assertEqual(t.isoformat(), "04:05:01.000123")
1381 self.assertEqual(t.isoformat(), str(t))
1382
1383 t = self.theclass()
1384 self.assertEqual(t.isoformat(), "00:00:00")
1385 self.assertEqual(t.isoformat(), str(t))
1386
1387 t = self.theclass(microsecond=1)
1388 self.assertEqual(t.isoformat(), "00:00:00.000001")
1389 self.assertEqual(t.isoformat(), str(t))
1390
1391 t = self.theclass(microsecond=10)
1392 self.assertEqual(t.isoformat(), "00:00:00.000010")
1393 self.assertEqual(t.isoformat(), str(t))
1394
1395 t = self.theclass(microsecond=100)
1396 self.assertEqual(t.isoformat(), "00:00:00.000100")
1397 self.assertEqual(t.isoformat(), str(t))
1398
1399 t = self.theclass(microsecond=1000)
1400 self.assertEqual(t.isoformat(), "00:00:00.001000")
1401 self.assertEqual(t.isoformat(), str(t))
1402
1403 t = self.theclass(microsecond=10000)
1404 self.assertEqual(t.isoformat(), "00:00:00.010000")
1405 self.assertEqual(t.isoformat(), str(t))
1406
1407 t = self.theclass(microsecond=100000)
1408 self.assertEqual(t.isoformat(), "00:00:00.100000")
1409 self.assertEqual(t.isoformat(), str(t))
1410
1411 def test_strftime(self):
1412 t = self.theclass(1, 2, 3, 4)
1413 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1414 # A naive object replaces %z and %Z with empty strings.
1415 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1416
1417 def test_str(self):
1418 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1419 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1420 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1421 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1422 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1423
1424 def test_repr(self):
1425 name = 'datetime.' + self.theclass.__name__
1426 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1427 "%s(1, 2, 3, 4)" % name)
1428 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1429 "%s(10, 2, 3, 4000)" % name)
1430 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1431 "%s(0, 2, 3, 400000)" % name)
1432 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1433 "%s(12, 2, 3)" % name)
1434 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1435 "%s(23, 15)" % name)
1436
1437 def test_resolution_info(self):
1438 self.assert_(isinstance(self.theclass.min, self.theclass))
1439 self.assert_(isinstance(self.theclass.max, self.theclass))
1440 self.assert_(isinstance(self.theclass.resolution, timedelta))
1441 self.assert_(self.theclass.max > self.theclass.min)
1442
1443 def test_pickling(self):
1444 import pickle, cPickle
1445 args = 20, 59, 16, 64**2
1446 orig = self.theclass(*args)
1447 state = orig.__getstate__()
1448 self.assertEqual(state, '\x14\x3b\x10\x00\x10\x00')
1449 derived = self.theclass()
1450 derived.__setstate__(state)
1451 self.assertEqual(orig, derived)
1452 for pickler in pickle, cPickle:
1453 for binary in 0, 1:
1454 green = pickler.dumps(orig, binary)
1455 derived = pickler.loads(green)
1456 self.assertEqual(orig, derived)
1457
1458 def test_bool(self):
1459 cls = self.theclass
1460 self.failUnless(cls(1))
1461 self.failUnless(cls(0, 1))
1462 self.failUnless(cls(0, 0, 1))
1463 self.failUnless(cls(0, 0, 0, 1))
1464 self.failUnless(not cls(0))
1465 self.failUnless(not cls())
1466
Tim Peters855fe882002-12-22 03:43:39 +00001467# A mixin for classes with a tzinfo= argument. Subclasses must define
1468# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
1469# must be legit (which is true for timetz and datetimetz).
1470class TZInfoBase(unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001471
Tim Peters855fe882002-12-22 03:43:39 +00001472 def test_bad_tzinfo_classes(self):
1473 cls = self.theclass
1474 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001475
Tim Peters855fe882002-12-22 03:43:39 +00001476 class NiceTry(object):
1477 def __init__(self): pass
1478 def utcoffset(self, dt): pass
1479 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1480
1481 class BetterTry(tzinfo):
1482 def __init__(self): pass
1483 def utcoffset(self, dt): pass
1484 b = BetterTry()
1485 t = cls(1, 1, 1, tzinfo=b)
1486 self.failUnless(t.tzinfo is b)
1487
1488 def test_utc_offset_out_of_bounds(self):
1489 class Edgy(tzinfo):
1490 def __init__(self, offset):
1491 self.offset = offset
1492 def utcoffset(self, dt):
1493 return self.offset
1494
1495 cls = self.theclass
1496 for offset, legit in ((-1440, False),
1497 (-1439, True),
1498 (1439, True),
1499 (1440, False)):
1500 if cls is timetz:
1501 t = cls(1, 2, 3, tzinfo=Edgy(offset))
1502 elif cls is datetimetz:
1503 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
1504 if legit:
1505 aofs = abs(offset)
1506 h, m = divmod(aofs, 60)
1507 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
1508 if isinstance(t, datetimetz):
1509 t = t.timetz()
1510 self.assertEqual(str(t), "01:02:03" + tag)
1511 else:
1512 self.assertRaises(ValueError, str, t)
1513
1514 def test_tzinfo_classes(self):
1515 cls = self.theclass
1516 class C1(tzinfo):
1517 def utcoffset(self, dt): return None
1518 def dst(self, dt): return None
1519 def tzname(self, dt): return None
1520 for t in (cls(1, 1, 1),
1521 cls(1, 1, 1, tzinfo=None),
1522 cls(1, 1, 1, tzinfo=C1())):
1523 self.failUnless(t.utcoffset() is None)
1524 self.failUnless(t.dst() is None)
1525 self.failUnless(t.tzname() is None)
1526
1527 class C2(tzinfo):
1528 def utcoffset(self, dt): return -1439
1529 def dst(self, dt): return 1439
1530 def tzname(self, dt): return "aname"
1531 class C3(tzinfo):
1532 def utcoffset(self, dt): return timedelta(minutes=-1439)
1533 def dst(self, dt): return timedelta(minutes=1439)
1534 def tzname(self, dt): return "aname"
1535 for t in cls(1, 1, 1, tzinfo=C2()), cls(1, 1, 1, tzinfo=C3()):
1536 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
1537 self.assertEqual(t.dst(), timedelta(minutes=1439))
1538 self.assertEqual(t.tzname(), "aname")
1539
1540 # Wrong types.
1541 class C4(tzinfo):
1542 def utcoffset(self, dt): return "aname"
1543 def dst(self, dt): return ()
1544 def tzname(self, dt): return 0
1545 t = cls(1, 1, 1, tzinfo=C4())
1546 self.assertRaises(TypeError, t.utcoffset)
1547 self.assertRaises(TypeError, t.dst)
1548 self.assertRaises(TypeError, t.tzname)
1549
1550 # Offset out of range.
1551 class C5(tzinfo):
1552 def utcoffset(self, dt): return -1440
1553 def dst(self, dt): return 1440
1554 class C6(tzinfo):
1555 def utcoffset(self, dt): return timedelta(hours=-24)
1556 def dst(self, dt): return timedelta(hours=24)
1557 for t in cls(1, 1, 1, tzinfo=C5()), cls(1, 1, 1, tzinfo=C6()):
1558 self.assertRaises(ValueError, t.utcoffset)
1559 self.assertRaises(ValueError, t.dst)
1560
1561 # Not a whole number of minutes.
1562 class C7(tzinfo):
1563 def utcoffset(self, dt): return timedelta(seconds=61)
1564 def dst(self, dt): return timedelta(microseconds=-81)
1565 t = cls(1, 1, 1, tzinfo=C7())
1566 self.assertRaises(ValueError, t.utcoffset)
1567 self.assertRaises(ValueError, t.dst)
1568
1569
1570class TestTimeTZ(TestTime, TZInfoBase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001571 theclass = timetz
1572
1573 def test_empty(self):
1574 t = self.theclass()
1575 self.assertEqual(t.hour, 0)
1576 self.assertEqual(t.minute, 0)
1577 self.assertEqual(t.second, 0)
1578 self.assertEqual(t.microsecond, 0)
1579 self.failUnless(t.tzinfo is None)
1580
Tim Peters2a799bf2002-12-16 20:18:38 +00001581 def test_zones(self):
1582 est = FixedOffset(-300, "EST", 1)
1583 utc = FixedOffset(0, "UTC", -2)
1584 met = FixedOffset(60, "MET", 3)
1585 t1 = timetz( 7, 47, tzinfo=est)
1586 t2 = timetz(12, 47, tzinfo=utc)
1587 t3 = timetz(13, 47, tzinfo=met)
1588 t4 = timetz(microsecond=40)
1589 t5 = timetz(microsecond=40, tzinfo=utc)
1590
1591 self.assertEqual(t1.tzinfo, est)
1592 self.assertEqual(t2.tzinfo, utc)
1593 self.assertEqual(t3.tzinfo, met)
1594 self.failUnless(t4.tzinfo is None)
1595 self.assertEqual(t5.tzinfo, utc)
1596
Tim Peters855fe882002-12-22 03:43:39 +00001597 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
1598 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
1599 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00001600 self.failUnless(t4.utcoffset() is None)
1601 self.assertRaises(TypeError, t1.utcoffset, "no args")
1602
1603 self.assertEqual(t1.tzname(), "EST")
1604 self.assertEqual(t2.tzname(), "UTC")
1605 self.assertEqual(t3.tzname(), "MET")
1606 self.failUnless(t4.tzname() is None)
1607 self.assertRaises(TypeError, t1.tzname, "no args")
1608
Tim Peters855fe882002-12-22 03:43:39 +00001609 self.assertEqual(t1.dst(), timedelta(minutes=1))
1610 self.assertEqual(t2.dst(), timedelta(minutes=-2))
1611 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00001612 self.failUnless(t4.dst() is None)
1613 self.assertRaises(TypeError, t1.dst, "no args")
1614
1615 self.assertEqual(hash(t1), hash(t2))
1616 self.assertEqual(hash(t1), hash(t3))
1617 self.assertEqual(hash(t2), hash(t3))
1618
1619 self.assertEqual(t1, t2)
1620 self.assertEqual(t1, t3)
1621 self.assertEqual(t2, t3)
1622 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
1623 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
1624 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
1625
1626 self.assertEqual(str(t1), "07:47:00-05:00")
1627 self.assertEqual(str(t2), "12:47:00+00:00")
1628 self.assertEqual(str(t3), "13:47:00+01:00")
1629 self.assertEqual(str(t4), "00:00:00.000040")
1630 self.assertEqual(str(t5), "00:00:00.000040+00:00")
1631
1632 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
1633 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
1634 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
1635 self.assertEqual(t4.isoformat(), "00:00:00.000040")
1636 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
1637
1638 d = 'datetime.timetz'
1639 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
1640 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
1641 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
1642 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
1643 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
1644
1645 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
1646 "07:47:00 %Z=EST %z=-0500")
1647 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
1648 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
1649
1650 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
1651 t1 = timetz(23, 59, tzinfo=yuck)
1652 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
1653 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
1654
Tim Petersb92bb712002-12-21 17:44:07 +00001655 # Check that an invalid tzname result raises an exception.
1656 class Badtzname(tzinfo):
1657 def tzname(self, dt): return 42
1658 t = timetz(2, 3, 4, tzinfo=Badtzname())
1659 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
1660 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00001661
1662 def test_hash_edge_cases(self):
1663 # Offsets that overflow a basic time.
1664 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
1665 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
1666 self.assertEqual(hash(t1), hash(t2))
1667
1668 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
1669 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
1670 self.assertEqual(hash(t1), hash(t2))
1671
Tim Peters2a799bf2002-12-16 20:18:38 +00001672 def test_pickling(self):
1673 import pickle, cPickle
1674
1675 # Try one without a tzinfo.
1676 args = 20, 59, 16, 64**2
1677 orig = self.theclass(*args)
1678 state = orig.__getstate__()
1679 self.assertEqual(state, ('\x14\x3b\x10\x00\x10\x00',))
1680 derived = self.theclass()
1681 derived.__setstate__(state)
1682 self.assertEqual(orig, derived)
1683 for pickler in pickle, cPickle:
1684 for binary in 0, 1:
1685 green = pickler.dumps(orig, binary)
1686 derived = pickler.loads(green)
1687 self.assertEqual(orig, derived)
1688
1689 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00001690 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00001691 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
1692 state = orig.__getstate__()
1693 derived = self.theclass()
1694 derived.__setstate__(state)
1695 self.assertEqual(orig, derived)
Tim Petersfb8472c2002-12-21 05:04:42 +00001696 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
Tim Peters855fe882002-12-22 03:43:39 +00001697 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
Tim Peters2a799bf2002-12-16 20:18:38 +00001698 self.assertEqual(derived.tzname(), 'cookie')
1699
1700 for pickler in pickle, cPickle:
1701 for binary in 0, 1:
1702 green = pickler.dumps(orig, binary)
1703 derived = pickler.loads(green)
1704 self.assertEqual(orig, derived)
Tim Petersfb8472c2002-12-21 05:04:42 +00001705 self.failUnless(isinstance(derived.tzinfo,
1706 PicklableFixedOffset))
Tim Peters855fe882002-12-22 03:43:39 +00001707 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
Tim Peters2a799bf2002-12-16 20:18:38 +00001708 self.assertEqual(derived.tzname(), 'cookie')
1709
1710 def test_more_bool(self):
1711 # Test cases with non-None tzinfo.
1712 cls = self.theclass
1713
1714 t = cls(0, tzinfo=FixedOffset(-300, ""))
1715 self.failUnless(t)
1716
1717 t = cls(5, tzinfo=FixedOffset(-300, ""))
1718 self.failUnless(t)
1719
1720 t = cls(5, tzinfo=FixedOffset(300, ""))
1721 self.failUnless(not t)
1722
1723 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
1724 self.failUnless(not t)
1725
1726 # Mostly ensuring this doesn't overflow internally.
1727 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
1728 self.failUnless(t)
1729
1730 # But this should yield a value error -- the utcoffset is bogus.
1731 t = cls(0, tzinfo=FixedOffset(24*60, ""))
1732 self.assertRaises(ValueError, lambda: bool(t))
1733
1734 # Likewise.
1735 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
1736 self.assertRaises(ValueError, lambda: bool(t))
1737
Tim Peters855fe882002-12-22 03:43:39 +00001738class TestDateTimeTZ(TestDateTime, TZInfoBase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001739 theclass = datetimetz
1740
1741 def test_trivial(self):
1742 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
1743 self.assertEqual(dt.year, 1)
1744 self.assertEqual(dt.month, 2)
1745 self.assertEqual(dt.day, 3)
1746 self.assertEqual(dt.hour, 4)
1747 self.assertEqual(dt.minute, 5)
1748 self.assertEqual(dt.second, 6)
1749 self.assertEqual(dt.microsecond, 7)
1750 self.assertEqual(dt.tzinfo, None)
1751
1752 def test_even_more_compare(self):
1753 # The test_compare() and test_more_compare() inherited from TestDate
1754 # and TestDateTime covered non-tzinfo cases.
1755
1756 # Smallest possible after UTC adjustment.
1757 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
1758 # Largest possible after UTC adjustment.
1759 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
1760 tzinfo=FixedOffset(-1439, ""))
1761
1762 # Make sure those compare correctly, and w/o overflow.
1763 self.failUnless(t1 < t2)
1764 self.failUnless(t1 != t2)
1765 self.failUnless(t2 > t1)
1766
1767 self.failUnless(t1 == t1)
1768 self.failUnless(t2 == t2)
1769
1770 # Equal afer adjustment.
1771 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
1772 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
1773 self.assertEqual(t1, t2)
1774
1775 # Change t1 not to subtract a minute, and t1 should be larger.
1776 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
1777 self.failUnless(t1 > t2)
1778
1779 # Change t1 to subtract 2 minutes, and t1 should be smaller.
1780 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
1781 self.failUnless(t1 < t2)
1782
1783 # Back to the original t1, but make seconds resolve it.
1784 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
1785 second=1)
1786 self.failUnless(t1 > t2)
1787
1788 # Likewise, but make microseconds resolve it.
1789 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
1790 microsecond=1)
1791 self.failUnless(t1 > t2)
1792
1793 # Make t2 naive and it should fail.
1794 t2 = self.theclass.min
1795 self.assertRaises(TypeError, lambda: t1 == t2)
1796 self.assertEqual(t2, t2)
1797
1798 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
1799 class Naive(tzinfo):
1800 def utcoffset(self, dt): return None
1801 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
1802 self.assertRaises(TypeError, lambda: t1 == t2)
1803 self.assertEqual(t2, t2)
1804
1805 # OTOH, it's OK to compare two of these mixing the two ways of being
1806 # naive.
1807 t1 = self.theclass(5, 6, 7)
1808 self.assertEqual(t1, t2)
1809
1810 # Try a bogus uctoffset.
1811 class Bogus(tzinfo):
1812 def utcoffset(self, dt): return 1440 # out of bounds
1813 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
1814 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
1815 self.assertRaises(ValueError, lambda: t1 == t1)
1816
Tim Peters2a799bf2002-12-16 20:18:38 +00001817 def test_pickling(self):
1818 import pickle, cPickle
1819
1820 # Try one without a tzinfo.
1821 args = 6, 7, 23, 20, 59, 1, 64**2
1822 orig = self.theclass(*args)
1823 state = orig.__getstate__()
1824 self.assertEqual(state, ('\x00\x06\x07\x17\x14\x3b\x01\x00\x10\x00',))
1825 derived = self.theclass(1, 1, 1)
1826 derived.__setstate__(state)
1827 self.assertEqual(orig, derived)
1828 for pickler in pickle, cPickle:
1829 for binary in 0, 1:
1830 green = pickler.dumps(orig, binary)
1831 derived = pickler.loads(green)
1832 self.assertEqual(orig, derived)
1833
1834 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00001835 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00001836 orig = self.theclass(*args, **{'tzinfo': tinfo})
1837 state = orig.__getstate__()
1838 derived = self.theclass(1, 1, 1)
1839 derived.__setstate__(state)
1840 self.assertEqual(orig, derived)
Tim Petersfb8472c2002-12-21 05:04:42 +00001841 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
Tim Peters855fe882002-12-22 03:43:39 +00001842 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
Tim Peters2a799bf2002-12-16 20:18:38 +00001843 self.assertEqual(derived.tzname(), 'cookie')
1844
1845 for pickler in pickle, cPickle:
1846 for binary in 0, 1:
1847 green = pickler.dumps(orig, binary)
1848 derived = pickler.loads(green)
1849 self.assertEqual(orig, derived)
Tim Petersfb8472c2002-12-21 05:04:42 +00001850 self.failUnless(isinstance(derived.tzinfo,
1851 PicklableFixedOffset))
Tim Peters855fe882002-12-22 03:43:39 +00001852 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
Tim Peters2a799bf2002-12-16 20:18:38 +00001853 self.assertEqual(derived.tzname(), 'cookie')
1854
1855 def test_extreme_hashes(self):
1856 # If an attempt is made to hash these via subtracting the offset
1857 # then hashing a datetime object, OverflowError results. The
1858 # Python implementation used to blow up here.
1859 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
1860 hash(t)
1861 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
1862 tzinfo=FixedOffset(-1439, ""))
1863 hash(t)
1864
1865 # OTOH, an OOB offset should blow up.
1866 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
1867 self.assertRaises(ValueError, hash, t)
1868
1869 def test_zones(self):
1870 est = FixedOffset(-300, "EST")
1871 utc = FixedOffset(0, "UTC")
1872 met = FixedOffset(60, "MET")
1873 t1 = datetimetz(2002, 3, 19, 7, 47, tzinfo=est)
1874 t2 = datetimetz(2002, 3, 19, 12, 47, tzinfo=utc)
1875 t3 = datetimetz(2002, 3, 19, 13, 47, tzinfo=met)
1876 self.assertEqual(t1.tzinfo, est)
1877 self.assertEqual(t2.tzinfo, utc)
1878 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00001879 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
1880 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
1881 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00001882 self.assertEqual(t1.tzname(), "EST")
1883 self.assertEqual(t2.tzname(), "UTC")
1884 self.assertEqual(t3.tzname(), "MET")
1885 self.assertEqual(hash(t1), hash(t2))
1886 self.assertEqual(hash(t1), hash(t3))
1887 self.assertEqual(hash(t2), hash(t3))
1888 self.assertEqual(t1, t2)
1889 self.assertEqual(t1, t3)
1890 self.assertEqual(t2, t3)
1891 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
1892 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
1893 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
1894 d = 'datetime.datetimetz(2002, 3, 19, '
1895 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
1896 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
1897 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
1898
1899 def test_combine(self):
1900 met = FixedOffset(60, "MET")
1901 d = date(2002, 3, 4)
1902 tz = timetz(18, 45, 3, 1234, tzinfo=met)
1903 dt = datetimetz.combine(d, tz)
1904 self.assertEqual(dt, datetimetz(2002, 3, 4, 18, 45, 3, 1234,
1905 tzinfo=met))
1906
1907 def test_extract(self):
1908 met = FixedOffset(60, "MET")
1909 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
1910 self.assertEqual(dt.date(), date(2002, 3, 4))
1911 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1912 self.assertEqual(dt.timetz(), timetz(18, 45, 3, 1234, tzinfo=met))
1913
1914 def test_tz_aware_arithmetic(self):
1915 import random
1916
1917 now = self.theclass.now()
1918 tz55 = FixedOffset(-330, "west 5:30")
1919 timeaware = timetz(now.hour, now.minute, now.second,
1920 now.microsecond, tzinfo=tz55)
1921 nowaware = self.theclass.combine(now.date(), timeaware)
1922 self.failUnless(nowaware.tzinfo is tz55)
1923 self.assertEqual(nowaware.timetz(), timeaware)
1924
1925 # Can't mix aware and non-aware.
1926 self.assertRaises(TypeError, lambda: now - nowaware)
1927 self.assertRaises(TypeError, lambda: nowaware - now)
1928
1929 # And adding datetimetz's doesn't make sense, aware or not.
1930 self.assertRaises(TypeError, lambda: now + nowaware)
1931 self.assertRaises(TypeError, lambda: nowaware + now)
1932 self.assertRaises(TypeError, lambda: nowaware + nowaware)
1933
1934 # Subtracting should yield 0.
1935 self.assertEqual(now - now, timedelta(0))
1936 self.assertEqual(nowaware - nowaware, timedelta(0))
1937
1938 # Adding a delta should preserve tzinfo.
1939 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
1940 nowawareplus = nowaware + delta
1941 self.failUnless(nowaware.tzinfo is tz55)
1942 nowawareplus2 = delta + nowaware
1943 self.failUnless(nowawareplus2.tzinfo is tz55)
1944 self.assertEqual(nowawareplus, nowawareplus2)
1945
1946 # that - delta should be what we started with, and that - what we
1947 # started with should be delta.
1948 diff = nowawareplus - delta
1949 self.failUnless(diff.tzinfo is tz55)
1950 self.assertEqual(nowaware, diff)
1951 self.assertRaises(TypeError, lambda: delta - nowawareplus)
1952 self.assertEqual(nowawareplus - nowaware, delta)
1953
1954 # Make up a random timezone.
1955 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
1956 # Attach it to nowawareplus -- this is clumsy.
1957 nowawareplus = self.theclass.combine(nowawareplus.date(),
1958 timetz(nowawareplus.hour,
1959 nowawareplus.minute,
1960 nowawareplus.second,
1961 nowawareplus.microsecond,
1962 tzinfo=tzr))
1963 self.failUnless(nowawareplus.tzinfo is tzr)
1964 # Make sure the difference takes the timezone adjustments into account.
1965 got = nowaware - nowawareplus
1966 # Expected: (nowaware base - nowaware offset) -
1967 # (nowawareplus base - nowawareplus offset) =
1968 # (nowaware base - nowawareplus base) +
1969 # (nowawareplus offset - nowaware offset) =
1970 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00001971 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00001972 self.assertEqual(got, expected)
1973
1974 # Try max possible difference.
1975 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
1976 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
1977 tzinfo=FixedOffset(-1439, "max"))
1978 maxdiff = max - min
1979 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
1980 timedelta(minutes=2*1439))
1981
1982 def test_tzinfo_now(self):
1983 meth = self.theclass.now
1984 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
1985 base = meth()
1986 # Try with and without naming the keyword.
1987 off42 = FixedOffset(42, "42")
1988 another = meth(off42)
1989 again = meth(tzinfo=off42)
1990 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00001991 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00001992 # Bad argument with and w/o naming the keyword.
1993 self.assertRaises(TypeError, meth, 16)
1994 self.assertRaises(TypeError, meth, tzinfo=16)
1995 # Bad keyword name.
1996 self.assertRaises(TypeError, meth, tinfo=off42)
1997 # Too many args.
1998 self.assertRaises(TypeError, meth, off42, off42)
1999
2000 def test_tzinfo_fromtimestamp(self):
2001 import time
2002 meth = self.theclass.fromtimestamp
2003 ts = time.time()
2004 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2005 base = meth(ts)
2006 # Try with and without naming the keyword.
2007 off42 = FixedOffset(42, "42")
2008 another = meth(ts, off42)
2009 again = meth(ts, tzinfo=off42)
2010 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002011 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002012 # Bad argument with and w/o naming the keyword.
2013 self.assertRaises(TypeError, meth, ts, 16)
2014 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2015 # Bad keyword name.
2016 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2017 # Too many args.
2018 self.assertRaises(TypeError, meth, ts, off42, off42)
2019 # Too few args.
2020 self.assertRaises(TypeError, meth)
2021
2022 def test_tzinfo_utcnow(self):
2023 meth = self.theclass.utcnow
2024 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2025 base = meth()
2026 # Try with and without naming the keyword; for whatever reason,
2027 # utcnow() doesn't accept a tzinfo argument.
2028 off42 = FixedOffset(42, "42")
2029 self.assertRaises(TypeError, meth, off42)
2030 self.assertRaises(TypeError, meth, tzinfo=off42)
2031
2032 def test_tzinfo_utcfromtimestamp(self):
2033 import time
2034 meth = self.theclass.utcfromtimestamp
2035 ts = time.time()
2036 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2037 base = meth(ts)
2038 # Try with and without naming the keyword; for whatever reason,
2039 # utcfromtimestamp() doesn't accept a tzinfo argument.
2040 off42 = FixedOffset(42, "42")
2041 self.assertRaises(TypeError, meth, ts, off42)
2042 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2043
2044 def test_tzinfo_timetuple(self):
2045 # TestDateTime tested most of this. datetimetz adds a twist to the
2046 # DST flag.
2047 class DST(tzinfo):
2048 def __init__(self, dstvalue):
2049 self.dstvalue = dstvalue
2050 def dst(self, dt):
2051 return self.dstvalue
2052
2053 cls = self.theclass
2054 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2055 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2056 t = d.timetuple()
2057 self.assertEqual(1, t.tm_year)
2058 self.assertEqual(1, t.tm_mon)
2059 self.assertEqual(1, t.tm_mday)
2060 self.assertEqual(10, t.tm_hour)
2061 self.assertEqual(20, t.tm_min)
2062 self.assertEqual(30, t.tm_sec)
2063 self.assertEqual(0, t.tm_wday)
2064 self.assertEqual(1, t.tm_yday)
2065 self.assertEqual(flag, t.tm_isdst)
2066
2067 # dst() returns wrong type.
2068 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2069
2070 # dst() at the edge.
2071 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2072 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2073
2074 # dst() out of range.
2075 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2076 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2077
2078 def test_utctimetuple(self):
2079 class DST(tzinfo):
2080 def __init__(self, dstvalue):
2081 self.dstvalue = dstvalue
2082 def dst(self, dt):
2083 return self.dstvalue
2084
2085 cls = self.theclass
2086 # This can't work: DST didn't implement utcoffset.
2087 self.assertRaises(NotImplementedError,
2088 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2089
2090 class UOFS(DST):
2091 def __init__(self, uofs, dofs=None):
2092 DST.__init__(self, dofs)
2093 self.uofs = uofs
2094 def utcoffset(self, dt):
2095 return self.uofs
2096
2097 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2098 # in effect for a UTC time.
2099 for dstvalue in -33, 33, 0, None:
2100 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2101 t = d.utctimetuple()
2102 self.assertEqual(d.year, t.tm_year)
2103 self.assertEqual(d.month, t.tm_mon)
2104 self.assertEqual(d.day, t.tm_mday)
2105 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2106 self.assertEqual(13, t.tm_min)
2107 self.assertEqual(d.second, t.tm_sec)
2108 self.assertEqual(d.weekday(), t.tm_wday)
2109 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2110 t.tm_yday)
2111 self.assertEqual(0, t.tm_isdst)
2112
2113 # At the edges, UTC adjustment can normalize into years out-of-range
2114 # for a datetime object. Ensure that a correct timetuple is
2115 # created anyway.
2116 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2117 # That goes back 1 minute less than a full day.
2118 t = tiny.utctimetuple()
2119 self.assertEqual(t.tm_year, MINYEAR-1)
2120 self.assertEqual(t.tm_mon, 12)
2121 self.assertEqual(t.tm_mday, 31)
2122 self.assertEqual(t.tm_hour, 0)
2123 self.assertEqual(t.tm_min, 1)
2124 self.assertEqual(t.tm_sec, 37)
2125 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2126 self.assertEqual(t.tm_isdst, 0)
2127
2128 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2129 # That goes forward 1 minute less than a full day.
2130 t = huge.utctimetuple()
2131 self.assertEqual(t.tm_year, MAXYEAR+1)
2132 self.assertEqual(t.tm_mon, 1)
2133 self.assertEqual(t.tm_mday, 1)
2134 self.assertEqual(t.tm_hour, 23)
2135 self.assertEqual(t.tm_min, 58)
2136 self.assertEqual(t.tm_sec, 37)
2137 self.assertEqual(t.tm_yday, 1)
2138 self.assertEqual(t.tm_isdst, 0)
2139
2140 def test_tzinfo_isoformat(self):
2141 zero = FixedOffset(0, "+00:00")
2142 plus = FixedOffset(220, "+03:40")
2143 minus = FixedOffset(-231, "-03:51")
2144 unknown = FixedOffset(None, "")
2145
2146 cls = self.theclass
2147 datestr = '0001-02-03'
2148 for ofs in None, zero, plus, minus, unknown:
2149 for us in 0, 987001:
2150 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2151 timestr = '04:05:59' + (us and '.987001' or '')
2152 ofsstr = ofs is not None and d.tzname() or ''
2153 tailstr = timestr + ofsstr
2154 iso = d.isoformat()
2155 self.assertEqual(iso, datestr + 'T' + tailstr)
2156 self.assertEqual(iso, d.isoformat('T'))
2157 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2158 self.assertEqual(str(d), datestr + ' ' + tailstr)
2159
2160
Tim Peterscfd4a8b2002-12-16 21:12:37 +00002161def test_suite():
Tim Peters2a799bf2002-12-16 20:18:38 +00002162 allsuites = [unittest.makeSuite(klass, 'test')
2163 for klass in (TestModule,
2164 TestTZInfo,
2165 TestTimeDelta,
2166 TestDateOnly,
2167 TestDate,
2168 TestDateTime,
2169 TestTime,
2170 TestTimeTZ,
2171 TestDateTimeTZ,
2172 )
2173 ]
2174 return unittest.TestSuite(allsuites)
2175
2176def test_main():
2177 import gc
2178 import sys
2179
Tim Peterscfd4a8b2002-12-16 21:12:37 +00002180 thesuite = test_suite()
Tim Peters2a799bf2002-12-16 20:18:38 +00002181 lastrc = None
2182 while True:
2183 test_support.run_suite(thesuite)
2184 if 1: # change to 0, under a debug build, for some leak detection
2185 break
2186 gc.collect()
2187 if gc.garbage:
2188 raise SystemError("gc.garbage not empty after test run: %r" %
2189 gc.garbage)
2190 if hasattr(sys, 'gettotalrefcount'):
2191 thisrc = sys.gettotalrefcount()
2192 print >> sys.stderr, '*' * 10, 'total refs:', thisrc,
2193 if lastrc:
2194 print >> sys.stderr, 'delta:', thisrc - lastrc
2195 else:
2196 print >> sys.stderr
2197 lastrc = thisrc
2198
2199if __name__ == "__main__":
2200 test_main()