blob: 287585ed3e5d6935ce4aff1a20c3955c0aedffe4 [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 Rossumd8faa362007-04-27 19:54:29 +00006import os
Tim Peters2a799bf2002-12-16 20:18:38 +00007import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00008import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00009import unittest
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000010try:
11 import cPickle
12except ImportError:
13 cPickle = None
Tim Peters2a799bf2002-12-16 20:18:38 +000014
15from test import test_support
16
17from datetime import MINYEAR, MAXYEAR
18from datetime import timedelta
19from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000020from datetime import time
21from datetime import date, datetime
22
Tim Peters35ad6412003-02-05 04:08:07 +000023pickle_choices = [(pickler, unpickler, proto)
Nick Coghlan650f0d02007-04-15 12:05:43 +000024 for pickler in (pickle, cPickle)
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000025 if pickler is not None
Nick Coghlan650f0d02007-04-15 12:05:43 +000026 for unpickler in (pickle, cPickle)
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000027 if unpickler is not None
Tim Peters35ad6412003-02-05 04:08:07 +000028 for proto in range(3)]
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000029if cPickle is None:
30 assert len(pickle_choices) == 3
31else:
32 assert len(pickle_choices) == 2*2*3
Guido van Rossum177e41a2003-01-30 22:06:23 +000033
Tim Peters68124bb2003-02-08 03:46:31 +000034# An arbitrary collection of objects of non-datetime types, for testing
35# mixed-type comparisons.
Guido van Rossume2a383d2007-01-15 16:59:06 +000036OTHERSTUFF = (10, 10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000037
Tim Peters2a799bf2002-12-16 20:18:38 +000038
39#############################################################################
40# module tests
41
42class TestModule(unittest.TestCase):
43
44 def test_constants(self):
45 import datetime
46 self.assertEqual(datetime.MINYEAR, 1)
47 self.assertEqual(datetime.MAXYEAR, 9999)
48
49#############################################################################
50# tzinfo tests
51
52class FixedOffset(tzinfo):
53 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000054 if isinstance(offset, int):
55 offset = timedelta(minutes=offset)
56 if isinstance(dstoffset, int):
57 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000058 self.__offset = offset
59 self.__name = name
60 self.__dstoffset = dstoffset
61 def __repr__(self):
62 return self.__name.lower()
63 def utcoffset(self, dt):
64 return self.__offset
65 def tzname(self, dt):
66 return self.__name
67 def dst(self, dt):
68 return self.__dstoffset
69
Tim Petersfb8472c2002-12-21 05:04:42 +000070class PicklableFixedOffset(FixedOffset):
71 def __init__(self, offset=None, name=None, dstoffset=None):
72 FixedOffset.__init__(self, offset, name, dstoffset)
73
Tim Peters2a799bf2002-12-16 20:18:38 +000074class TestTZInfo(unittest.TestCase):
75
76 def test_non_abstractness(self):
77 # In order to allow subclasses to get pickled, the C implementation
78 # wasn't able to get away with having __init__ raise
79 # NotImplementedError.
80 useless = tzinfo()
81 dt = datetime.max
82 self.assertRaises(NotImplementedError, useless.tzname, dt)
83 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
84 self.assertRaises(NotImplementedError, useless.dst, dt)
85
86 def test_subclass_must_override(self):
87 class NotEnough(tzinfo):
88 def __init__(self, offset, name):
89 self.__offset = offset
90 self.__name = name
91 self.failUnless(issubclass(NotEnough, tzinfo))
92 ne = NotEnough(3, "NotByALongShot")
93 self.failUnless(isinstance(ne, tzinfo))
94
95 dt = datetime.now()
96 self.assertRaises(NotImplementedError, ne.tzname, dt)
97 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
98 self.assertRaises(NotImplementedError, ne.dst, dt)
99
100 def test_normal(self):
101 fo = FixedOffset(3, "Three")
102 self.failUnless(isinstance(fo, tzinfo))
103 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000104 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000105 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000106 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000107
108 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000109 # There's no point to pickling tzinfo objects on their own (they
110 # carry no data), but they need to be picklable anyway else
111 # concrete subclasses can't be pickled.
112 orig = tzinfo.__new__(tzinfo)
113 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000114 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000115 green = pickler.dumps(orig, proto)
116 derived = unpickler.loads(green)
117 self.failUnless(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000118
119 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000120 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000121 offset = timedelta(minutes=-300)
122 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000123 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000124 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000125 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000126 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000127 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000128 green = pickler.dumps(orig, proto)
129 derived = unpickler.loads(green)
130 self.failUnless(isinstance(derived, tzinfo))
131 self.failUnless(type(derived) is PicklableFixedOffset)
132 self.assertEqual(derived.utcoffset(None), offset)
133 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000134
135#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000136# Base clase for testing a particular aspect of timedelta, time, date and
137# datetime comparisons.
138
Guido van Rossumd8faa362007-04-27 19:54:29 +0000139class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000140 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
141
142 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
143 # legit constructor.
144
145 def test_harmless_mixed_comparison(self):
146 me = self.theclass(1, 1, 1)
147
148 self.failIf(me == ())
149 self.failUnless(me != ())
150 self.failIf(() == me)
151 self.failUnless(() != me)
152
Guido van Rossume2a383d2007-01-15 16:59:06 +0000153 self.failUnless(me in [1, 20, [], me])
154 self.failIf(me not in [1, 20, [], me])
Tim Peters07534a62003-02-07 22:50:28 +0000155
Guido van Rossume2a383d2007-01-15 16:59:06 +0000156 self.failUnless([] in [me, 1, 20, []])
157 self.failIf([] not in [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000158
159 def test_harmful_mixed_comparison(self):
160 me = self.theclass(1, 1, 1)
161
162 self.assertRaises(TypeError, lambda: me < ())
163 self.assertRaises(TypeError, lambda: me <= ())
164 self.assertRaises(TypeError, lambda: me > ())
165 self.assertRaises(TypeError, lambda: me >= ())
166
167 self.assertRaises(TypeError, lambda: () < me)
168 self.assertRaises(TypeError, lambda: () <= me)
169 self.assertRaises(TypeError, lambda: () > me)
170 self.assertRaises(TypeError, lambda: () >= me)
171
172 self.assertRaises(TypeError, cmp, (), me)
173 self.assertRaises(TypeError, cmp, me, ())
174
175#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000176# timedelta tests
177
Guido van Rossumd8faa362007-04-27 19:54:29 +0000178class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000179
180 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000181
182 def test_constructor(self):
183 eq = self.assertEqual
184 td = timedelta
185
186 # Check keyword args to constructor
187 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
188 milliseconds=0, microseconds=0))
189 eq(td(1), td(days=1))
190 eq(td(0, 1), td(seconds=1))
191 eq(td(0, 0, 1), td(microseconds=1))
192 eq(td(weeks=1), td(days=7))
193 eq(td(days=1), td(hours=24))
194 eq(td(hours=1), td(minutes=60))
195 eq(td(minutes=1), td(seconds=60))
196 eq(td(seconds=1), td(milliseconds=1000))
197 eq(td(milliseconds=1), td(microseconds=1000))
198
199 # Check float args to constructor
200 eq(td(weeks=1.0/7), td(days=1))
201 eq(td(days=1.0/24), td(hours=1))
202 eq(td(hours=1.0/60), td(minutes=1))
203 eq(td(minutes=1.0/60), td(seconds=1))
204 eq(td(seconds=0.001), td(milliseconds=1))
205 eq(td(milliseconds=0.001), td(microseconds=1))
206
207 def test_computations(self):
208 eq = self.assertEqual
209 td = timedelta
210
211 a = td(7) # One week
212 b = td(0, 60) # One minute
213 c = td(0, 0, 1000) # One millisecond
214 eq(a+b+c, td(7, 60, 1000))
215 eq(a-b, td(6, 24*3600 - 60))
216 eq(-a, td(-7))
217 eq(+a, td(7))
218 eq(-b, td(-1, 24*3600 - 60))
219 eq(-c, td(-1, 24*3600 - 1, 999000))
220 eq(abs(a), a)
221 eq(abs(-a), a)
222 eq(td(6, 24*3600), a)
223 eq(td(0, 0, 60*1000000), b)
224 eq(a*10, td(70))
225 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000226 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000227 eq(b*10, td(0, 600))
228 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000229 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000230 eq(c*10, td(0, 0, 10000))
231 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000232 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000233 eq(a*-1, -a)
234 eq(b*-2, -b-b)
235 eq(c*-2, -c+-c)
236 eq(b*(60*24), (b*60)*24)
237 eq(b*(60*24), (60*b)*24)
238 eq(c*1000, td(0, 1))
239 eq(1000*c, td(0, 1))
240 eq(a//7, td(1))
241 eq(b//10, td(0, 6))
242 eq(c//1000, td(0, 0, 1))
243 eq(a//10, td(0, 7*24*360))
244 eq(a//3600000, td(0, 0, 7*24*1000))
245
246 def test_disallowed_computations(self):
247 a = timedelta(42)
248
249 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000250 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000251 self.assertRaises(TypeError, lambda: a+i)
252 self.assertRaises(TypeError, lambda: a-i)
253 self.assertRaises(TypeError, lambda: i+a)
254 self.assertRaises(TypeError, lambda: i-a)
255
256 # Mul/div by float isn't supported.
257 x = 2.3
258 self.assertRaises(TypeError, lambda: a*x)
259 self.assertRaises(TypeError, lambda: x*a)
260 self.assertRaises(TypeError, lambda: a/x)
261 self.assertRaises(TypeError, lambda: x/a)
262 self.assertRaises(TypeError, lambda: a // x)
263 self.assertRaises(TypeError, lambda: x // a)
264
265 # Divison of int by timedelta doesn't make sense.
266 # Division by zero doesn't make sense.
Guido van Rossume2a383d2007-01-15 16:59:06 +0000267 for zero in 0, 0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000268 self.assertRaises(TypeError, lambda: zero // a)
269 self.assertRaises(ZeroDivisionError, lambda: a // zero)
270
271 def test_basic_attributes(self):
272 days, seconds, us = 1, 7, 31
273 td = timedelta(days, seconds, us)
274 self.assertEqual(td.days, days)
275 self.assertEqual(td.seconds, seconds)
276 self.assertEqual(td.microseconds, us)
277
278 def test_carries(self):
279 t1 = timedelta(days=100,
280 weeks=-7,
281 hours=-24*(100-49),
282 minutes=-3,
283 seconds=12,
284 microseconds=(3*60 - 12) * 1e6 + 1)
285 t2 = timedelta(microseconds=1)
286 self.assertEqual(t1, t2)
287
288 def test_hash_equality(self):
289 t1 = timedelta(days=100,
290 weeks=-7,
291 hours=-24*(100-49),
292 minutes=-3,
293 seconds=12,
294 microseconds=(3*60 - 12) * 1000000)
295 t2 = timedelta()
296 self.assertEqual(hash(t1), hash(t2))
297
298 t1 += timedelta(weeks=7)
299 t2 += timedelta(days=7*7)
300 self.assertEqual(t1, t2)
301 self.assertEqual(hash(t1), hash(t2))
302
303 d = {t1: 1}
304 d[t2] = 2
305 self.assertEqual(len(d), 1)
306 self.assertEqual(d[t1], 2)
307
308 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000309 args = 12, 34, 56
310 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000311 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000312 green = pickler.dumps(orig, proto)
313 derived = unpickler.loads(green)
314 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000315
316 def test_compare(self):
317 t1 = timedelta(2, 3, 4)
318 t2 = timedelta(2, 3, 4)
319 self.failUnless(t1 == t2)
320 self.failUnless(t1 <= t2)
321 self.failUnless(t1 >= t2)
322 self.failUnless(not t1 != t2)
323 self.failUnless(not t1 < t2)
324 self.failUnless(not t1 > t2)
325 self.assertEqual(cmp(t1, t2), 0)
326 self.assertEqual(cmp(t2, t1), 0)
327
328 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
329 t2 = timedelta(*args) # this is larger than t1
330 self.failUnless(t1 < t2)
331 self.failUnless(t2 > t1)
332 self.failUnless(t1 <= t2)
333 self.failUnless(t2 >= t1)
334 self.failUnless(t1 != t2)
335 self.failUnless(t2 != t1)
336 self.failUnless(not t1 == t2)
337 self.failUnless(not t2 == t1)
338 self.failUnless(not t1 > t2)
339 self.failUnless(not t2 < t1)
340 self.failUnless(not t1 >= t2)
341 self.failUnless(not t2 <= t1)
342 self.assertEqual(cmp(t1, t2), -1)
343 self.assertEqual(cmp(t2, t1), 1)
344
Tim Peters68124bb2003-02-08 03:46:31 +0000345 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000346 self.assertEqual(t1 == badarg, False)
347 self.assertEqual(t1 != badarg, True)
348 self.assertEqual(badarg == t1, False)
349 self.assertEqual(badarg != t1, True)
350
Tim Peters2a799bf2002-12-16 20:18:38 +0000351 self.assertRaises(TypeError, lambda: t1 <= badarg)
352 self.assertRaises(TypeError, lambda: t1 < badarg)
353 self.assertRaises(TypeError, lambda: t1 > badarg)
354 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000355 self.assertRaises(TypeError, lambda: badarg <= t1)
356 self.assertRaises(TypeError, lambda: badarg < t1)
357 self.assertRaises(TypeError, lambda: badarg > t1)
358 self.assertRaises(TypeError, lambda: badarg >= t1)
359
360 def test_str(self):
361 td = timedelta
362 eq = self.assertEqual
363
364 eq(str(td(1)), "1 day, 0:00:00")
365 eq(str(td(-1)), "-1 day, 0:00:00")
366 eq(str(td(2)), "2 days, 0:00:00")
367 eq(str(td(-2)), "-2 days, 0:00:00")
368
369 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
370 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
371 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
372 "-210 days, 23:12:34")
373
374 eq(str(td(milliseconds=1)), "0:00:00.001000")
375 eq(str(td(microseconds=3)), "0:00:00.000003")
376
377 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
378 microseconds=999999)),
379 "999999999 days, 23:59:59.999999")
380
381 def test_roundtrip(self):
382 for td in (timedelta(days=999999999, hours=23, minutes=59,
383 seconds=59, microseconds=999999),
384 timedelta(days=-999999999),
385 timedelta(days=1, seconds=2, microseconds=3)):
386
387 # Verify td -> string -> td identity.
388 s = repr(td)
389 self.failUnless(s.startswith('datetime.'))
390 s = s[9:]
391 td2 = eval(s)
392 self.assertEqual(td, td2)
393
394 # Verify identity via reconstructing from pieces.
395 td2 = timedelta(td.days, td.seconds, td.microseconds)
396 self.assertEqual(td, td2)
397
398 def test_resolution_info(self):
399 self.assert_(isinstance(timedelta.min, timedelta))
400 self.assert_(isinstance(timedelta.max, timedelta))
401 self.assert_(isinstance(timedelta.resolution, timedelta))
402 self.assert_(timedelta.max > timedelta.min)
403 self.assertEqual(timedelta.min, timedelta(-999999999))
404 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
405 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
406
407 def test_overflow(self):
408 tiny = timedelta.resolution
409
410 td = timedelta.min + tiny
411 td -= tiny # no problem
412 self.assertRaises(OverflowError, td.__sub__, tiny)
413 self.assertRaises(OverflowError, td.__add__, -tiny)
414
415 td = timedelta.max - tiny
416 td += tiny # no problem
417 self.assertRaises(OverflowError, td.__add__, tiny)
418 self.assertRaises(OverflowError, td.__sub__, -tiny)
419
420 self.assertRaises(OverflowError, lambda: -timedelta.max)
421
422 def test_microsecond_rounding(self):
423 td = timedelta
424 eq = self.assertEqual
425
426 # Single-field rounding.
427 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
428 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
429 eq(td(milliseconds=0.6/1000), td(microseconds=1))
430 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
431
432 # Rounding due to contributions from more than one field.
433 us_per_hour = 3600e6
434 us_per_day = us_per_hour * 24
435 eq(td(days=.4/us_per_day), td(0))
436 eq(td(hours=.2/us_per_hour), td(0))
437 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
438
439 eq(td(days=-.4/us_per_day), td(0))
440 eq(td(hours=-.2/us_per_hour), td(0))
441 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
442
443 def test_massive_normalization(self):
444 td = timedelta(microseconds=-1)
445 self.assertEqual((td.days, td.seconds, td.microseconds),
446 (-1, 24*3600-1, 999999))
447
448 def test_bool(self):
449 self.failUnless(timedelta(1))
450 self.failUnless(timedelta(0, 1))
451 self.failUnless(timedelta(0, 0, 1))
452 self.failUnless(timedelta(microseconds=1))
453 self.failUnless(not timedelta(0))
454
Tim Petersb0c854d2003-05-17 15:57:00 +0000455 def test_subclass_timedelta(self):
456
457 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000458 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000459 def from_td(td):
460 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000461
462 def as_hours(self):
463 sum = (self.days * 24 +
464 self.seconds / 3600.0 +
465 self.microseconds / 3600e6)
466 return round(sum)
467
468 t1 = T(days=1)
469 self.assert_(type(t1) is T)
470 self.assertEqual(t1.as_hours(), 24)
471
472 t2 = T(days=-1, seconds=-3600)
473 self.assert_(type(t2) is T)
474 self.assertEqual(t2.as_hours(), -25)
475
476 t3 = t1 + t2
477 self.assert_(type(t3) is timedelta)
478 t4 = T.from_td(t3)
479 self.assert_(type(t4) is T)
480 self.assertEqual(t3.days, t4.days)
481 self.assertEqual(t3.seconds, t4.seconds)
482 self.assertEqual(t3.microseconds, t4.microseconds)
483 self.assertEqual(str(t3), str(t4))
484 self.assertEqual(t4.as_hours(), -1)
485
Tim Peters2a799bf2002-12-16 20:18:38 +0000486#############################################################################
487# date tests
488
489class TestDateOnly(unittest.TestCase):
490 # Tests here won't pass if also run on datetime objects, so don't
491 # subclass this to test datetimes too.
492
493 def test_delta_non_days_ignored(self):
494 dt = date(2000, 1, 2)
495 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
496 microseconds=5)
497 days = timedelta(delta.days)
498 self.assertEqual(days, timedelta(1))
499
500 dt2 = dt + delta
501 self.assertEqual(dt2, dt + days)
502
503 dt2 = delta + dt
504 self.assertEqual(dt2, dt + days)
505
506 dt2 = dt - delta
507 self.assertEqual(dt2, dt - days)
508
509 delta = -delta
510 days = timedelta(delta.days)
511 self.assertEqual(days, timedelta(-2))
512
513 dt2 = dt + delta
514 self.assertEqual(dt2, dt + days)
515
516 dt2 = delta + dt
517 self.assertEqual(dt2, dt + days)
518
519 dt2 = dt - delta
520 self.assertEqual(dt2, dt - days)
521
Tim Peters604c0132004-06-07 23:04:33 +0000522class SubclassDate(date):
523 sub_var = 1
524
Guido van Rossumd8faa362007-04-27 19:54:29 +0000525class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000526 # Tests here should pass for both dates and datetimes, except for a
527 # few tests that TestDateTime overrides.
528
529 theclass = date
530
531 def test_basic_attributes(self):
532 dt = self.theclass(2002, 3, 1)
533 self.assertEqual(dt.year, 2002)
534 self.assertEqual(dt.month, 3)
535 self.assertEqual(dt.day, 1)
536
537 def test_roundtrip(self):
538 for dt in (self.theclass(1, 2, 3),
539 self.theclass.today()):
540 # Verify dt -> string -> date identity.
541 s = repr(dt)
542 self.failUnless(s.startswith('datetime.'))
543 s = s[9:]
544 dt2 = eval(s)
545 self.assertEqual(dt, dt2)
546
547 # Verify identity via reconstructing from pieces.
548 dt2 = self.theclass(dt.year, dt.month, dt.day)
549 self.assertEqual(dt, dt2)
550
551 def test_ordinal_conversions(self):
552 # Check some fixed values.
553 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
554 (1, 12, 31, 365),
555 (2, 1, 1, 366),
556 # first example from "Calendrical Calculations"
557 (1945, 11, 12, 710347)]:
558 d = self.theclass(y, m, d)
559 self.assertEqual(n, d.toordinal())
560 fromord = self.theclass.fromordinal(n)
561 self.assertEqual(d, fromord)
562 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000563 # if we're checking something fancier than a date, verify
564 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000565 self.assertEqual(fromord.hour, 0)
566 self.assertEqual(fromord.minute, 0)
567 self.assertEqual(fromord.second, 0)
568 self.assertEqual(fromord.microsecond, 0)
569
Tim Peters0bf60bd2003-01-08 20:40:01 +0000570 # Check first and last days of year spottily across the whole
571 # range of years supported.
572 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000573 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
574 d = self.theclass(year, 1, 1)
575 n = d.toordinal()
576 d2 = self.theclass.fromordinal(n)
577 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000578 # Verify that moving back a day gets to the end of year-1.
579 if year > 1:
580 d = self.theclass.fromordinal(n-1)
581 d2 = self.theclass(year-1, 12, 31)
582 self.assertEqual(d, d2)
583 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000584
585 # Test every day in a leap-year and a non-leap year.
586 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
587 for year, isleap in (2000, True), (2002, False):
588 n = self.theclass(year, 1, 1).toordinal()
589 for month, maxday in zip(range(1, 13), dim):
590 if month == 2 and isleap:
591 maxday += 1
592 for day in range(1, maxday+1):
593 d = self.theclass(year, month, day)
594 self.assertEqual(d.toordinal(), n)
595 self.assertEqual(d, self.theclass.fromordinal(n))
596 n += 1
597
598 def test_extreme_ordinals(self):
599 a = self.theclass.min
600 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
601 aord = a.toordinal()
602 b = a.fromordinal(aord)
603 self.assertEqual(a, b)
604
605 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
606
607 b = a + timedelta(days=1)
608 self.assertEqual(b.toordinal(), aord + 1)
609 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
610
611 a = self.theclass.max
612 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
613 aord = a.toordinal()
614 b = a.fromordinal(aord)
615 self.assertEqual(a, b)
616
617 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
618
619 b = a - timedelta(days=1)
620 self.assertEqual(b.toordinal(), aord - 1)
621 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
622
623 def test_bad_constructor_arguments(self):
624 # bad years
625 self.theclass(MINYEAR, 1, 1) # no exception
626 self.theclass(MAXYEAR, 1, 1) # no exception
627 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
628 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
629 # bad months
630 self.theclass(2000, 1, 1) # no exception
631 self.theclass(2000, 12, 1) # no exception
632 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
633 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
634 # bad days
635 self.theclass(2000, 2, 29) # no exception
636 self.theclass(2004, 2, 29) # no exception
637 self.theclass(2400, 2, 29) # no exception
638 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
639 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
640 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
641 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
642 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
643 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
644
645 def test_hash_equality(self):
646 d = self.theclass(2000, 12, 31)
647 # same thing
648 e = self.theclass(2000, 12, 31)
649 self.assertEqual(d, e)
650 self.assertEqual(hash(d), hash(e))
651
652 dic = {d: 1}
653 dic[e] = 2
654 self.assertEqual(len(dic), 1)
655 self.assertEqual(dic[d], 2)
656 self.assertEqual(dic[e], 2)
657
658 d = self.theclass(2001, 1, 1)
659 # same thing
660 e = self.theclass(2001, 1, 1)
661 self.assertEqual(d, e)
662 self.assertEqual(hash(d), hash(e))
663
664 dic = {d: 1}
665 dic[e] = 2
666 self.assertEqual(len(dic), 1)
667 self.assertEqual(dic[d], 2)
668 self.assertEqual(dic[e], 2)
669
670 def test_computations(self):
671 a = self.theclass(2002, 1, 31)
672 b = self.theclass(1956, 1, 31)
673
674 diff = a-b
675 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
676 self.assertEqual(diff.seconds, 0)
677 self.assertEqual(diff.microseconds, 0)
678
679 day = timedelta(1)
680 week = timedelta(7)
681 a = self.theclass(2002, 3, 2)
682 self.assertEqual(a + day, self.theclass(2002, 3, 3))
683 self.assertEqual(day + a, self.theclass(2002, 3, 3))
684 self.assertEqual(a - day, self.theclass(2002, 3, 1))
685 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
686 self.assertEqual(a + week, self.theclass(2002, 3, 9))
687 self.assertEqual(a - week, self.theclass(2002, 2, 23))
688 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
689 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
690 self.assertEqual((a + week) - a, week)
691 self.assertEqual((a + day) - a, day)
692 self.assertEqual((a - week) - a, -week)
693 self.assertEqual((a - day) - a, -day)
694 self.assertEqual(a - (a + week), -week)
695 self.assertEqual(a - (a + day), -day)
696 self.assertEqual(a - (a - week), week)
697 self.assertEqual(a - (a - day), day)
698
699 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000700 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000701 self.assertRaises(TypeError, lambda: a+i)
702 self.assertRaises(TypeError, lambda: a-i)
703 self.assertRaises(TypeError, lambda: i+a)
704 self.assertRaises(TypeError, lambda: i-a)
705
706 # delta - date is senseless.
707 self.assertRaises(TypeError, lambda: day - a)
708 # mixing date and (delta or date) via * or // is senseless
709 self.assertRaises(TypeError, lambda: day * a)
710 self.assertRaises(TypeError, lambda: a * day)
711 self.assertRaises(TypeError, lambda: day // a)
712 self.assertRaises(TypeError, lambda: a // day)
713 self.assertRaises(TypeError, lambda: a * a)
714 self.assertRaises(TypeError, lambda: a // a)
715 # date + date is senseless
716 self.assertRaises(TypeError, lambda: a + a)
717
718 def test_overflow(self):
719 tiny = self.theclass.resolution
720
721 dt = self.theclass.min + tiny
722 dt -= tiny # no problem
723 self.assertRaises(OverflowError, dt.__sub__, tiny)
724 self.assertRaises(OverflowError, dt.__add__, -tiny)
725
726 dt = self.theclass.max - tiny
727 dt += tiny # no problem
728 self.assertRaises(OverflowError, dt.__add__, tiny)
729 self.assertRaises(OverflowError, dt.__sub__, -tiny)
730
731 def test_fromtimestamp(self):
732 import time
733
734 # Try an arbitrary fixed value.
735 year, month, day = 1999, 9, 19
736 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
737 d = self.theclass.fromtimestamp(ts)
738 self.assertEqual(d.year, year)
739 self.assertEqual(d.month, month)
740 self.assertEqual(d.day, day)
741
Tim Peters1b6f7a92004-06-20 02:50:16 +0000742 def test_insane_fromtimestamp(self):
743 # It's possible that some platform maps time_t to double,
744 # and that this test will fail there. This test should
745 # exempt such platforms (provided they return reasonable
746 # results!).
747 for insane in -1e200, 1e200:
748 self.assertRaises(ValueError, self.theclass.fromtimestamp,
749 insane)
750
Tim Peters2a799bf2002-12-16 20:18:38 +0000751 def test_today(self):
752 import time
753
754 # We claim that today() is like fromtimestamp(time.time()), so
755 # prove it.
756 for dummy in range(3):
757 today = self.theclass.today()
758 ts = time.time()
759 todayagain = self.theclass.fromtimestamp(ts)
760 if today == todayagain:
761 break
762 # There are several legit reasons that could fail:
763 # 1. It recently became midnight, between the today() and the
764 # time() calls.
765 # 2. The platform time() has such fine resolution that we'll
766 # never get the same value twice.
767 # 3. The platform time() has poor resolution, and we just
768 # happened to call today() right before a resolution quantum
769 # boundary.
770 # 4. The system clock got fiddled between calls.
771 # In any case, wait a little while and try again.
772 time.sleep(0.1)
773
774 # It worked or it didn't. If it didn't, assume it's reason #2, and
775 # let the test pass if they're within half a second of each other.
776 self.failUnless(today == todayagain or
777 abs(todayagain - today) < timedelta(seconds=0.5))
778
779 def test_weekday(self):
780 for i in range(7):
781 # March 4, 2002 is a Monday
782 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
783 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
784 # January 2, 1956 is a Monday
785 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
786 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
787
788 def test_isocalendar(self):
789 # Check examples from
790 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
791 for i in range(7):
792 d = self.theclass(2003, 12, 22+i)
793 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
794 d = self.theclass(2003, 12, 29) + timedelta(i)
795 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
796 d = self.theclass(2004, 1, 5+i)
797 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
798 d = self.theclass(2009, 12, 21+i)
799 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
800 d = self.theclass(2009, 12, 28) + timedelta(i)
801 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
802 d = self.theclass(2010, 1, 4+i)
803 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
804
805 def test_iso_long_years(self):
806 # Calculate long ISO years and compare to table from
807 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
808 ISO_LONG_YEARS_TABLE = """
809 4 32 60 88
810 9 37 65 93
811 15 43 71 99
812 20 48 76
813 26 54 82
814
815 105 133 161 189
816 111 139 167 195
817 116 144 172
818 122 150 178
819 128 156 184
820
821 201 229 257 285
822 207 235 263 291
823 212 240 268 296
824 218 246 274
825 224 252 280
826
827 303 331 359 387
828 308 336 364 392
829 314 342 370 398
830 320 348 376
831 325 353 381
832 """
833 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
834 iso_long_years.sort()
835 L = []
836 for i in range(400):
837 d = self.theclass(2000+i, 12, 31)
838 d1 = self.theclass(1600+i, 12, 31)
839 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
840 if d.isocalendar()[1] == 53:
841 L.append(i)
842 self.assertEqual(L, iso_long_years)
843
844 def test_isoformat(self):
845 t = self.theclass(2, 3, 2)
846 self.assertEqual(t.isoformat(), "0002-03-02")
847
848 def test_ctime(self):
849 t = self.theclass(2002, 3, 2)
850 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
851
852 def test_strftime(self):
853 t = self.theclass(2005, 3, 2)
854 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000855 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000856 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000857
858 self.assertRaises(TypeError, t.strftime) # needs an arg
859 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
860 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
861
862 # A naive object replaces %z and %Z w/ empty strings.
863 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
864
865 def test_resolution_info(self):
866 self.assert_(isinstance(self.theclass.min, self.theclass))
867 self.assert_(isinstance(self.theclass.max, self.theclass))
868 self.assert_(isinstance(self.theclass.resolution, timedelta))
869 self.assert_(self.theclass.max > self.theclass.min)
870
871 def test_extreme_timedelta(self):
872 big = self.theclass.max - self.theclass.min
873 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
874 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
875 # n == 315537897599999999 ~= 2**58.13
876 justasbig = timedelta(0, 0, n)
877 self.assertEqual(big, justasbig)
878 self.assertEqual(self.theclass.min + big, self.theclass.max)
879 self.assertEqual(self.theclass.max - big, self.theclass.min)
880
881 def test_timetuple(self):
882 for i in range(7):
883 # January 2, 1956 is a Monday (0)
884 d = self.theclass(1956, 1, 2+i)
885 t = d.timetuple()
886 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
887 # February 1, 1956 is a Wednesday (2)
888 d = self.theclass(1956, 2, 1+i)
889 t = d.timetuple()
890 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
891 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
892 # of the year.
893 d = self.theclass(1956, 3, 1+i)
894 t = d.timetuple()
895 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
896 self.assertEqual(t.tm_year, 1956)
897 self.assertEqual(t.tm_mon, 3)
898 self.assertEqual(t.tm_mday, 1+i)
899 self.assertEqual(t.tm_hour, 0)
900 self.assertEqual(t.tm_min, 0)
901 self.assertEqual(t.tm_sec, 0)
902 self.assertEqual(t.tm_wday, (3+i)%7)
903 self.assertEqual(t.tm_yday, 61+i)
904 self.assertEqual(t.tm_isdst, -1)
905
906 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000907 args = 6, 7, 23
908 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000909 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000910 green = pickler.dumps(orig, proto)
911 derived = unpickler.loads(green)
912 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000913
914 def test_compare(self):
915 t1 = self.theclass(2, 3, 4)
916 t2 = self.theclass(2, 3, 4)
917 self.failUnless(t1 == t2)
918 self.failUnless(t1 <= t2)
919 self.failUnless(t1 >= t2)
920 self.failUnless(not t1 != t2)
921 self.failUnless(not t1 < t2)
922 self.failUnless(not t1 > t2)
923 self.assertEqual(cmp(t1, t2), 0)
924 self.assertEqual(cmp(t2, t1), 0)
925
926 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
927 t2 = self.theclass(*args) # this is larger than t1
928 self.failUnless(t1 < t2)
929 self.failUnless(t2 > t1)
930 self.failUnless(t1 <= t2)
931 self.failUnless(t2 >= t1)
932 self.failUnless(t1 != t2)
933 self.failUnless(t2 != t1)
934 self.failUnless(not t1 == t2)
935 self.failUnless(not t2 == t1)
936 self.failUnless(not t1 > t2)
937 self.failUnless(not t2 < t1)
938 self.failUnless(not t1 >= t2)
939 self.failUnless(not t2 <= t1)
940 self.assertEqual(cmp(t1, t2), -1)
941 self.assertEqual(cmp(t2, t1), 1)
942
Tim Peters68124bb2003-02-08 03:46:31 +0000943 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000944 self.assertEqual(t1 == badarg, False)
945 self.assertEqual(t1 != badarg, True)
946 self.assertEqual(badarg == t1, False)
947 self.assertEqual(badarg != t1, True)
948
Tim Peters2a799bf2002-12-16 20:18:38 +0000949 self.assertRaises(TypeError, lambda: t1 < badarg)
950 self.assertRaises(TypeError, lambda: t1 > badarg)
951 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000952 self.assertRaises(TypeError, lambda: badarg <= t1)
953 self.assertRaises(TypeError, lambda: badarg < t1)
954 self.assertRaises(TypeError, lambda: badarg > t1)
955 self.assertRaises(TypeError, lambda: badarg >= t1)
956
Tim Peters8d81a012003-01-24 22:36:34 +0000957 def test_mixed_compare(self):
958 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000959
960 # Our class can be compared for equality to other classes
961 self.assertEqual(our == 1, False)
962 self.assertEqual(1 == our, False)
963 self.assertEqual(our != 1, True)
964 self.assertEqual(1 != our, True)
965
966 # But the ordering is undefined
967 self.assertRaises(TypeError, lambda: our < 1)
968 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000969 self.assertRaises(TypeError, cmp, our, 1)
970 self.assertRaises(TypeError, cmp, 1, our)
971
Guido van Rossum19960592006-08-24 17:29:38 +0000972 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +0000973
Guido van Rossum19960592006-08-24 17:29:38 +0000974 class SomeClass:
975 pass
976
977 their = SomeClass()
978 self.assertEqual(our == their, False)
979 self.assertEqual(their == our, False)
980 self.assertEqual(our != their, True)
981 self.assertEqual(their != our, True)
982 self.assertRaises(TypeError, lambda: our < their)
983 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000984 self.assertRaises(TypeError, cmp, our, their)
Guido van Rossum19960592006-08-24 17:29:38 +0000985 self.assertRaises(TypeError, cmp, their, our)
Tim Peters8d81a012003-01-24 22:36:34 +0000986
Guido van Rossum19960592006-08-24 17:29:38 +0000987 # However, if the other class explicitly defines ordering
988 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +0000989
Guido van Rossum19960592006-08-24 17:29:38 +0000990 class LargerThanAnything:
991 def __lt__(self, other):
992 return False
993 def __le__(self, other):
994 return isinstance(other, LargerThanAnything)
995 def __eq__(self, other):
996 return isinstance(other, LargerThanAnything)
997 def __ne__(self, other):
998 return not isinstance(other, LargerThanAnything)
999 def __gt__(self, other):
1000 return not isinstance(other, LargerThanAnything)
1001 def __ge__(self, other):
1002 return True
1003
1004 their = LargerThanAnything()
1005 self.assertEqual(our == their, False)
1006 self.assertEqual(their == our, False)
1007 self.assertEqual(our != their, True)
1008 self.assertEqual(their != our, True)
1009 self.assertEqual(our < their, True)
1010 self.assertEqual(their < our, False)
1011 self.assertEqual(cmp(our, their), -1)
1012 self.assertEqual(cmp(their, our), 1)
Tim Peters8d81a012003-01-24 22:36:34 +00001013
Tim Peters2a799bf2002-12-16 20:18:38 +00001014 def test_bool(self):
1015 # All dates are considered true.
1016 self.failUnless(self.theclass.min)
1017 self.failUnless(self.theclass.max)
1018
Tim Petersd6844152002-12-22 20:58:42 +00001019 def test_srftime_out_of_range(self):
1020 # For nasty technical reasons, we can't handle years before 1900.
1021 cls = self.theclass
1022 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1023 for y in 1, 49, 51, 99, 100, 1000, 1899:
1024 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001025
1026 def test_replace(self):
1027 cls = self.theclass
1028 args = [1, 2, 3]
1029 base = cls(*args)
1030 self.assertEqual(base, base.replace())
1031
1032 i = 0
1033 for name, newval in (("year", 2),
1034 ("month", 3),
1035 ("day", 4)):
1036 newargs = args[:]
1037 newargs[i] = newval
1038 expected = cls(*newargs)
1039 got = base.replace(**{name: newval})
1040 self.assertEqual(expected, got)
1041 i += 1
1042
1043 # Out of bounds.
1044 base = cls(2000, 2, 29)
1045 self.assertRaises(ValueError, base.replace, year=2001)
1046
Tim Petersa98924a2003-05-17 05:55:19 +00001047 def test_subclass_date(self):
1048
1049 class C(self.theclass):
1050 theAnswer = 42
1051
1052 def __new__(cls, *args, **kws):
1053 temp = kws.copy()
1054 extra = temp.pop('extra')
1055 result = self.theclass.__new__(cls, *args, **temp)
1056 result.extra = extra
1057 return result
1058
1059 def newmeth(self, start):
1060 return start + self.year + self.month
1061
1062 args = 2003, 4, 14
1063
1064 dt1 = self.theclass(*args)
1065 dt2 = C(*args, **{'extra': 7})
1066
1067 self.assertEqual(dt2.__class__, C)
1068 self.assertEqual(dt2.theAnswer, 42)
1069 self.assertEqual(dt2.extra, 7)
1070 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1071 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1072
Tim Peters604c0132004-06-07 23:04:33 +00001073 def test_pickling_subclass_date(self):
1074
1075 args = 6, 7, 23
1076 orig = SubclassDate(*args)
1077 for pickler, unpickler, proto in pickle_choices:
1078 green = pickler.dumps(orig, proto)
1079 derived = unpickler.loads(green)
1080 self.assertEqual(orig, derived)
1081
Tim Peters3f606292004-03-21 23:38:41 +00001082 def test_backdoor_resistance(self):
1083 # For fast unpickling, the constructor accepts a pickle string.
1084 # This is a low-overhead backdoor. A user can (by intent or
1085 # mistake) pass a string directly, which (if it's the right length)
1086 # will get treated like a pickle, and bypass the normal sanity
1087 # checks in the constructor. This can create insane objects.
1088 # The constructor doesn't want to burn the time to validate all
1089 # fields, but does check the month field. This stops, e.g.,
1090 # datetime.datetime('1995-03-25') from yielding an insane object.
1091 base = '1995-03-25'
1092 if not issubclass(self.theclass, datetime):
1093 base = base[:4]
1094 for month_byte in '9', chr(0), chr(13), '\xff':
1095 self.assertRaises(TypeError, self.theclass,
1096 base[:2] + month_byte + base[3:])
1097 for ord_byte in range(1, 13):
1098 # This shouldn't blow up because of the month byte alone. If
1099 # the implementation changes to do more-careful checking, it may
1100 # blow up because other fields are insane.
1101 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001102
Tim Peters2a799bf2002-12-16 20:18:38 +00001103#############################################################################
1104# datetime tests
1105
Tim Peters604c0132004-06-07 23:04:33 +00001106class SubclassDatetime(datetime):
1107 sub_var = 1
1108
Tim Peters2a799bf2002-12-16 20:18:38 +00001109class TestDateTime(TestDate):
1110
1111 theclass = datetime
1112
1113 def test_basic_attributes(self):
1114 dt = self.theclass(2002, 3, 1, 12, 0)
1115 self.assertEqual(dt.year, 2002)
1116 self.assertEqual(dt.month, 3)
1117 self.assertEqual(dt.day, 1)
1118 self.assertEqual(dt.hour, 12)
1119 self.assertEqual(dt.minute, 0)
1120 self.assertEqual(dt.second, 0)
1121 self.assertEqual(dt.microsecond, 0)
1122
1123 def test_basic_attributes_nonzero(self):
1124 # Make sure all attributes are non-zero so bugs in
1125 # bit-shifting access show up.
1126 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1127 self.assertEqual(dt.year, 2002)
1128 self.assertEqual(dt.month, 3)
1129 self.assertEqual(dt.day, 1)
1130 self.assertEqual(dt.hour, 12)
1131 self.assertEqual(dt.minute, 59)
1132 self.assertEqual(dt.second, 59)
1133 self.assertEqual(dt.microsecond, 8000)
1134
1135 def test_roundtrip(self):
1136 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1137 self.theclass.now()):
1138 # Verify dt -> string -> datetime identity.
1139 s = repr(dt)
1140 self.failUnless(s.startswith('datetime.'))
1141 s = s[9:]
1142 dt2 = eval(s)
1143 self.assertEqual(dt, dt2)
1144
1145 # Verify identity via reconstructing from pieces.
1146 dt2 = self.theclass(dt.year, dt.month, dt.day,
1147 dt.hour, dt.minute, dt.second,
1148 dt.microsecond)
1149 self.assertEqual(dt, dt2)
1150
1151 def test_isoformat(self):
1152 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1153 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1154 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1155 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1156 # str is ISO format with the separator forced to a blank.
1157 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1158
1159 t = self.theclass(2, 3, 2)
1160 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1161 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1162 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1163 # str is ISO format with the separator forced to a blank.
1164 self.assertEqual(str(t), "0002-03-02 00:00:00")
1165
1166 def test_more_ctime(self):
1167 # Test fields that TestDate doesn't touch.
1168 import time
1169
1170 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1171 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1172 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1173 # out. The difference is that t.ctime() produces " 2" for the day,
1174 # but platform ctime() produces "02" for the day. According to
1175 # C99, t.ctime() is correct here.
1176 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1177
1178 # So test a case where that difference doesn't matter.
1179 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1180 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1181
1182 def test_tz_independent_comparing(self):
1183 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1184 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1185 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1186 self.assertEqual(dt1, dt3)
1187 self.assert_(dt2 > dt3)
1188
1189 # Make sure comparison doesn't forget microseconds, and isn't done
1190 # via comparing a float timestamp (an IEEE double doesn't have enough
1191 # precision to span microsecond resolution across years 1 thru 9999,
1192 # so comparing via timestamp necessarily calls some distinct values
1193 # equal).
1194 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1195 us = timedelta(microseconds=1)
1196 dt2 = dt1 + us
1197 self.assertEqual(dt2 - dt1, us)
1198 self.assert_(dt1 < dt2)
1199
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001200 def test_strftime_with_bad_tzname_replace(self):
1201 # verify ok if tzinfo.tzname().replace() returns a non-string
1202 class MyTzInfo(FixedOffset):
1203 def tzname(self, dt):
1204 class MyStr(str):
1205 def replace(self, *args):
1206 return None
1207 return MyStr('name')
1208 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1209 self.assertRaises(TypeError, t.strftime, '%Z')
1210
Tim Peters2a799bf2002-12-16 20:18:38 +00001211 def test_bad_constructor_arguments(self):
1212 # bad years
1213 self.theclass(MINYEAR, 1, 1) # no exception
1214 self.theclass(MAXYEAR, 1, 1) # no exception
1215 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1216 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1217 # bad months
1218 self.theclass(2000, 1, 1) # no exception
1219 self.theclass(2000, 12, 1) # no exception
1220 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1221 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1222 # bad days
1223 self.theclass(2000, 2, 29) # no exception
1224 self.theclass(2004, 2, 29) # no exception
1225 self.theclass(2400, 2, 29) # no exception
1226 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1227 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1228 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1229 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1230 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1231 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1232 # bad hours
1233 self.theclass(2000, 1, 31, 0) # no exception
1234 self.theclass(2000, 1, 31, 23) # no exception
1235 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1236 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1237 # bad minutes
1238 self.theclass(2000, 1, 31, 23, 0) # no exception
1239 self.theclass(2000, 1, 31, 23, 59) # no exception
1240 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1241 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1242 # bad seconds
1243 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1244 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1245 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1246 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1247 # bad microseconds
1248 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1249 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1250 self.assertRaises(ValueError, self.theclass,
1251 2000, 1, 31, 23, 59, 59, -1)
1252 self.assertRaises(ValueError, self.theclass,
1253 2000, 1, 31, 23, 59, 59,
1254 1000000)
1255
1256 def test_hash_equality(self):
1257 d = self.theclass(2000, 12, 31, 23, 30, 17)
1258 e = self.theclass(2000, 12, 31, 23, 30, 17)
1259 self.assertEqual(d, e)
1260 self.assertEqual(hash(d), hash(e))
1261
1262 dic = {d: 1}
1263 dic[e] = 2
1264 self.assertEqual(len(dic), 1)
1265 self.assertEqual(dic[d], 2)
1266 self.assertEqual(dic[e], 2)
1267
1268 d = self.theclass(2001, 1, 1, 0, 5, 17)
1269 e = self.theclass(2001, 1, 1, 0, 5, 17)
1270 self.assertEqual(d, e)
1271 self.assertEqual(hash(d), hash(e))
1272
1273 dic = {d: 1}
1274 dic[e] = 2
1275 self.assertEqual(len(dic), 1)
1276 self.assertEqual(dic[d], 2)
1277 self.assertEqual(dic[e], 2)
1278
1279 def test_computations(self):
1280 a = self.theclass(2002, 1, 31)
1281 b = self.theclass(1956, 1, 31)
1282 diff = a-b
1283 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1284 self.assertEqual(diff.seconds, 0)
1285 self.assertEqual(diff.microseconds, 0)
1286 a = self.theclass(2002, 3, 2, 17, 6)
1287 millisec = timedelta(0, 0, 1000)
1288 hour = timedelta(0, 3600)
1289 day = timedelta(1)
1290 week = timedelta(7)
1291 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1292 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1293 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1294 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1295 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1296 self.assertEqual(a - hour, a + -hour)
1297 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1298 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1299 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1300 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1301 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1302 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1303 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1304 self.assertEqual((a + week) - a, week)
1305 self.assertEqual((a + day) - a, day)
1306 self.assertEqual((a + hour) - a, hour)
1307 self.assertEqual((a + millisec) - a, millisec)
1308 self.assertEqual((a - week) - a, -week)
1309 self.assertEqual((a - day) - a, -day)
1310 self.assertEqual((a - hour) - a, -hour)
1311 self.assertEqual((a - millisec) - a, -millisec)
1312 self.assertEqual(a - (a + week), -week)
1313 self.assertEqual(a - (a + day), -day)
1314 self.assertEqual(a - (a + hour), -hour)
1315 self.assertEqual(a - (a + millisec), -millisec)
1316 self.assertEqual(a - (a - week), week)
1317 self.assertEqual(a - (a - day), day)
1318 self.assertEqual(a - (a - hour), hour)
1319 self.assertEqual(a - (a - millisec), millisec)
1320 self.assertEqual(a + (week + day + hour + millisec),
1321 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1322 self.assertEqual(a + (week + day + hour + millisec),
1323 (((a + week) + day) + hour) + millisec)
1324 self.assertEqual(a - (week + day + hour + millisec),
1325 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1326 self.assertEqual(a - (week + day + hour + millisec),
1327 (((a - week) - day) - hour) - millisec)
1328 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001329 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001330 self.assertRaises(TypeError, lambda: a+i)
1331 self.assertRaises(TypeError, lambda: a-i)
1332 self.assertRaises(TypeError, lambda: i+a)
1333 self.assertRaises(TypeError, lambda: i-a)
1334
1335 # delta - datetime is senseless.
1336 self.assertRaises(TypeError, lambda: day - a)
1337 # mixing datetime and (delta or datetime) via * or // is senseless
1338 self.assertRaises(TypeError, lambda: day * a)
1339 self.assertRaises(TypeError, lambda: a * day)
1340 self.assertRaises(TypeError, lambda: day // a)
1341 self.assertRaises(TypeError, lambda: a // day)
1342 self.assertRaises(TypeError, lambda: a * a)
1343 self.assertRaises(TypeError, lambda: a // a)
1344 # datetime + datetime is senseless
1345 self.assertRaises(TypeError, lambda: a + a)
1346
1347 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001348 args = 6, 7, 23, 20, 59, 1, 64**2
1349 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001350 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001351 green = pickler.dumps(orig, proto)
1352 derived = unpickler.loads(green)
1353 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001354
Guido van Rossum275666f2003-02-07 21:49:01 +00001355 def test_more_pickling(self):
1356 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1357 s = pickle.dumps(a)
1358 b = pickle.loads(s)
1359 self.assertEqual(b.year, 2003)
1360 self.assertEqual(b.month, 2)
1361 self.assertEqual(b.day, 7)
1362
Tim Peters604c0132004-06-07 23:04:33 +00001363 def test_pickling_subclass_datetime(self):
1364 args = 6, 7, 23, 20, 59, 1, 64**2
1365 orig = SubclassDatetime(*args)
1366 for pickler, unpickler, proto in pickle_choices:
1367 green = pickler.dumps(orig, proto)
1368 derived = unpickler.loads(green)
1369 self.assertEqual(orig, derived)
1370
Tim Peters2a799bf2002-12-16 20:18:38 +00001371 def test_more_compare(self):
1372 # The test_compare() inherited from TestDate covers the error cases.
1373 # We just want to test lexicographic ordering on the members datetime
1374 # has that date lacks.
1375 args = [2000, 11, 29, 20, 58, 16, 999998]
1376 t1 = self.theclass(*args)
1377 t2 = self.theclass(*args)
1378 self.failUnless(t1 == t2)
1379 self.failUnless(t1 <= t2)
1380 self.failUnless(t1 >= t2)
1381 self.failUnless(not t1 != t2)
1382 self.failUnless(not t1 < t2)
1383 self.failUnless(not t1 > t2)
1384 self.assertEqual(cmp(t1, t2), 0)
1385 self.assertEqual(cmp(t2, t1), 0)
1386
1387 for i in range(len(args)):
1388 newargs = args[:]
1389 newargs[i] = args[i] + 1
1390 t2 = self.theclass(*newargs) # this is larger than t1
1391 self.failUnless(t1 < t2)
1392 self.failUnless(t2 > t1)
1393 self.failUnless(t1 <= t2)
1394 self.failUnless(t2 >= t1)
1395 self.failUnless(t1 != t2)
1396 self.failUnless(t2 != t1)
1397 self.failUnless(not t1 == t2)
1398 self.failUnless(not t2 == t1)
1399 self.failUnless(not t1 > t2)
1400 self.failUnless(not t2 < t1)
1401 self.failUnless(not t1 >= t2)
1402 self.failUnless(not t2 <= t1)
1403 self.assertEqual(cmp(t1, t2), -1)
1404 self.assertEqual(cmp(t2, t1), 1)
1405
1406
1407 # A helper for timestamp constructor tests.
1408 def verify_field_equality(self, expected, got):
1409 self.assertEqual(expected.tm_year, got.year)
1410 self.assertEqual(expected.tm_mon, got.month)
1411 self.assertEqual(expected.tm_mday, got.day)
1412 self.assertEqual(expected.tm_hour, got.hour)
1413 self.assertEqual(expected.tm_min, got.minute)
1414 self.assertEqual(expected.tm_sec, got.second)
1415
1416 def test_fromtimestamp(self):
1417 import time
1418
1419 ts = time.time()
1420 expected = time.localtime(ts)
1421 got = self.theclass.fromtimestamp(ts)
1422 self.verify_field_equality(expected, got)
1423
1424 def test_utcfromtimestamp(self):
1425 import time
1426
1427 ts = time.time()
1428 expected = time.gmtime(ts)
1429 got = self.theclass.utcfromtimestamp(ts)
1430 self.verify_field_equality(expected, got)
1431
Thomas Wouters477c8d52006-05-27 19:21:47 +00001432 def test_microsecond_rounding(self):
1433 # Test whether fromtimestamp "rounds up" floats that are less
1434 # than one microsecond smaller than an integer.
1435 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1436 self.theclass.fromtimestamp(1))
1437
Tim Peters1b6f7a92004-06-20 02:50:16 +00001438 def test_insane_fromtimestamp(self):
1439 # It's possible that some platform maps time_t to double,
1440 # and that this test will fail there. This test should
1441 # exempt such platforms (provided they return reasonable
1442 # results!).
1443 for insane in -1e200, 1e200:
1444 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1445 insane)
1446
1447 def test_insane_utcfromtimestamp(self):
1448 # It's possible that some platform maps time_t to double,
1449 # and that this test will fail there. This test should
1450 # exempt such platforms (provided they return reasonable
1451 # results!).
1452 for insane in -1e200, 1e200:
1453 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1454 insane)
1455
Guido van Rossumd8faa362007-04-27 19:54:29 +00001456 def test_negative_float_fromtimestamp(self):
1457 # Windows doesn't accept negative timestamps
1458 if os.name == "nt":
1459 return
1460 # The result is tz-dependent; at least test that this doesn't
1461 # fail (like it did before bug 1646728 was fixed).
1462 self.theclass.fromtimestamp(-1.05)
1463
1464 def test_negative_float_utcfromtimestamp(self):
1465 # Windows doesn't accept negative timestamps
1466 if os.name == "nt":
1467 return
1468 d = self.theclass.utcfromtimestamp(-1.05)
1469 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1470
Tim Peters2a799bf2002-12-16 20:18:38 +00001471 def test_utcnow(self):
1472 import time
1473
1474 # Call it a success if utcnow() and utcfromtimestamp() are within
1475 # a second of each other.
1476 tolerance = timedelta(seconds=1)
1477 for dummy in range(3):
1478 from_now = self.theclass.utcnow()
1479 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1480 if abs(from_timestamp - from_now) <= tolerance:
1481 break
1482 # Else try again a few times.
1483 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1484
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001485 def test_strptime(self):
1486 import time
1487
1488 string = '2004-12-01 13:02:47'
1489 format = '%Y-%m-%d %H:%M:%S'
1490 expected = self.theclass(*(time.strptime(string, format)[0:6]))
1491 got = self.theclass.strptime(string, format)
1492 self.assertEqual(expected, got)
1493
Tim Peters2a799bf2002-12-16 20:18:38 +00001494 def test_more_timetuple(self):
1495 # This tests fields beyond those tested by the TestDate.test_timetuple.
1496 t = self.theclass(2004, 12, 31, 6, 22, 33)
1497 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1498 self.assertEqual(t.timetuple(),
1499 (t.year, t.month, t.day,
1500 t.hour, t.minute, t.second,
1501 t.weekday(),
1502 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1503 -1))
1504 tt = t.timetuple()
1505 self.assertEqual(tt.tm_year, t.year)
1506 self.assertEqual(tt.tm_mon, t.month)
1507 self.assertEqual(tt.tm_mday, t.day)
1508 self.assertEqual(tt.tm_hour, t.hour)
1509 self.assertEqual(tt.tm_min, t.minute)
1510 self.assertEqual(tt.tm_sec, t.second)
1511 self.assertEqual(tt.tm_wday, t.weekday())
1512 self.assertEqual(tt.tm_yday, t.toordinal() -
1513 date(t.year, 1, 1).toordinal() + 1)
1514 self.assertEqual(tt.tm_isdst, -1)
1515
1516 def test_more_strftime(self):
1517 # This tests fields beyond those tested by the TestDate.test_strftime.
1518 t = self.theclass(2004, 12, 31, 6, 22, 33)
1519 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1520 "12 31 04 33 22 06 366")
1521
1522 def test_extract(self):
1523 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1524 self.assertEqual(dt.date(), date(2002, 3, 4))
1525 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1526
1527 def test_combine(self):
1528 d = date(2002, 3, 4)
1529 t = time(18, 45, 3, 1234)
1530 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1531 combine = self.theclass.combine
1532 dt = combine(d, t)
1533 self.assertEqual(dt, expected)
1534
1535 dt = combine(time=t, date=d)
1536 self.assertEqual(dt, expected)
1537
1538 self.assertEqual(d, dt.date())
1539 self.assertEqual(t, dt.time())
1540 self.assertEqual(dt, combine(dt.date(), dt.time()))
1541
1542 self.assertRaises(TypeError, combine) # need an arg
1543 self.assertRaises(TypeError, combine, d) # need two args
1544 self.assertRaises(TypeError, combine, t, d) # args reversed
1545 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1546 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1547
Tim Peters12bf3392002-12-24 05:41:27 +00001548 def test_replace(self):
1549 cls = self.theclass
1550 args = [1, 2, 3, 4, 5, 6, 7]
1551 base = cls(*args)
1552 self.assertEqual(base, base.replace())
1553
1554 i = 0
1555 for name, newval in (("year", 2),
1556 ("month", 3),
1557 ("day", 4),
1558 ("hour", 5),
1559 ("minute", 6),
1560 ("second", 7),
1561 ("microsecond", 8)):
1562 newargs = args[:]
1563 newargs[i] = newval
1564 expected = cls(*newargs)
1565 got = base.replace(**{name: newval})
1566 self.assertEqual(expected, got)
1567 i += 1
1568
1569 # Out of bounds.
1570 base = cls(2000, 2, 29)
1571 self.assertRaises(ValueError, base.replace, year=2001)
1572
Tim Peters80475bb2002-12-25 07:40:55 +00001573 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001574 # Pretty boring! The TZ test is more interesting here. astimezone()
1575 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001576 dt = self.theclass.now()
1577 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001578 self.assertRaises(TypeError, dt.astimezone) # not enough args
1579 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1580 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001581 self.assertRaises(ValueError, dt.astimezone, f) # naive
1582 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001583
Tim Peters52dcce22003-01-23 16:36:11 +00001584 class Bogus(tzinfo):
1585 def utcoffset(self, dt): return None
1586 def dst(self, dt): return timedelta(0)
1587 bog = Bogus()
1588 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1589
1590 class AlsoBogus(tzinfo):
1591 def utcoffset(self, dt): return timedelta(0)
1592 def dst(self, dt): return None
1593 alsobog = AlsoBogus()
1594 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001595
Tim Petersa98924a2003-05-17 05:55:19 +00001596 def test_subclass_datetime(self):
1597
1598 class C(self.theclass):
1599 theAnswer = 42
1600
1601 def __new__(cls, *args, **kws):
1602 temp = kws.copy()
1603 extra = temp.pop('extra')
1604 result = self.theclass.__new__(cls, *args, **temp)
1605 result.extra = extra
1606 return result
1607
1608 def newmeth(self, start):
1609 return start + self.year + self.month + self.second
1610
1611 args = 2003, 4, 14, 12, 13, 41
1612
1613 dt1 = self.theclass(*args)
1614 dt2 = C(*args, **{'extra': 7})
1615
1616 self.assertEqual(dt2.__class__, C)
1617 self.assertEqual(dt2.theAnswer, 42)
1618 self.assertEqual(dt2.extra, 7)
1619 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1620 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1621 dt1.second - 7)
1622
Tim Peters604c0132004-06-07 23:04:33 +00001623class SubclassTime(time):
1624 sub_var = 1
1625
Guido van Rossumd8faa362007-04-27 19:54:29 +00001626class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001627
1628 theclass = time
1629
1630 def test_basic_attributes(self):
1631 t = self.theclass(12, 0)
1632 self.assertEqual(t.hour, 12)
1633 self.assertEqual(t.minute, 0)
1634 self.assertEqual(t.second, 0)
1635 self.assertEqual(t.microsecond, 0)
1636
1637 def test_basic_attributes_nonzero(self):
1638 # Make sure all attributes are non-zero so bugs in
1639 # bit-shifting access show up.
1640 t = self.theclass(12, 59, 59, 8000)
1641 self.assertEqual(t.hour, 12)
1642 self.assertEqual(t.minute, 59)
1643 self.assertEqual(t.second, 59)
1644 self.assertEqual(t.microsecond, 8000)
1645
1646 def test_roundtrip(self):
1647 t = self.theclass(1, 2, 3, 4)
1648
1649 # Verify t -> string -> time identity.
1650 s = repr(t)
1651 self.failUnless(s.startswith('datetime.'))
1652 s = s[9:]
1653 t2 = eval(s)
1654 self.assertEqual(t, t2)
1655
1656 # Verify identity via reconstructing from pieces.
1657 t2 = self.theclass(t.hour, t.minute, t.second,
1658 t.microsecond)
1659 self.assertEqual(t, t2)
1660
1661 def test_comparing(self):
1662 args = [1, 2, 3, 4]
1663 t1 = self.theclass(*args)
1664 t2 = self.theclass(*args)
1665 self.failUnless(t1 == t2)
1666 self.failUnless(t1 <= t2)
1667 self.failUnless(t1 >= t2)
1668 self.failUnless(not t1 != t2)
1669 self.failUnless(not t1 < t2)
1670 self.failUnless(not t1 > t2)
1671 self.assertEqual(cmp(t1, t2), 0)
1672 self.assertEqual(cmp(t2, t1), 0)
1673
1674 for i in range(len(args)):
1675 newargs = args[:]
1676 newargs[i] = args[i] + 1
1677 t2 = self.theclass(*newargs) # this is larger than t1
1678 self.failUnless(t1 < t2)
1679 self.failUnless(t2 > t1)
1680 self.failUnless(t1 <= t2)
1681 self.failUnless(t2 >= t1)
1682 self.failUnless(t1 != t2)
1683 self.failUnless(t2 != t1)
1684 self.failUnless(not t1 == t2)
1685 self.failUnless(not t2 == t1)
1686 self.failUnless(not t1 > t2)
1687 self.failUnless(not t2 < t1)
1688 self.failUnless(not t1 >= t2)
1689 self.failUnless(not t2 <= t1)
1690 self.assertEqual(cmp(t1, t2), -1)
1691 self.assertEqual(cmp(t2, t1), 1)
1692
Tim Peters68124bb2003-02-08 03:46:31 +00001693 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001694 self.assertEqual(t1 == badarg, False)
1695 self.assertEqual(t1 != badarg, True)
1696 self.assertEqual(badarg == t1, False)
1697 self.assertEqual(badarg != t1, True)
1698
Tim Peters2a799bf2002-12-16 20:18:38 +00001699 self.assertRaises(TypeError, lambda: t1 <= badarg)
1700 self.assertRaises(TypeError, lambda: t1 < badarg)
1701 self.assertRaises(TypeError, lambda: t1 > badarg)
1702 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001703 self.assertRaises(TypeError, lambda: badarg <= t1)
1704 self.assertRaises(TypeError, lambda: badarg < t1)
1705 self.assertRaises(TypeError, lambda: badarg > t1)
1706 self.assertRaises(TypeError, lambda: badarg >= t1)
1707
1708 def test_bad_constructor_arguments(self):
1709 # bad hours
1710 self.theclass(0, 0) # no exception
1711 self.theclass(23, 0) # no exception
1712 self.assertRaises(ValueError, self.theclass, -1, 0)
1713 self.assertRaises(ValueError, self.theclass, 24, 0)
1714 # bad minutes
1715 self.theclass(23, 0) # no exception
1716 self.theclass(23, 59) # no exception
1717 self.assertRaises(ValueError, self.theclass, 23, -1)
1718 self.assertRaises(ValueError, self.theclass, 23, 60)
1719 # bad seconds
1720 self.theclass(23, 59, 0) # no exception
1721 self.theclass(23, 59, 59) # no exception
1722 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1723 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1724 # bad microseconds
1725 self.theclass(23, 59, 59, 0) # no exception
1726 self.theclass(23, 59, 59, 999999) # no exception
1727 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1728 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1729
1730 def test_hash_equality(self):
1731 d = self.theclass(23, 30, 17)
1732 e = self.theclass(23, 30, 17)
1733 self.assertEqual(d, e)
1734 self.assertEqual(hash(d), hash(e))
1735
1736 dic = {d: 1}
1737 dic[e] = 2
1738 self.assertEqual(len(dic), 1)
1739 self.assertEqual(dic[d], 2)
1740 self.assertEqual(dic[e], 2)
1741
1742 d = self.theclass(0, 5, 17)
1743 e = self.theclass(0, 5, 17)
1744 self.assertEqual(d, e)
1745 self.assertEqual(hash(d), hash(e))
1746
1747 dic = {d: 1}
1748 dic[e] = 2
1749 self.assertEqual(len(dic), 1)
1750 self.assertEqual(dic[d], 2)
1751 self.assertEqual(dic[e], 2)
1752
1753 def test_isoformat(self):
1754 t = self.theclass(4, 5, 1, 123)
1755 self.assertEqual(t.isoformat(), "04:05:01.000123")
1756 self.assertEqual(t.isoformat(), str(t))
1757
1758 t = self.theclass()
1759 self.assertEqual(t.isoformat(), "00:00:00")
1760 self.assertEqual(t.isoformat(), str(t))
1761
1762 t = self.theclass(microsecond=1)
1763 self.assertEqual(t.isoformat(), "00:00:00.000001")
1764 self.assertEqual(t.isoformat(), str(t))
1765
1766 t = self.theclass(microsecond=10)
1767 self.assertEqual(t.isoformat(), "00:00:00.000010")
1768 self.assertEqual(t.isoformat(), str(t))
1769
1770 t = self.theclass(microsecond=100)
1771 self.assertEqual(t.isoformat(), "00:00:00.000100")
1772 self.assertEqual(t.isoformat(), str(t))
1773
1774 t = self.theclass(microsecond=1000)
1775 self.assertEqual(t.isoformat(), "00:00:00.001000")
1776 self.assertEqual(t.isoformat(), str(t))
1777
1778 t = self.theclass(microsecond=10000)
1779 self.assertEqual(t.isoformat(), "00:00:00.010000")
1780 self.assertEqual(t.isoformat(), str(t))
1781
1782 t = self.theclass(microsecond=100000)
1783 self.assertEqual(t.isoformat(), "00:00:00.100000")
1784 self.assertEqual(t.isoformat(), str(t))
1785
Thomas Wouterscf297e42007-02-23 15:07:44 +00001786 def test_1653736(self):
1787 # verify it doesn't accept extra keyword arguments
1788 t = self.theclass(second=1)
1789 self.assertRaises(TypeError, t.isoformat, foo=3)
1790
Tim Peters2a799bf2002-12-16 20:18:38 +00001791 def test_strftime(self):
1792 t = self.theclass(1, 2, 3, 4)
1793 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1794 # A naive object replaces %z and %Z with empty strings.
1795 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1796
1797 def test_str(self):
1798 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1799 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1800 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1801 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1802 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1803
1804 def test_repr(self):
1805 name = 'datetime.' + self.theclass.__name__
1806 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1807 "%s(1, 2, 3, 4)" % name)
1808 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1809 "%s(10, 2, 3, 4000)" % name)
1810 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1811 "%s(0, 2, 3, 400000)" % name)
1812 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1813 "%s(12, 2, 3)" % name)
1814 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1815 "%s(23, 15)" % name)
1816
1817 def test_resolution_info(self):
1818 self.assert_(isinstance(self.theclass.min, self.theclass))
1819 self.assert_(isinstance(self.theclass.max, self.theclass))
1820 self.assert_(isinstance(self.theclass.resolution, timedelta))
1821 self.assert_(self.theclass.max > self.theclass.min)
1822
1823 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001824 args = 20, 59, 16, 64**2
1825 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001826 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001827 green = pickler.dumps(orig, proto)
1828 derived = unpickler.loads(green)
1829 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001830
Tim Peters604c0132004-06-07 23:04:33 +00001831 def test_pickling_subclass_time(self):
1832 args = 20, 59, 16, 64**2
1833 orig = SubclassTime(*args)
1834 for pickler, unpickler, proto in pickle_choices:
1835 green = pickler.dumps(orig, proto)
1836 derived = unpickler.loads(green)
1837 self.assertEqual(orig, derived)
1838
Tim Peters2a799bf2002-12-16 20:18:38 +00001839 def test_bool(self):
1840 cls = self.theclass
1841 self.failUnless(cls(1))
1842 self.failUnless(cls(0, 1))
1843 self.failUnless(cls(0, 0, 1))
1844 self.failUnless(cls(0, 0, 0, 1))
1845 self.failUnless(not cls(0))
1846 self.failUnless(not cls())
1847
Tim Peters12bf3392002-12-24 05:41:27 +00001848 def test_replace(self):
1849 cls = self.theclass
1850 args = [1, 2, 3, 4]
1851 base = cls(*args)
1852 self.assertEqual(base, base.replace())
1853
1854 i = 0
1855 for name, newval in (("hour", 5),
1856 ("minute", 6),
1857 ("second", 7),
1858 ("microsecond", 8)):
1859 newargs = args[:]
1860 newargs[i] = newval
1861 expected = cls(*newargs)
1862 got = base.replace(**{name: newval})
1863 self.assertEqual(expected, got)
1864 i += 1
1865
1866 # Out of bounds.
1867 base = cls(1)
1868 self.assertRaises(ValueError, base.replace, hour=24)
1869 self.assertRaises(ValueError, base.replace, minute=-1)
1870 self.assertRaises(ValueError, base.replace, second=100)
1871 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1872
Tim Petersa98924a2003-05-17 05:55:19 +00001873 def test_subclass_time(self):
1874
1875 class C(self.theclass):
1876 theAnswer = 42
1877
1878 def __new__(cls, *args, **kws):
1879 temp = kws.copy()
1880 extra = temp.pop('extra')
1881 result = self.theclass.__new__(cls, *args, **temp)
1882 result.extra = extra
1883 return result
1884
1885 def newmeth(self, start):
1886 return start + self.hour + self.second
1887
1888 args = 4, 5, 6
1889
1890 dt1 = self.theclass(*args)
1891 dt2 = C(*args, **{'extra': 7})
1892
1893 self.assertEqual(dt2.__class__, C)
1894 self.assertEqual(dt2.theAnswer, 42)
1895 self.assertEqual(dt2.extra, 7)
1896 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1897 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1898
Armin Rigof4afb212005-11-07 07:15:48 +00001899 def test_backdoor_resistance(self):
1900 # see TestDate.test_backdoor_resistance().
1901 base = '2:59.0'
1902 for hour_byte in ' ', '9', chr(24), '\xff':
1903 self.assertRaises(TypeError, self.theclass,
1904 hour_byte + base[1:])
1905
Tim Peters855fe882002-12-22 03:43:39 +00001906# A mixin for classes with a tzinfo= argument. Subclasses must define
1907# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001908# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001909class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001910
Tim Petersbad8ff02002-12-30 20:52:32 +00001911 def test_argument_passing(self):
1912 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001913 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001914 class introspective(tzinfo):
1915 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001916 def utcoffset(self, dt):
1917 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001918 dst = utcoffset
1919
1920 obj = cls(1, 2, 3, tzinfo=introspective())
1921
Tim Peters0bf60bd2003-01-08 20:40:01 +00001922 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001923 self.assertEqual(obj.tzname(), expected)
1924
Tim Peters0bf60bd2003-01-08 20:40:01 +00001925 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001926 self.assertEqual(obj.utcoffset(), expected)
1927 self.assertEqual(obj.dst(), expected)
1928
Tim Peters855fe882002-12-22 03:43:39 +00001929 def test_bad_tzinfo_classes(self):
1930 cls = self.theclass
1931 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001932
Tim Peters855fe882002-12-22 03:43:39 +00001933 class NiceTry(object):
1934 def __init__(self): pass
1935 def utcoffset(self, dt): pass
1936 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1937
1938 class BetterTry(tzinfo):
1939 def __init__(self): pass
1940 def utcoffset(self, dt): pass
1941 b = BetterTry()
1942 t = cls(1, 1, 1, tzinfo=b)
1943 self.failUnless(t.tzinfo is b)
1944
1945 def test_utc_offset_out_of_bounds(self):
1946 class Edgy(tzinfo):
1947 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00001948 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00001949 def utcoffset(self, dt):
1950 return self.offset
1951
1952 cls = self.theclass
1953 for offset, legit in ((-1440, False),
1954 (-1439, True),
1955 (1439, True),
1956 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001957 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00001958 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001959 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00001960 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001961 else:
1962 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00001963 if legit:
1964 aofs = abs(offset)
1965 h, m = divmod(aofs, 60)
1966 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001967 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00001968 t = t.timetz()
1969 self.assertEqual(str(t), "01:02:03" + tag)
1970 else:
1971 self.assertRaises(ValueError, str, t)
1972
1973 def test_tzinfo_classes(self):
1974 cls = self.theclass
1975 class C1(tzinfo):
1976 def utcoffset(self, dt): return None
1977 def dst(self, dt): return None
1978 def tzname(self, dt): return None
1979 for t in (cls(1, 1, 1),
1980 cls(1, 1, 1, tzinfo=None),
1981 cls(1, 1, 1, tzinfo=C1())):
1982 self.failUnless(t.utcoffset() is None)
1983 self.failUnless(t.dst() is None)
1984 self.failUnless(t.tzname() is None)
1985
Tim Peters855fe882002-12-22 03:43:39 +00001986 class C3(tzinfo):
1987 def utcoffset(self, dt): return timedelta(minutes=-1439)
1988 def dst(self, dt): return timedelta(minutes=1439)
1989 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001990 t = cls(1, 1, 1, tzinfo=C3())
1991 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
1992 self.assertEqual(t.dst(), timedelta(minutes=1439))
1993 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00001994
1995 # Wrong types.
1996 class C4(tzinfo):
1997 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001998 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00001999 def tzname(self, dt): return 0
2000 t = cls(1, 1, 1, tzinfo=C4())
2001 self.assertRaises(TypeError, t.utcoffset)
2002 self.assertRaises(TypeError, t.dst)
2003 self.assertRaises(TypeError, t.tzname)
2004
2005 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002006 class C6(tzinfo):
2007 def utcoffset(self, dt): return timedelta(hours=-24)
2008 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002009 t = cls(1, 1, 1, tzinfo=C6())
2010 self.assertRaises(ValueError, t.utcoffset)
2011 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002012
2013 # Not a whole number of minutes.
2014 class C7(tzinfo):
2015 def utcoffset(self, dt): return timedelta(seconds=61)
2016 def dst(self, dt): return timedelta(microseconds=-81)
2017 t = cls(1, 1, 1, tzinfo=C7())
2018 self.assertRaises(ValueError, t.utcoffset)
2019 self.assertRaises(ValueError, t.dst)
2020
Tim Peters4c0db782002-12-26 05:01:19 +00002021 def test_aware_compare(self):
2022 cls = self.theclass
2023
Tim Peters60c76e42002-12-27 00:41:11 +00002024 # Ensure that utcoffset() gets ignored if the comparands have
2025 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002026 class OperandDependentOffset(tzinfo):
2027 def utcoffset(self, t):
2028 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002029 # d0 and d1 equal after adjustment
2030 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002031 else:
Tim Peters397301e2003-01-02 21:28:08 +00002032 # d2 off in the weeds
2033 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002034
2035 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2036 d0 = base.replace(minute=3)
2037 d1 = base.replace(minute=9)
2038 d2 = base.replace(minute=11)
2039 for x in d0, d1, d2:
2040 for y in d0, d1, d2:
2041 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002042 expected = cmp(x.minute, y.minute)
2043 self.assertEqual(got, expected)
2044
2045 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002046 # Note that a time can't actually have an operand-depedent offset,
2047 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2048 # so skip this test for time.
2049 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002050 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2051 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2052 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2053 for x in d0, d1, d2:
2054 for y in d0, d1, d2:
2055 got = cmp(x, y)
2056 if (x is d0 or x is d1) and (y is d0 or y is d1):
2057 expected = 0
2058 elif x is y is d2:
2059 expected = 0
2060 elif x is d2:
2061 expected = -1
2062 else:
2063 assert y is d2
2064 expected = 1
2065 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002066
Tim Peters855fe882002-12-22 03:43:39 +00002067
Tim Peters0bf60bd2003-01-08 20:40:01 +00002068# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002069class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002070 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002071
2072 def test_empty(self):
2073 t = self.theclass()
2074 self.assertEqual(t.hour, 0)
2075 self.assertEqual(t.minute, 0)
2076 self.assertEqual(t.second, 0)
2077 self.assertEqual(t.microsecond, 0)
2078 self.failUnless(t.tzinfo is None)
2079
Tim Peters2a799bf2002-12-16 20:18:38 +00002080 def test_zones(self):
2081 est = FixedOffset(-300, "EST", 1)
2082 utc = FixedOffset(0, "UTC", -2)
2083 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002084 t1 = time( 7, 47, tzinfo=est)
2085 t2 = time(12, 47, tzinfo=utc)
2086 t3 = time(13, 47, tzinfo=met)
2087 t4 = time(microsecond=40)
2088 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002089
2090 self.assertEqual(t1.tzinfo, est)
2091 self.assertEqual(t2.tzinfo, utc)
2092 self.assertEqual(t3.tzinfo, met)
2093 self.failUnless(t4.tzinfo is None)
2094 self.assertEqual(t5.tzinfo, utc)
2095
Tim Peters855fe882002-12-22 03:43:39 +00002096 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2097 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2098 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002099 self.failUnless(t4.utcoffset() is None)
2100 self.assertRaises(TypeError, t1.utcoffset, "no args")
2101
2102 self.assertEqual(t1.tzname(), "EST")
2103 self.assertEqual(t2.tzname(), "UTC")
2104 self.assertEqual(t3.tzname(), "MET")
2105 self.failUnless(t4.tzname() is None)
2106 self.assertRaises(TypeError, t1.tzname, "no args")
2107
Tim Peters855fe882002-12-22 03:43:39 +00002108 self.assertEqual(t1.dst(), timedelta(minutes=1))
2109 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2110 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002111 self.failUnless(t4.dst() is None)
2112 self.assertRaises(TypeError, t1.dst, "no args")
2113
2114 self.assertEqual(hash(t1), hash(t2))
2115 self.assertEqual(hash(t1), hash(t3))
2116 self.assertEqual(hash(t2), hash(t3))
2117
2118 self.assertEqual(t1, t2)
2119 self.assertEqual(t1, t3)
2120 self.assertEqual(t2, t3)
2121 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2122 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2123 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2124
2125 self.assertEqual(str(t1), "07:47:00-05:00")
2126 self.assertEqual(str(t2), "12:47:00+00:00")
2127 self.assertEqual(str(t3), "13:47:00+01:00")
2128 self.assertEqual(str(t4), "00:00:00.000040")
2129 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2130
2131 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2132 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2133 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2134 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2135 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2136
Tim Peters0bf60bd2003-01-08 20:40:01 +00002137 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002138 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2139 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2140 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2141 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2142 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2143
2144 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2145 "07:47:00 %Z=EST %z=-0500")
2146 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2147 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2148
2149 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002150 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002151 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2152 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2153
Tim Petersb92bb712002-12-21 17:44:07 +00002154 # Check that an invalid tzname result raises an exception.
2155 class Badtzname(tzinfo):
2156 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002157 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002158 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2159 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002160
2161 def test_hash_edge_cases(self):
2162 # Offsets that overflow a basic time.
2163 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2164 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2165 self.assertEqual(hash(t1), hash(t2))
2166
2167 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2168 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2169 self.assertEqual(hash(t1), hash(t2))
2170
Tim Peters2a799bf2002-12-16 20:18:38 +00002171 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002172 # Try one without a tzinfo.
2173 args = 20, 59, 16, 64**2
2174 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002175 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002176 green = pickler.dumps(orig, proto)
2177 derived = unpickler.loads(green)
2178 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002179
2180 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002181 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002182 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002183 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002184 green = pickler.dumps(orig, proto)
2185 derived = unpickler.loads(green)
2186 self.assertEqual(orig, derived)
2187 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2188 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2189 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002190
2191 def test_more_bool(self):
2192 # Test cases with non-None tzinfo.
2193 cls = self.theclass
2194
2195 t = cls(0, tzinfo=FixedOffset(-300, ""))
2196 self.failUnless(t)
2197
2198 t = cls(5, tzinfo=FixedOffset(-300, ""))
2199 self.failUnless(t)
2200
2201 t = cls(5, tzinfo=FixedOffset(300, ""))
2202 self.failUnless(not t)
2203
2204 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2205 self.failUnless(not t)
2206
2207 # Mostly ensuring this doesn't overflow internally.
2208 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2209 self.failUnless(t)
2210
2211 # But this should yield a value error -- the utcoffset is bogus.
2212 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2213 self.assertRaises(ValueError, lambda: bool(t))
2214
2215 # Likewise.
2216 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2217 self.assertRaises(ValueError, lambda: bool(t))
2218
Tim Peters12bf3392002-12-24 05:41:27 +00002219 def test_replace(self):
2220 cls = self.theclass
2221 z100 = FixedOffset(100, "+100")
2222 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2223 args = [1, 2, 3, 4, z100]
2224 base = cls(*args)
2225 self.assertEqual(base, base.replace())
2226
2227 i = 0
2228 for name, newval in (("hour", 5),
2229 ("minute", 6),
2230 ("second", 7),
2231 ("microsecond", 8),
2232 ("tzinfo", zm200)):
2233 newargs = args[:]
2234 newargs[i] = newval
2235 expected = cls(*newargs)
2236 got = base.replace(**{name: newval})
2237 self.assertEqual(expected, got)
2238 i += 1
2239
2240 # Ensure we can get rid of a tzinfo.
2241 self.assertEqual(base.tzname(), "+100")
2242 base2 = base.replace(tzinfo=None)
2243 self.failUnless(base2.tzinfo is None)
2244 self.failUnless(base2.tzname() is None)
2245
2246 # Ensure we can add one.
2247 base3 = base2.replace(tzinfo=z100)
2248 self.assertEqual(base, base3)
2249 self.failUnless(base.tzinfo is base3.tzinfo)
2250
2251 # Out of bounds.
2252 base = cls(1)
2253 self.assertRaises(ValueError, base.replace, hour=24)
2254 self.assertRaises(ValueError, base.replace, minute=-1)
2255 self.assertRaises(ValueError, base.replace, second=100)
2256 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2257
Tim Peters60c76e42002-12-27 00:41:11 +00002258 def test_mixed_compare(self):
2259 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002260 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002261 self.assertEqual(t1, t2)
2262 t2 = t2.replace(tzinfo=None)
2263 self.assertEqual(t1, t2)
2264 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2265 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002266 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2267 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002268
Tim Peters0bf60bd2003-01-08 20:40:01 +00002269 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002270 class Varies(tzinfo):
2271 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002272 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002273 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002274 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002275 return self.offset
2276
2277 v = Varies()
2278 t1 = t2.replace(tzinfo=v)
2279 t2 = t2.replace(tzinfo=v)
2280 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2281 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2282 self.assertEqual(t1, t2)
2283
2284 # But if they're not identical, it isn't ignored.
2285 t2 = t2.replace(tzinfo=Varies())
2286 self.failUnless(t1 < t2) # t1's offset counter still going up
2287
Tim Petersa98924a2003-05-17 05:55:19 +00002288 def test_subclass_timetz(self):
2289
2290 class C(self.theclass):
2291 theAnswer = 42
2292
2293 def __new__(cls, *args, **kws):
2294 temp = kws.copy()
2295 extra = temp.pop('extra')
2296 result = self.theclass.__new__(cls, *args, **temp)
2297 result.extra = extra
2298 return result
2299
2300 def newmeth(self, start):
2301 return start + self.hour + self.second
2302
2303 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2304
2305 dt1 = self.theclass(*args)
2306 dt2 = C(*args, **{'extra': 7})
2307
2308 self.assertEqual(dt2.__class__, C)
2309 self.assertEqual(dt2.theAnswer, 42)
2310 self.assertEqual(dt2.extra, 7)
2311 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2312 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2313
Tim Peters4c0db782002-12-26 05:01:19 +00002314
Tim Peters0bf60bd2003-01-08 20:40:01 +00002315# Testing datetime objects with a non-None tzinfo.
2316
Guido van Rossumd8faa362007-04-27 19:54:29 +00002317class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002318 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002319
2320 def test_trivial(self):
2321 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2322 self.assertEqual(dt.year, 1)
2323 self.assertEqual(dt.month, 2)
2324 self.assertEqual(dt.day, 3)
2325 self.assertEqual(dt.hour, 4)
2326 self.assertEqual(dt.minute, 5)
2327 self.assertEqual(dt.second, 6)
2328 self.assertEqual(dt.microsecond, 7)
2329 self.assertEqual(dt.tzinfo, None)
2330
2331 def test_even_more_compare(self):
2332 # The test_compare() and test_more_compare() inherited from TestDate
2333 # and TestDateTime covered non-tzinfo cases.
2334
2335 # Smallest possible after UTC adjustment.
2336 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2337 # Largest possible after UTC adjustment.
2338 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2339 tzinfo=FixedOffset(-1439, ""))
2340
2341 # Make sure those compare correctly, and w/o overflow.
2342 self.failUnless(t1 < t2)
2343 self.failUnless(t1 != t2)
2344 self.failUnless(t2 > t1)
2345
2346 self.failUnless(t1 == t1)
2347 self.failUnless(t2 == t2)
2348
2349 # Equal afer adjustment.
2350 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2351 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2352 self.assertEqual(t1, t2)
2353
2354 # Change t1 not to subtract a minute, and t1 should be larger.
2355 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2356 self.failUnless(t1 > t2)
2357
2358 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2359 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2360 self.failUnless(t1 < t2)
2361
2362 # Back to the original t1, but make seconds resolve it.
2363 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2364 second=1)
2365 self.failUnless(t1 > t2)
2366
2367 # Likewise, but make microseconds resolve it.
2368 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2369 microsecond=1)
2370 self.failUnless(t1 > t2)
2371
2372 # Make t2 naive and it should fail.
2373 t2 = self.theclass.min
2374 self.assertRaises(TypeError, lambda: t1 == t2)
2375 self.assertEqual(t2, t2)
2376
2377 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2378 class Naive(tzinfo):
2379 def utcoffset(self, dt): return None
2380 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2381 self.assertRaises(TypeError, lambda: t1 == t2)
2382 self.assertEqual(t2, t2)
2383
2384 # OTOH, it's OK to compare two of these mixing the two ways of being
2385 # naive.
2386 t1 = self.theclass(5, 6, 7)
2387 self.assertEqual(t1, t2)
2388
2389 # Try a bogus uctoffset.
2390 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002391 def utcoffset(self, dt):
2392 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002393 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2394 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002395 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002396
Tim Peters2a799bf2002-12-16 20:18:38 +00002397 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002398 # Try one without a tzinfo.
2399 args = 6, 7, 23, 20, 59, 1, 64**2
2400 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002401 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002402 green = pickler.dumps(orig, proto)
2403 derived = unpickler.loads(green)
2404 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002405
2406 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002407 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002408 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002409 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002410 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002411 green = pickler.dumps(orig, proto)
2412 derived = unpickler.loads(green)
2413 self.assertEqual(orig, derived)
2414 self.failUnless(isinstance(derived.tzinfo,
2415 PicklableFixedOffset))
2416 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2417 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002418
2419 def test_extreme_hashes(self):
2420 # If an attempt is made to hash these via subtracting the offset
2421 # then hashing a datetime object, OverflowError results. The
2422 # Python implementation used to blow up here.
2423 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2424 hash(t)
2425 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2426 tzinfo=FixedOffset(-1439, ""))
2427 hash(t)
2428
2429 # OTOH, an OOB offset should blow up.
2430 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2431 self.assertRaises(ValueError, hash, t)
2432
2433 def test_zones(self):
2434 est = FixedOffset(-300, "EST")
2435 utc = FixedOffset(0, "UTC")
2436 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002437 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2438 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2439 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002440 self.assertEqual(t1.tzinfo, est)
2441 self.assertEqual(t2.tzinfo, utc)
2442 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002443 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2444 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2445 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002446 self.assertEqual(t1.tzname(), "EST")
2447 self.assertEqual(t2.tzname(), "UTC")
2448 self.assertEqual(t3.tzname(), "MET")
2449 self.assertEqual(hash(t1), hash(t2))
2450 self.assertEqual(hash(t1), hash(t3))
2451 self.assertEqual(hash(t2), hash(t3))
2452 self.assertEqual(t1, t2)
2453 self.assertEqual(t1, t3)
2454 self.assertEqual(t2, t3)
2455 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2456 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2457 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002458 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002459 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2460 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2461 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2462
2463 def test_combine(self):
2464 met = FixedOffset(60, "MET")
2465 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002466 tz = time(18, 45, 3, 1234, tzinfo=met)
2467 dt = datetime.combine(d, tz)
2468 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002469 tzinfo=met))
2470
2471 def test_extract(self):
2472 met = FixedOffset(60, "MET")
2473 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2474 self.assertEqual(dt.date(), date(2002, 3, 4))
2475 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002476 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002477
2478 def test_tz_aware_arithmetic(self):
2479 import random
2480
2481 now = self.theclass.now()
2482 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002483 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002484 nowaware = self.theclass.combine(now.date(), timeaware)
2485 self.failUnless(nowaware.tzinfo is tz55)
2486 self.assertEqual(nowaware.timetz(), timeaware)
2487
2488 # Can't mix aware and non-aware.
2489 self.assertRaises(TypeError, lambda: now - nowaware)
2490 self.assertRaises(TypeError, lambda: nowaware - now)
2491
Tim Peters0bf60bd2003-01-08 20:40:01 +00002492 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002493 self.assertRaises(TypeError, lambda: now + nowaware)
2494 self.assertRaises(TypeError, lambda: nowaware + now)
2495 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2496
2497 # Subtracting should yield 0.
2498 self.assertEqual(now - now, timedelta(0))
2499 self.assertEqual(nowaware - nowaware, timedelta(0))
2500
2501 # Adding a delta should preserve tzinfo.
2502 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2503 nowawareplus = nowaware + delta
2504 self.failUnless(nowaware.tzinfo is tz55)
2505 nowawareplus2 = delta + nowaware
2506 self.failUnless(nowawareplus2.tzinfo is tz55)
2507 self.assertEqual(nowawareplus, nowawareplus2)
2508
2509 # that - delta should be what we started with, and that - what we
2510 # started with should be delta.
2511 diff = nowawareplus - delta
2512 self.failUnless(diff.tzinfo is tz55)
2513 self.assertEqual(nowaware, diff)
2514 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2515 self.assertEqual(nowawareplus - nowaware, delta)
2516
2517 # Make up a random timezone.
2518 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002519 # Attach it to nowawareplus.
2520 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002521 self.failUnless(nowawareplus.tzinfo is tzr)
2522 # Make sure the difference takes the timezone adjustments into account.
2523 got = nowaware - nowawareplus
2524 # Expected: (nowaware base - nowaware offset) -
2525 # (nowawareplus base - nowawareplus offset) =
2526 # (nowaware base - nowawareplus base) +
2527 # (nowawareplus offset - nowaware offset) =
2528 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002529 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002530 self.assertEqual(got, expected)
2531
2532 # Try max possible difference.
2533 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2534 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2535 tzinfo=FixedOffset(-1439, "max"))
2536 maxdiff = max - min
2537 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2538 timedelta(minutes=2*1439))
2539
2540 def test_tzinfo_now(self):
2541 meth = self.theclass.now
2542 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2543 base = meth()
2544 # Try with and without naming the keyword.
2545 off42 = FixedOffset(42, "42")
2546 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002547 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002548 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002549 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002550 # Bad argument with and w/o naming the keyword.
2551 self.assertRaises(TypeError, meth, 16)
2552 self.assertRaises(TypeError, meth, tzinfo=16)
2553 # Bad keyword name.
2554 self.assertRaises(TypeError, meth, tinfo=off42)
2555 # Too many args.
2556 self.assertRaises(TypeError, meth, off42, off42)
2557
Tim Peters10cadce2003-01-23 19:58:02 +00002558 # We don't know which time zone we're in, and don't have a tzinfo
2559 # class to represent it, so seeing whether a tz argument actually
2560 # does a conversion is tricky.
2561 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2562 utc = FixedOffset(0, "utc", 0)
2563 for dummy in range(3):
2564 now = datetime.now(weirdtz)
2565 self.failUnless(now.tzinfo is weirdtz)
2566 utcnow = datetime.utcnow().replace(tzinfo=utc)
2567 now2 = utcnow.astimezone(weirdtz)
2568 if abs(now - now2) < timedelta(seconds=30):
2569 break
2570 # Else the code is broken, or more than 30 seconds passed between
2571 # calls; assuming the latter, just try again.
2572 else:
2573 # Three strikes and we're out.
2574 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2575
Tim Peters2a799bf2002-12-16 20:18:38 +00002576 def test_tzinfo_fromtimestamp(self):
2577 import time
2578 meth = self.theclass.fromtimestamp
2579 ts = time.time()
2580 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2581 base = meth(ts)
2582 # Try with and without naming the keyword.
2583 off42 = FixedOffset(42, "42")
2584 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002585 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002586 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002587 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002588 # Bad argument with and w/o naming the keyword.
2589 self.assertRaises(TypeError, meth, ts, 16)
2590 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2591 # Bad keyword name.
2592 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2593 # Too many args.
2594 self.assertRaises(TypeError, meth, ts, off42, off42)
2595 # Too few args.
2596 self.assertRaises(TypeError, meth)
2597
Tim Peters2a44a8d2003-01-23 20:53:10 +00002598 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002599 timestamp = 1000000000
2600 utcdatetime = datetime.utcfromtimestamp(timestamp)
2601 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2602 # But on some flavor of Mac, it's nowhere near that. So we can't have
2603 # any idea here what time that actually is, we can only test that
2604 # relative changes match.
2605 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2606 tz = FixedOffset(utcoffset, "tz", 0)
2607 expected = utcdatetime + utcoffset
2608 got = datetime.fromtimestamp(timestamp, tz)
2609 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002610
Tim Peters2a799bf2002-12-16 20:18:38 +00002611 def test_tzinfo_utcnow(self):
2612 meth = self.theclass.utcnow
2613 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2614 base = meth()
2615 # Try with and without naming the keyword; for whatever reason,
2616 # utcnow() doesn't accept a tzinfo argument.
2617 off42 = FixedOffset(42, "42")
2618 self.assertRaises(TypeError, meth, off42)
2619 self.assertRaises(TypeError, meth, tzinfo=off42)
2620
2621 def test_tzinfo_utcfromtimestamp(self):
2622 import time
2623 meth = self.theclass.utcfromtimestamp
2624 ts = time.time()
2625 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2626 base = meth(ts)
2627 # Try with and without naming the keyword; for whatever reason,
2628 # utcfromtimestamp() doesn't accept a tzinfo argument.
2629 off42 = FixedOffset(42, "42")
2630 self.assertRaises(TypeError, meth, ts, off42)
2631 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2632
2633 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002634 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002635 # DST flag.
2636 class DST(tzinfo):
2637 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002638 if isinstance(dstvalue, int):
2639 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002640 self.dstvalue = dstvalue
2641 def dst(self, dt):
2642 return self.dstvalue
2643
2644 cls = self.theclass
2645 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2646 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2647 t = d.timetuple()
2648 self.assertEqual(1, t.tm_year)
2649 self.assertEqual(1, t.tm_mon)
2650 self.assertEqual(1, t.tm_mday)
2651 self.assertEqual(10, t.tm_hour)
2652 self.assertEqual(20, t.tm_min)
2653 self.assertEqual(30, t.tm_sec)
2654 self.assertEqual(0, t.tm_wday)
2655 self.assertEqual(1, t.tm_yday)
2656 self.assertEqual(flag, t.tm_isdst)
2657
2658 # dst() returns wrong type.
2659 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2660
2661 # dst() at the edge.
2662 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2663 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2664
2665 # dst() out of range.
2666 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2667 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2668
2669 def test_utctimetuple(self):
2670 class DST(tzinfo):
2671 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002672 if isinstance(dstvalue, int):
2673 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002674 self.dstvalue = dstvalue
2675 def dst(self, dt):
2676 return self.dstvalue
2677
2678 cls = self.theclass
2679 # This can't work: DST didn't implement utcoffset.
2680 self.assertRaises(NotImplementedError,
2681 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2682
2683 class UOFS(DST):
2684 def __init__(self, uofs, dofs=None):
2685 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002686 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002687 def utcoffset(self, dt):
2688 return self.uofs
2689
2690 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2691 # in effect for a UTC time.
2692 for dstvalue in -33, 33, 0, None:
2693 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2694 t = d.utctimetuple()
2695 self.assertEqual(d.year, t.tm_year)
2696 self.assertEqual(d.month, t.tm_mon)
2697 self.assertEqual(d.day, t.tm_mday)
2698 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2699 self.assertEqual(13, t.tm_min)
2700 self.assertEqual(d.second, t.tm_sec)
2701 self.assertEqual(d.weekday(), t.tm_wday)
2702 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2703 t.tm_yday)
2704 self.assertEqual(0, t.tm_isdst)
2705
2706 # At the edges, UTC adjustment can normalize into years out-of-range
2707 # for a datetime object. Ensure that a correct timetuple is
2708 # created anyway.
2709 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2710 # That goes back 1 minute less than a full day.
2711 t = tiny.utctimetuple()
2712 self.assertEqual(t.tm_year, MINYEAR-1)
2713 self.assertEqual(t.tm_mon, 12)
2714 self.assertEqual(t.tm_mday, 31)
2715 self.assertEqual(t.tm_hour, 0)
2716 self.assertEqual(t.tm_min, 1)
2717 self.assertEqual(t.tm_sec, 37)
2718 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2719 self.assertEqual(t.tm_isdst, 0)
2720
2721 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2722 # That goes forward 1 minute less than a full day.
2723 t = huge.utctimetuple()
2724 self.assertEqual(t.tm_year, MAXYEAR+1)
2725 self.assertEqual(t.tm_mon, 1)
2726 self.assertEqual(t.tm_mday, 1)
2727 self.assertEqual(t.tm_hour, 23)
2728 self.assertEqual(t.tm_min, 58)
2729 self.assertEqual(t.tm_sec, 37)
2730 self.assertEqual(t.tm_yday, 1)
2731 self.assertEqual(t.tm_isdst, 0)
2732
2733 def test_tzinfo_isoformat(self):
2734 zero = FixedOffset(0, "+00:00")
2735 plus = FixedOffset(220, "+03:40")
2736 minus = FixedOffset(-231, "-03:51")
2737 unknown = FixedOffset(None, "")
2738
2739 cls = self.theclass
2740 datestr = '0001-02-03'
2741 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002742 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002743 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2744 timestr = '04:05:59' + (us and '.987001' or '')
2745 ofsstr = ofs is not None and d.tzname() or ''
2746 tailstr = timestr + ofsstr
2747 iso = d.isoformat()
2748 self.assertEqual(iso, datestr + 'T' + tailstr)
2749 self.assertEqual(iso, d.isoformat('T'))
2750 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2751 self.assertEqual(str(d), datestr + ' ' + tailstr)
2752
Tim Peters12bf3392002-12-24 05:41:27 +00002753 def test_replace(self):
2754 cls = self.theclass
2755 z100 = FixedOffset(100, "+100")
2756 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2757 args = [1, 2, 3, 4, 5, 6, 7, z100]
2758 base = cls(*args)
2759 self.assertEqual(base, base.replace())
2760
2761 i = 0
2762 for name, newval in (("year", 2),
2763 ("month", 3),
2764 ("day", 4),
2765 ("hour", 5),
2766 ("minute", 6),
2767 ("second", 7),
2768 ("microsecond", 8),
2769 ("tzinfo", zm200)):
2770 newargs = args[:]
2771 newargs[i] = newval
2772 expected = cls(*newargs)
2773 got = base.replace(**{name: newval})
2774 self.assertEqual(expected, got)
2775 i += 1
2776
2777 # Ensure we can get rid of a tzinfo.
2778 self.assertEqual(base.tzname(), "+100")
2779 base2 = base.replace(tzinfo=None)
2780 self.failUnless(base2.tzinfo is None)
2781 self.failUnless(base2.tzname() is None)
2782
2783 # Ensure we can add one.
2784 base3 = base2.replace(tzinfo=z100)
2785 self.assertEqual(base, base3)
2786 self.failUnless(base.tzinfo is base3.tzinfo)
2787
2788 # Out of bounds.
2789 base = cls(2000, 2, 29)
2790 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002791
Tim Peters80475bb2002-12-25 07:40:55 +00002792 def test_more_astimezone(self):
2793 # The inherited test_astimezone covered some trivial and error cases.
2794 fnone = FixedOffset(None, "None")
2795 f44m = FixedOffset(44, "44")
2796 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2797
Tim Peters10cadce2003-01-23 19:58:02 +00002798 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002799 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002800 # Replacing with degenerate tzinfo raises an exception.
2801 self.assertRaises(ValueError, dt.astimezone, fnone)
2802 # Ditto with None tz.
2803 self.assertRaises(TypeError, dt.astimezone, None)
2804 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002805 x = dt.astimezone(dt.tzinfo)
2806 self.failUnless(x.tzinfo is f44m)
2807 self.assertEqual(x.date(), dt.date())
2808 self.assertEqual(x.time(), dt.time())
2809
2810 # Replacing with different tzinfo does adjust.
2811 got = dt.astimezone(fm5h)
2812 self.failUnless(got.tzinfo is fm5h)
2813 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2814 expected = dt - dt.utcoffset() # in effect, convert to UTC
2815 expected += fm5h.utcoffset(dt) # and from there to local time
2816 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2817 self.assertEqual(got.date(), expected.date())
2818 self.assertEqual(got.time(), expected.time())
2819 self.assertEqual(got.timetz(), expected.timetz())
2820 self.failUnless(got.tzinfo is expected.tzinfo)
2821 self.assertEqual(got, expected)
2822
Tim Peters4c0db782002-12-26 05:01:19 +00002823 def test_aware_subtract(self):
2824 cls = self.theclass
2825
Tim Peters60c76e42002-12-27 00:41:11 +00002826 # Ensure that utcoffset() is ignored when the operands have the
2827 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002828 class OperandDependentOffset(tzinfo):
2829 def utcoffset(self, t):
2830 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002831 # d0 and d1 equal after adjustment
2832 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002833 else:
Tim Peters397301e2003-01-02 21:28:08 +00002834 # d2 off in the weeds
2835 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002836
2837 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2838 d0 = base.replace(minute=3)
2839 d1 = base.replace(minute=9)
2840 d2 = base.replace(minute=11)
2841 for x in d0, d1, d2:
2842 for y in d0, d1, d2:
2843 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002844 expected = timedelta(minutes=x.minute - y.minute)
2845 self.assertEqual(got, expected)
2846
2847 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2848 # ignored.
2849 base = cls(8, 9, 10, 11, 12, 13, 14)
2850 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2851 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2852 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2853 for x in d0, d1, d2:
2854 for y in d0, d1, d2:
2855 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002856 if (x is d0 or x is d1) and (y is d0 or y is d1):
2857 expected = timedelta(0)
2858 elif x is y is d2:
2859 expected = timedelta(0)
2860 elif x is d2:
2861 expected = timedelta(minutes=(11-59)-0)
2862 else:
2863 assert y is d2
2864 expected = timedelta(minutes=0-(11-59))
2865 self.assertEqual(got, expected)
2866
Tim Peters60c76e42002-12-27 00:41:11 +00002867 def test_mixed_compare(self):
2868 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002869 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002870 self.assertEqual(t1, t2)
2871 t2 = t2.replace(tzinfo=None)
2872 self.assertEqual(t1, t2)
2873 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2874 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002875 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2876 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002877
Tim Peters0bf60bd2003-01-08 20:40:01 +00002878 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002879 class Varies(tzinfo):
2880 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002881 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002882 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002883 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002884 return self.offset
2885
2886 v = Varies()
2887 t1 = t2.replace(tzinfo=v)
2888 t2 = t2.replace(tzinfo=v)
2889 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2890 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2891 self.assertEqual(t1, t2)
2892
2893 # But if they're not identical, it isn't ignored.
2894 t2 = t2.replace(tzinfo=Varies())
2895 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002896
Tim Petersa98924a2003-05-17 05:55:19 +00002897 def test_subclass_datetimetz(self):
2898
2899 class C(self.theclass):
2900 theAnswer = 42
2901
2902 def __new__(cls, *args, **kws):
2903 temp = kws.copy()
2904 extra = temp.pop('extra')
2905 result = self.theclass.__new__(cls, *args, **temp)
2906 result.extra = extra
2907 return result
2908
2909 def newmeth(self, start):
2910 return start + self.hour + self.year
2911
2912 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2913
2914 dt1 = self.theclass(*args)
2915 dt2 = C(*args, **{'extra': 7})
2916
2917 self.assertEqual(dt2.__class__, C)
2918 self.assertEqual(dt2.theAnswer, 42)
2919 self.assertEqual(dt2.extra, 7)
2920 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2921 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2922
Tim Peters621818b2002-12-29 23:44:49 +00002923# Pain to set up DST-aware tzinfo classes.
2924
2925def first_sunday_on_or_after(dt):
2926 days_to_go = 6 - dt.weekday()
2927 if days_to_go:
2928 dt += timedelta(days_to_go)
2929 return dt
2930
2931ZERO = timedelta(0)
2932HOUR = timedelta(hours=1)
2933DAY = timedelta(days=1)
2934# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2935DSTSTART = datetime(1, 4, 1, 2)
2936# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002937# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2938# being standard time on that day, there is no spelling in local time of
2939# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2940DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002941
2942class USTimeZone(tzinfo):
2943
2944 def __init__(self, hours, reprname, stdname, dstname):
2945 self.stdoffset = timedelta(hours=hours)
2946 self.reprname = reprname
2947 self.stdname = stdname
2948 self.dstname = dstname
2949
2950 def __repr__(self):
2951 return self.reprname
2952
2953 def tzname(self, dt):
2954 if self.dst(dt):
2955 return self.dstname
2956 else:
2957 return self.stdname
2958
2959 def utcoffset(self, dt):
2960 return self.stdoffset + self.dst(dt)
2961
2962 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00002963 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00002964 # An exception instead may be sensible here, in one or more of
2965 # the cases.
2966 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00002967 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00002968
2969 # Find first Sunday in April.
2970 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
2971 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
2972
2973 # Find last Sunday in October.
2974 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
2975 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
2976
Tim Peters621818b2002-12-29 23:44:49 +00002977 # Can't compare naive to aware objects, so strip the timezone from
2978 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00002979 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00002980 return HOUR
2981 else:
2982 return ZERO
2983
Tim Peters521fc152002-12-31 17:36:56 +00002984Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
2985Central = USTimeZone(-6, "Central", "CST", "CDT")
2986Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
2987Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00002988utc_real = FixedOffset(0, "UTC", 0)
2989# For better test coverage, we want another flavor of UTC that's west of
2990# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00002991utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00002992
2993class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00002994 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002995 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00002996 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002997
Tim Peters0bf60bd2003-01-08 20:40:01 +00002998 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00002999
Tim Peters521fc152002-12-31 17:36:56 +00003000 # Check a time that's inside DST.
3001 def checkinside(self, dt, tz, utc, dston, dstoff):
3002 self.assertEqual(dt.dst(), HOUR)
3003
3004 # Conversion to our own timezone is always an identity.
3005 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003006
3007 asutc = dt.astimezone(utc)
3008 there_and_back = asutc.astimezone(tz)
3009
3010 # Conversion to UTC and back isn't always an identity here,
3011 # because there are redundant spellings (in local time) of
3012 # UTC time when DST begins: the clock jumps from 1:59:59
3013 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3014 # make sense then. The classes above treat 2:MM:SS as
3015 # daylight time then (it's "after 2am"), really an alias
3016 # for 1:MM:SS standard time. The latter form is what
3017 # conversion back from UTC produces.
3018 if dt.date() == dston.date() and dt.hour == 2:
3019 # We're in the redundant hour, and coming back from
3020 # UTC gives the 1:MM:SS standard-time spelling.
3021 self.assertEqual(there_and_back + HOUR, dt)
3022 # Although during was considered to be in daylight
3023 # time, there_and_back is not.
3024 self.assertEqual(there_and_back.dst(), ZERO)
3025 # They're the same times in UTC.
3026 self.assertEqual(there_and_back.astimezone(utc),
3027 dt.astimezone(utc))
3028 else:
3029 # We're not in the redundant hour.
3030 self.assertEqual(dt, there_and_back)
3031
Tim Peters327098a2003-01-20 22:54:38 +00003032 # Because we have a redundant spelling when DST begins, there is
3033 # (unforunately) an hour when DST ends that can't be spelled at all in
3034 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3035 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3036 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3037 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3038 # expressed in local time. Nevertheless, we want conversion back
3039 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003040 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003041 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003042 if dt.date() == dstoff.date() and dt.hour == 0:
3043 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003044 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003045 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3046 nexthour_utc += HOUR
3047 nexthour_tz = nexthour_utc.astimezone(tz)
3048 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003049 else:
Tim Peters327098a2003-01-20 22:54:38 +00003050 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003051
3052 # Check a time that's outside DST.
3053 def checkoutside(self, dt, tz, utc):
3054 self.assertEqual(dt.dst(), ZERO)
3055
3056 # Conversion to our own timezone is always an identity.
3057 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003058
3059 # Converting to UTC and back is an identity too.
3060 asutc = dt.astimezone(utc)
3061 there_and_back = asutc.astimezone(tz)
3062 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003063
Tim Peters1024bf82002-12-30 17:09:40 +00003064 def convert_between_tz_and_utc(self, tz, utc):
3065 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003066 # Because 1:MM on the day DST ends is taken as being standard time,
3067 # there is no spelling in tz for the last hour of daylight time.
3068 # For purposes of the test, the last hour of DST is 0:MM, which is
3069 # taken as being daylight time (and 1:MM is taken as being standard
3070 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003071 dstoff = self.dstoff.replace(tzinfo=tz)
3072 for delta in (timedelta(weeks=13),
3073 DAY,
3074 HOUR,
3075 timedelta(minutes=1),
3076 timedelta(microseconds=1)):
3077
Tim Peters521fc152002-12-31 17:36:56 +00003078 self.checkinside(dston, tz, utc, dston, dstoff)
3079 for during in dston + delta, dstoff - delta:
3080 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003081
Tim Peters521fc152002-12-31 17:36:56 +00003082 self.checkoutside(dstoff, tz, utc)
3083 for outside in dston - delta, dstoff + delta:
3084 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003085
Tim Peters621818b2002-12-29 23:44:49 +00003086 def test_easy(self):
3087 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003088 self.convert_between_tz_and_utc(Eastern, utc_real)
3089 self.convert_between_tz_and_utc(Pacific, utc_real)
3090 self.convert_between_tz_and_utc(Eastern, utc_fake)
3091 self.convert_between_tz_and_utc(Pacific, utc_fake)
3092 # The next is really dancing near the edge. It works because
3093 # Pacific and Eastern are far enough apart that their "problem
3094 # hours" don't overlap.
3095 self.convert_between_tz_and_utc(Eastern, Pacific)
3096 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003097 # OTOH, these fail! Don't enable them. The difficulty is that
3098 # the edge case tests assume that every hour is representable in
3099 # the "utc" class. This is always true for a fixed-offset tzinfo
3100 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3101 # For these adjacent DST-aware time zones, the range of time offsets
3102 # tested ends up creating hours in the one that aren't representable
3103 # in the other. For the same reason, we would see failures in the
3104 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3105 # offset deltas in convert_between_tz_and_utc().
3106 #
3107 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3108 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003109
Tim Petersf3615152003-01-01 21:51:37 +00003110 def test_tricky(self):
3111 # 22:00 on day before daylight starts.
3112 fourback = self.dston - timedelta(hours=4)
3113 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003114 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003115 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3116 # 2", we should get the 3 spelling.
3117 # If we plug 22:00 the day before into Eastern, it "looks like std
3118 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3119 # to 22:00 lands on 2:00, which makes no sense in local time (the
3120 # local clock jumps from 1 to 3). The point here is to make sure we
3121 # get the 3 spelling.
3122 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003123 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003124 self.assertEqual(expected, got)
3125
3126 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3127 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003128 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003129 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3130 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3131 # spelling.
3132 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003133 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003134 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003135
Tim Petersadf64202003-01-04 06:03:15 +00003136 # Now on the day DST ends, we want "repeat an hour" behavior.
3137 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3138 # EST 23:MM 0:MM 1:MM 2:MM
3139 # EDT 0:MM 1:MM 2:MM 3:MM
3140 # wall 0:MM 1:MM 1:MM 2:MM against these
3141 for utc in utc_real, utc_fake:
3142 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003143 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003144 # Convert that to UTC.
3145 first_std_hour -= tz.utcoffset(None)
3146 # Adjust for possibly fake UTC.
3147 asutc = first_std_hour + utc.utcoffset(None)
3148 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3149 # tz=Eastern.
3150 asutcbase = asutc.replace(tzinfo=utc)
3151 for tzhour in (0, 1, 1, 2):
3152 expectedbase = self.dstoff.replace(hour=tzhour)
3153 for minute in 0, 30, 59:
3154 expected = expectedbase.replace(minute=minute)
3155 asutc = asutcbase.replace(minute=minute)
3156 astz = asutc.astimezone(tz)
3157 self.assertEqual(astz.replace(tzinfo=None), expected)
3158 asutcbase += HOUR
3159
3160
Tim Peters710fb152003-01-02 19:35:54 +00003161 def test_bogus_dst(self):
3162 class ok(tzinfo):
3163 def utcoffset(self, dt): return HOUR
3164 def dst(self, dt): return HOUR
3165
3166 now = self.theclass.now().replace(tzinfo=utc_real)
3167 # Doesn't blow up.
3168 now.astimezone(ok())
3169
3170 # Does blow up.
3171 class notok(ok):
3172 def dst(self, dt): return None
3173 self.assertRaises(ValueError, now.astimezone, notok())
3174
Tim Peters52dcce22003-01-23 16:36:11 +00003175 def test_fromutc(self):
3176 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3177 now = datetime.utcnow().replace(tzinfo=utc_real)
3178 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3179 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3180 enow = Eastern.fromutc(now) # doesn't blow up
3181 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3182 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3183 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3184
3185 # Always converts UTC to standard time.
3186 class FauxUSTimeZone(USTimeZone):
3187 def fromutc(self, dt):
3188 return dt + self.stdoffset
3189 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3190
3191 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3192 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3193 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3194
3195 # Check around DST start.
3196 start = self.dston.replace(hour=4, tzinfo=Eastern)
3197 fstart = start.replace(tzinfo=FEastern)
3198 for wall in 23, 0, 1, 3, 4, 5:
3199 expected = start.replace(hour=wall)
3200 if wall == 23:
3201 expected -= timedelta(days=1)
3202 got = Eastern.fromutc(start)
3203 self.assertEqual(expected, got)
3204
3205 expected = fstart + FEastern.stdoffset
3206 got = FEastern.fromutc(fstart)
3207 self.assertEqual(expected, got)
3208
3209 # Ensure astimezone() calls fromutc() too.
3210 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3211 self.assertEqual(expected, got)
3212
3213 start += HOUR
3214 fstart += HOUR
3215
3216 # Check around DST end.
3217 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3218 fstart = start.replace(tzinfo=FEastern)
3219 for wall in 0, 1, 1, 2, 3, 4:
3220 expected = start.replace(hour=wall)
3221 got = Eastern.fromutc(start)
3222 self.assertEqual(expected, got)
3223
3224 expected = fstart + FEastern.stdoffset
3225 got = FEastern.fromutc(fstart)
3226 self.assertEqual(expected, got)
3227
3228 # Ensure astimezone() calls fromutc() too.
3229 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3230 self.assertEqual(expected, got)
3231
3232 start += HOUR
3233 fstart += HOUR
3234
Tim Peters710fb152003-01-02 19:35:54 +00003235
Tim Peters528ca532004-09-16 01:30:50 +00003236#############################################################################
3237# oddballs
3238
3239class Oddballs(unittest.TestCase):
3240
3241 def test_bug_1028306(self):
3242 # Trying to compare a date to a datetime should act like a mixed-
3243 # type comparison, despite that datetime is a subclass of date.
3244 as_date = date.today()
3245 as_datetime = datetime.combine(as_date, time())
3246 self.assert_(as_date != as_datetime)
3247 self.assert_(as_datetime != as_date)
3248 self.assert_(not as_date == as_datetime)
3249 self.assert_(not as_datetime == as_date)
3250 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3251 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3252 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3253 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3254 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3255 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3256 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3257 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3258
3259 # Neverthelss, comparison should work with the base-class (date)
3260 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003261 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003262 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003263 as_different = as_datetime.replace(day= different_day)
3264 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003265
3266 # And date should compare with other subclasses of date. If a
3267 # subclass wants to stop this, it's up to the subclass to do so.
3268 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3269 self.assertEqual(as_date, date_sc)
3270 self.assertEqual(date_sc, as_date)
3271
3272 # Ditto for datetimes.
3273 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3274 as_date.day, 0, 0, 0)
3275 self.assertEqual(as_datetime, datetime_sc)
3276 self.assertEqual(datetime_sc, as_datetime)
3277
Tim Peters2a799bf2002-12-16 20:18:38 +00003278def test_main():
Guido van Rossumd8faa362007-04-27 19:54:29 +00003279 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003280
3281if __name__ == "__main__":
3282 test_main()