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