blob: 6b4c667d6ee09c558417388b34da7206d2f0adc9 [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
6import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00008import unittest
Guido van Rossumbf12cdb2006-08-17 20:24:18 +00009try:
10 import cPickle
11except ImportError:
12 cPickle = None
Tim Peters2a799bf2002-12-16 20:18:38 +000013
14from test import test_support
15
16from datetime import MINYEAR, MAXYEAR
17from datetime import timedelta
18from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000019from datetime import time
20from datetime import date, datetime
21
Tim Peters35ad6412003-02-05 04:08:07 +000022pickle_choices = [(pickler, unpickler, proto)
23 for pickler in pickle, cPickle
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000024 if pickler is not None
Tim Peters35ad6412003-02-05 04:08:07 +000025 for unpickler in pickle, cPickle
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000026 if unpickler is not None
Tim Peters35ad6412003-02-05 04:08:07 +000027 for proto in range(3)]
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000028if cPickle is None:
29 assert len(pickle_choices) == 3
30else:
31 assert len(pickle_choices) == 2*2*3
Guido van Rossum177e41a2003-01-30 22:06:23 +000032
Tim Peters68124bb2003-02-08 03:46:31 +000033# An arbitrary collection of objects of non-datetime types, for testing
34# mixed-type comparisons.
Guido van Rossume2a383d2007-01-15 16:59:06 +000035OTHERSTUFF = (10, 10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000036
Tim Peters2a799bf2002-12-16 20:18:38 +000037
38#############################################################################
39# module tests
40
41class TestModule(unittest.TestCase):
42
43 def test_constants(self):
44 import datetime
45 self.assertEqual(datetime.MINYEAR, 1)
46 self.assertEqual(datetime.MAXYEAR, 9999)
47
48#############################################################################
49# tzinfo tests
50
51class FixedOffset(tzinfo):
52 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000053 if isinstance(offset, int):
54 offset = timedelta(minutes=offset)
55 if isinstance(dstoffset, int):
56 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000057 self.__offset = offset
58 self.__name = name
59 self.__dstoffset = dstoffset
60 def __repr__(self):
61 return self.__name.lower()
62 def utcoffset(self, dt):
63 return self.__offset
64 def tzname(self, dt):
65 return self.__name
66 def dst(self, dt):
67 return self.__dstoffset
68
Tim Petersfb8472c2002-12-21 05:04:42 +000069class PicklableFixedOffset(FixedOffset):
70 def __init__(self, offset=None, name=None, dstoffset=None):
71 FixedOffset.__init__(self, offset, name, dstoffset)
72
Tim Peters2a799bf2002-12-16 20:18:38 +000073class TestTZInfo(unittest.TestCase):
74
75 def test_non_abstractness(self):
76 # In order to allow subclasses to get pickled, the C implementation
77 # wasn't able to get away with having __init__ raise
78 # NotImplementedError.
79 useless = tzinfo()
80 dt = datetime.max
81 self.assertRaises(NotImplementedError, useless.tzname, dt)
82 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
83 self.assertRaises(NotImplementedError, useless.dst, dt)
84
85 def test_subclass_must_override(self):
86 class NotEnough(tzinfo):
87 def __init__(self, offset, name):
88 self.__offset = offset
89 self.__name = name
90 self.failUnless(issubclass(NotEnough, tzinfo))
91 ne = NotEnough(3, "NotByALongShot")
92 self.failUnless(isinstance(ne, tzinfo))
93
94 dt = datetime.now()
95 self.assertRaises(NotImplementedError, ne.tzname, dt)
96 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
97 self.assertRaises(NotImplementedError, ne.dst, dt)
98
99 def test_normal(self):
100 fo = FixedOffset(3, "Three")
101 self.failUnless(isinstance(fo, tzinfo))
102 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000103 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000104 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000105 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000106
107 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000108 # There's no point to pickling tzinfo objects on their own (they
109 # carry no data), but they need to be picklable anyway else
110 # concrete subclasses can't be pickled.
111 orig = tzinfo.__new__(tzinfo)
112 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000113 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000114 green = pickler.dumps(orig, proto)
115 derived = unpickler.loads(green)
116 self.failUnless(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000117
118 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000119 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000120 offset = timedelta(minutes=-300)
121 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000122 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000123 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000124 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000125 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000126 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000127 green = pickler.dumps(orig, proto)
128 derived = unpickler.loads(green)
129 self.failUnless(isinstance(derived, tzinfo))
130 self.failUnless(type(derived) is PicklableFixedOffset)
131 self.assertEqual(derived.utcoffset(None), offset)
132 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000133
134#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000135# Base clase for testing a particular aspect of timedelta, time, date and
136# datetime comparisons.
137
138class HarmlessMixedComparison(unittest.TestCase):
139 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
140
141 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
142 # legit constructor.
143
144 def test_harmless_mixed_comparison(self):
145 me = self.theclass(1, 1, 1)
146
147 self.failIf(me == ())
148 self.failUnless(me != ())
149 self.failIf(() == me)
150 self.failUnless(() != me)
151
Guido van Rossume2a383d2007-01-15 16:59:06 +0000152 self.failUnless(me in [1, 20, [], me])
153 self.failIf(me not in [1, 20, [], me])
Tim Peters07534a62003-02-07 22:50:28 +0000154
Guido van Rossume2a383d2007-01-15 16:59:06 +0000155 self.failUnless([] in [me, 1, 20, []])
156 self.failIf([] not in [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000157
158 def test_harmful_mixed_comparison(self):
159 me = self.theclass(1, 1, 1)
160
161 self.assertRaises(TypeError, lambda: me < ())
162 self.assertRaises(TypeError, lambda: me <= ())
163 self.assertRaises(TypeError, lambda: me > ())
164 self.assertRaises(TypeError, lambda: me >= ())
165
166 self.assertRaises(TypeError, lambda: () < me)
167 self.assertRaises(TypeError, lambda: () <= me)
168 self.assertRaises(TypeError, lambda: () > me)
169 self.assertRaises(TypeError, lambda: () >= me)
170
171 self.assertRaises(TypeError, cmp, (), me)
172 self.assertRaises(TypeError, cmp, me, ())
173
174#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000175# timedelta tests
176
Tim Peters07534a62003-02-07 22:50:28 +0000177class TestTimeDelta(HarmlessMixedComparison):
178
179 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000180
181 def test_constructor(self):
182 eq = self.assertEqual
183 td = timedelta
184
185 # Check keyword args to constructor
186 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
187 milliseconds=0, microseconds=0))
188 eq(td(1), td(days=1))
189 eq(td(0, 1), td(seconds=1))
190 eq(td(0, 0, 1), td(microseconds=1))
191 eq(td(weeks=1), td(days=7))
192 eq(td(days=1), td(hours=24))
193 eq(td(hours=1), td(minutes=60))
194 eq(td(minutes=1), td(seconds=60))
195 eq(td(seconds=1), td(milliseconds=1000))
196 eq(td(milliseconds=1), td(microseconds=1000))
197
198 # Check float args to constructor
199 eq(td(weeks=1.0/7), td(days=1))
200 eq(td(days=1.0/24), td(hours=1))
201 eq(td(hours=1.0/60), td(minutes=1))
202 eq(td(minutes=1.0/60), td(seconds=1))
203 eq(td(seconds=0.001), td(milliseconds=1))
204 eq(td(milliseconds=0.001), td(microseconds=1))
205
206 def test_computations(self):
207 eq = self.assertEqual
208 td = timedelta
209
210 a = td(7) # One week
211 b = td(0, 60) # One minute
212 c = td(0, 0, 1000) # One millisecond
213 eq(a+b+c, td(7, 60, 1000))
214 eq(a-b, td(6, 24*3600 - 60))
215 eq(-a, td(-7))
216 eq(+a, td(7))
217 eq(-b, td(-1, 24*3600 - 60))
218 eq(-c, td(-1, 24*3600 - 1, 999000))
219 eq(abs(a), a)
220 eq(abs(-a), a)
221 eq(td(6, 24*3600), a)
222 eq(td(0, 0, 60*1000000), b)
223 eq(a*10, td(70))
224 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000225 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000226 eq(b*10, td(0, 600))
227 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000228 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000229 eq(c*10, td(0, 0, 10000))
230 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000231 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000232 eq(a*-1, -a)
233 eq(b*-2, -b-b)
234 eq(c*-2, -c+-c)
235 eq(b*(60*24), (b*60)*24)
236 eq(b*(60*24), (60*b)*24)
237 eq(c*1000, td(0, 1))
238 eq(1000*c, td(0, 1))
239 eq(a//7, td(1))
240 eq(b//10, td(0, 6))
241 eq(c//1000, td(0, 0, 1))
242 eq(a//10, td(0, 7*24*360))
243 eq(a//3600000, td(0, 0, 7*24*1000))
244
245 def test_disallowed_computations(self):
246 a = timedelta(42)
247
248 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000249 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000250 self.assertRaises(TypeError, lambda: a+i)
251 self.assertRaises(TypeError, lambda: a-i)
252 self.assertRaises(TypeError, lambda: i+a)
253 self.assertRaises(TypeError, lambda: i-a)
254
255 # Mul/div by float isn't supported.
256 x = 2.3
257 self.assertRaises(TypeError, lambda: a*x)
258 self.assertRaises(TypeError, lambda: x*a)
259 self.assertRaises(TypeError, lambda: a/x)
260 self.assertRaises(TypeError, lambda: x/a)
261 self.assertRaises(TypeError, lambda: a // x)
262 self.assertRaises(TypeError, lambda: x // a)
263
264 # Divison of int by timedelta doesn't make sense.
265 # Division by zero doesn't make sense.
Guido van Rossume2a383d2007-01-15 16:59:06 +0000266 for zero in 0, 0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000267 self.assertRaises(TypeError, lambda: zero // a)
268 self.assertRaises(ZeroDivisionError, lambda: a // zero)
269
270 def test_basic_attributes(self):
271 days, seconds, us = 1, 7, 31
272 td = timedelta(days, seconds, us)
273 self.assertEqual(td.days, days)
274 self.assertEqual(td.seconds, seconds)
275 self.assertEqual(td.microseconds, us)
276
277 def test_carries(self):
278 t1 = timedelta(days=100,
279 weeks=-7,
280 hours=-24*(100-49),
281 minutes=-3,
282 seconds=12,
283 microseconds=(3*60 - 12) * 1e6 + 1)
284 t2 = timedelta(microseconds=1)
285 self.assertEqual(t1, t2)
286
287 def test_hash_equality(self):
288 t1 = timedelta(days=100,
289 weeks=-7,
290 hours=-24*(100-49),
291 minutes=-3,
292 seconds=12,
293 microseconds=(3*60 - 12) * 1000000)
294 t2 = timedelta()
295 self.assertEqual(hash(t1), hash(t2))
296
297 t1 += timedelta(weeks=7)
298 t2 += timedelta(days=7*7)
299 self.assertEqual(t1, t2)
300 self.assertEqual(hash(t1), hash(t2))
301
302 d = {t1: 1}
303 d[t2] = 2
304 self.assertEqual(len(d), 1)
305 self.assertEqual(d[t1], 2)
306
307 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000308 args = 12, 34, 56
309 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000310 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000311 green = pickler.dumps(orig, proto)
312 derived = unpickler.loads(green)
313 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000314
315 def test_compare(self):
316 t1 = timedelta(2, 3, 4)
317 t2 = timedelta(2, 3, 4)
318 self.failUnless(t1 == t2)
319 self.failUnless(t1 <= t2)
320 self.failUnless(t1 >= t2)
321 self.failUnless(not t1 != t2)
322 self.failUnless(not t1 < t2)
323 self.failUnless(not t1 > t2)
324 self.assertEqual(cmp(t1, t2), 0)
325 self.assertEqual(cmp(t2, t1), 0)
326
327 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
328 t2 = timedelta(*args) # this is larger than t1
329 self.failUnless(t1 < t2)
330 self.failUnless(t2 > t1)
331 self.failUnless(t1 <= t2)
332 self.failUnless(t2 >= t1)
333 self.failUnless(t1 != t2)
334 self.failUnless(t2 != t1)
335 self.failUnless(not t1 == t2)
336 self.failUnless(not t2 == t1)
337 self.failUnless(not t1 > t2)
338 self.failUnless(not t2 < t1)
339 self.failUnless(not t1 >= t2)
340 self.failUnless(not t2 <= t1)
341 self.assertEqual(cmp(t1, t2), -1)
342 self.assertEqual(cmp(t2, t1), 1)
343
Tim Peters68124bb2003-02-08 03:46:31 +0000344 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000345 self.assertEqual(t1 == badarg, False)
346 self.assertEqual(t1 != badarg, True)
347 self.assertEqual(badarg == t1, False)
348 self.assertEqual(badarg != t1, True)
349
Tim Peters2a799bf2002-12-16 20:18:38 +0000350 self.assertRaises(TypeError, lambda: t1 <= badarg)
351 self.assertRaises(TypeError, lambda: t1 < badarg)
352 self.assertRaises(TypeError, lambda: t1 > badarg)
353 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000354 self.assertRaises(TypeError, lambda: badarg <= t1)
355 self.assertRaises(TypeError, lambda: badarg < t1)
356 self.assertRaises(TypeError, lambda: badarg > t1)
357 self.assertRaises(TypeError, lambda: badarg >= t1)
358
359 def test_str(self):
360 td = timedelta
361 eq = self.assertEqual
362
363 eq(str(td(1)), "1 day, 0:00:00")
364 eq(str(td(-1)), "-1 day, 0:00:00")
365 eq(str(td(2)), "2 days, 0:00:00")
366 eq(str(td(-2)), "-2 days, 0:00:00")
367
368 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
369 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
370 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
371 "-210 days, 23:12:34")
372
373 eq(str(td(milliseconds=1)), "0:00:00.001000")
374 eq(str(td(microseconds=3)), "0:00:00.000003")
375
376 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
377 microseconds=999999)),
378 "999999999 days, 23:59:59.999999")
379
380 def test_roundtrip(self):
381 for td in (timedelta(days=999999999, hours=23, minutes=59,
382 seconds=59, microseconds=999999),
383 timedelta(days=-999999999),
384 timedelta(days=1, seconds=2, microseconds=3)):
385
386 # Verify td -> string -> td identity.
387 s = repr(td)
388 self.failUnless(s.startswith('datetime.'))
389 s = s[9:]
390 td2 = eval(s)
391 self.assertEqual(td, td2)
392
393 # Verify identity via reconstructing from pieces.
394 td2 = timedelta(td.days, td.seconds, td.microseconds)
395 self.assertEqual(td, td2)
396
397 def test_resolution_info(self):
398 self.assert_(isinstance(timedelta.min, timedelta))
399 self.assert_(isinstance(timedelta.max, timedelta))
400 self.assert_(isinstance(timedelta.resolution, timedelta))
401 self.assert_(timedelta.max > timedelta.min)
402 self.assertEqual(timedelta.min, timedelta(-999999999))
403 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
404 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
405
406 def test_overflow(self):
407 tiny = timedelta.resolution
408
409 td = timedelta.min + tiny
410 td -= tiny # no problem
411 self.assertRaises(OverflowError, td.__sub__, tiny)
412 self.assertRaises(OverflowError, td.__add__, -tiny)
413
414 td = timedelta.max - tiny
415 td += tiny # no problem
416 self.assertRaises(OverflowError, td.__add__, tiny)
417 self.assertRaises(OverflowError, td.__sub__, -tiny)
418
419 self.assertRaises(OverflowError, lambda: -timedelta.max)
420
421 def test_microsecond_rounding(self):
422 td = timedelta
423 eq = self.assertEqual
424
425 # Single-field rounding.
426 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
427 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
428 eq(td(milliseconds=0.6/1000), td(microseconds=1))
429 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
430
431 # Rounding due to contributions from more than one field.
432 us_per_hour = 3600e6
433 us_per_day = us_per_hour * 24
434 eq(td(days=.4/us_per_day), td(0))
435 eq(td(hours=.2/us_per_hour), td(0))
436 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
437
438 eq(td(days=-.4/us_per_day), td(0))
439 eq(td(hours=-.2/us_per_hour), td(0))
440 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
441
442 def test_massive_normalization(self):
443 td = timedelta(microseconds=-1)
444 self.assertEqual((td.days, td.seconds, td.microseconds),
445 (-1, 24*3600-1, 999999))
446
447 def test_bool(self):
448 self.failUnless(timedelta(1))
449 self.failUnless(timedelta(0, 1))
450 self.failUnless(timedelta(0, 0, 1))
451 self.failUnless(timedelta(microseconds=1))
452 self.failUnless(not timedelta(0))
453
Tim Petersb0c854d2003-05-17 15:57:00 +0000454 def test_subclass_timedelta(self):
455
456 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000457 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000458 def from_td(td):
459 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000460
461 def as_hours(self):
462 sum = (self.days * 24 +
463 self.seconds / 3600.0 +
464 self.microseconds / 3600e6)
465 return round(sum)
466
467 t1 = T(days=1)
468 self.assert_(type(t1) is T)
469 self.assertEqual(t1.as_hours(), 24)
470
471 t2 = T(days=-1, seconds=-3600)
472 self.assert_(type(t2) is T)
473 self.assertEqual(t2.as_hours(), -25)
474
475 t3 = t1 + t2
476 self.assert_(type(t3) is timedelta)
477 t4 = T.from_td(t3)
478 self.assert_(type(t4) is T)
479 self.assertEqual(t3.days, t4.days)
480 self.assertEqual(t3.seconds, t4.seconds)
481 self.assertEqual(t3.microseconds, t4.microseconds)
482 self.assertEqual(str(t3), str(t4))
483 self.assertEqual(t4.as_hours(), -1)
484
Tim Peters2a799bf2002-12-16 20:18:38 +0000485#############################################################################
486# date tests
487
488class TestDateOnly(unittest.TestCase):
489 # Tests here won't pass if also run on datetime objects, so don't
490 # subclass this to test datetimes too.
491
492 def test_delta_non_days_ignored(self):
493 dt = date(2000, 1, 2)
494 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
495 microseconds=5)
496 days = timedelta(delta.days)
497 self.assertEqual(days, timedelta(1))
498
499 dt2 = dt + delta
500 self.assertEqual(dt2, dt + days)
501
502 dt2 = delta + dt
503 self.assertEqual(dt2, dt + days)
504
505 dt2 = dt - delta
506 self.assertEqual(dt2, dt - days)
507
508 delta = -delta
509 days = timedelta(delta.days)
510 self.assertEqual(days, timedelta(-2))
511
512 dt2 = dt + delta
513 self.assertEqual(dt2, dt + days)
514
515 dt2 = delta + dt
516 self.assertEqual(dt2, dt + days)
517
518 dt2 = dt - delta
519 self.assertEqual(dt2, dt - days)
520
Tim Peters604c0132004-06-07 23:04:33 +0000521class SubclassDate(date):
522 sub_var = 1
523
Tim Peters07534a62003-02-07 22:50:28 +0000524class TestDate(HarmlessMixedComparison):
Tim Peters2a799bf2002-12-16 20:18:38 +0000525 # Tests here should pass for both dates and datetimes, except for a
526 # few tests that TestDateTime overrides.
527
528 theclass = date
529
530 def test_basic_attributes(self):
531 dt = self.theclass(2002, 3, 1)
532 self.assertEqual(dt.year, 2002)
533 self.assertEqual(dt.month, 3)
534 self.assertEqual(dt.day, 1)
535
536 def test_roundtrip(self):
537 for dt in (self.theclass(1, 2, 3),
538 self.theclass.today()):
539 # Verify dt -> string -> date identity.
540 s = repr(dt)
541 self.failUnless(s.startswith('datetime.'))
542 s = s[9:]
543 dt2 = eval(s)
544 self.assertEqual(dt, dt2)
545
546 # Verify identity via reconstructing from pieces.
547 dt2 = self.theclass(dt.year, dt.month, dt.day)
548 self.assertEqual(dt, dt2)
549
550 def test_ordinal_conversions(self):
551 # Check some fixed values.
552 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
553 (1, 12, 31, 365),
554 (2, 1, 1, 366),
555 # first example from "Calendrical Calculations"
556 (1945, 11, 12, 710347)]:
557 d = self.theclass(y, m, d)
558 self.assertEqual(n, d.toordinal())
559 fromord = self.theclass.fromordinal(n)
560 self.assertEqual(d, fromord)
561 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000562 # if we're checking something fancier than a date, verify
563 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000564 self.assertEqual(fromord.hour, 0)
565 self.assertEqual(fromord.minute, 0)
566 self.assertEqual(fromord.second, 0)
567 self.assertEqual(fromord.microsecond, 0)
568
Tim Peters0bf60bd2003-01-08 20:40:01 +0000569 # Check first and last days of year spottily across the whole
570 # range of years supported.
571 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000572 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
573 d = self.theclass(year, 1, 1)
574 n = d.toordinal()
575 d2 = self.theclass.fromordinal(n)
576 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000577 # Verify that moving back a day gets to the end of year-1.
578 if year > 1:
579 d = self.theclass.fromordinal(n-1)
580 d2 = self.theclass(year-1, 12, 31)
581 self.assertEqual(d, d2)
582 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000583
584 # Test every day in a leap-year and a non-leap year.
585 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
586 for year, isleap in (2000, True), (2002, False):
587 n = self.theclass(year, 1, 1).toordinal()
588 for month, maxday in zip(range(1, 13), dim):
589 if month == 2 and isleap:
590 maxday += 1
591 for day in range(1, maxday+1):
592 d = self.theclass(year, month, day)
593 self.assertEqual(d.toordinal(), n)
594 self.assertEqual(d, self.theclass.fromordinal(n))
595 n += 1
596
597 def test_extreme_ordinals(self):
598 a = self.theclass.min
599 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
600 aord = a.toordinal()
601 b = a.fromordinal(aord)
602 self.assertEqual(a, b)
603
604 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
605
606 b = a + timedelta(days=1)
607 self.assertEqual(b.toordinal(), aord + 1)
608 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
609
610 a = self.theclass.max
611 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
612 aord = a.toordinal()
613 b = a.fromordinal(aord)
614 self.assertEqual(a, b)
615
616 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
617
618 b = a - timedelta(days=1)
619 self.assertEqual(b.toordinal(), aord - 1)
620 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
621
622 def test_bad_constructor_arguments(self):
623 # bad years
624 self.theclass(MINYEAR, 1, 1) # no exception
625 self.theclass(MAXYEAR, 1, 1) # no exception
626 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
627 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
628 # bad months
629 self.theclass(2000, 1, 1) # no exception
630 self.theclass(2000, 12, 1) # no exception
631 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
632 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
633 # bad days
634 self.theclass(2000, 2, 29) # no exception
635 self.theclass(2004, 2, 29) # no exception
636 self.theclass(2400, 2, 29) # no exception
637 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
638 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
639 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
640 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
641 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
642 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
643
644 def test_hash_equality(self):
645 d = self.theclass(2000, 12, 31)
646 # same thing
647 e = self.theclass(2000, 12, 31)
648 self.assertEqual(d, e)
649 self.assertEqual(hash(d), hash(e))
650
651 dic = {d: 1}
652 dic[e] = 2
653 self.assertEqual(len(dic), 1)
654 self.assertEqual(dic[d], 2)
655 self.assertEqual(dic[e], 2)
656
657 d = self.theclass(2001, 1, 1)
658 # same thing
659 e = self.theclass(2001, 1, 1)
660 self.assertEqual(d, e)
661 self.assertEqual(hash(d), hash(e))
662
663 dic = {d: 1}
664 dic[e] = 2
665 self.assertEqual(len(dic), 1)
666 self.assertEqual(dic[d], 2)
667 self.assertEqual(dic[e], 2)
668
669 def test_computations(self):
670 a = self.theclass(2002, 1, 31)
671 b = self.theclass(1956, 1, 31)
672
673 diff = a-b
674 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
675 self.assertEqual(diff.seconds, 0)
676 self.assertEqual(diff.microseconds, 0)
677
678 day = timedelta(1)
679 week = timedelta(7)
680 a = self.theclass(2002, 3, 2)
681 self.assertEqual(a + day, self.theclass(2002, 3, 3))
682 self.assertEqual(day + a, self.theclass(2002, 3, 3))
683 self.assertEqual(a - day, self.theclass(2002, 3, 1))
684 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
685 self.assertEqual(a + week, self.theclass(2002, 3, 9))
686 self.assertEqual(a - week, self.theclass(2002, 2, 23))
687 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
688 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
689 self.assertEqual((a + week) - a, week)
690 self.assertEqual((a + day) - a, day)
691 self.assertEqual((a - week) - a, -week)
692 self.assertEqual((a - day) - a, -day)
693 self.assertEqual(a - (a + week), -week)
694 self.assertEqual(a - (a + day), -day)
695 self.assertEqual(a - (a - week), week)
696 self.assertEqual(a - (a - day), day)
697
698 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000699 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000700 self.assertRaises(TypeError, lambda: a+i)
701 self.assertRaises(TypeError, lambda: a-i)
702 self.assertRaises(TypeError, lambda: i+a)
703 self.assertRaises(TypeError, lambda: i-a)
704
705 # delta - date is senseless.
706 self.assertRaises(TypeError, lambda: day - a)
707 # mixing date and (delta or date) via * or // is senseless
708 self.assertRaises(TypeError, lambda: day * a)
709 self.assertRaises(TypeError, lambda: a * day)
710 self.assertRaises(TypeError, lambda: day // a)
711 self.assertRaises(TypeError, lambda: a // day)
712 self.assertRaises(TypeError, lambda: a * a)
713 self.assertRaises(TypeError, lambda: a // a)
714 # date + date is senseless
715 self.assertRaises(TypeError, lambda: a + a)
716
717 def test_overflow(self):
718 tiny = self.theclass.resolution
719
720 dt = self.theclass.min + tiny
721 dt -= tiny # no problem
722 self.assertRaises(OverflowError, dt.__sub__, tiny)
723 self.assertRaises(OverflowError, dt.__add__, -tiny)
724
725 dt = self.theclass.max - tiny
726 dt += tiny # no problem
727 self.assertRaises(OverflowError, dt.__add__, tiny)
728 self.assertRaises(OverflowError, dt.__sub__, -tiny)
729
730 def test_fromtimestamp(self):
731 import time
732
733 # Try an arbitrary fixed value.
734 year, month, day = 1999, 9, 19
735 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
736 d = self.theclass.fromtimestamp(ts)
737 self.assertEqual(d.year, year)
738 self.assertEqual(d.month, month)
739 self.assertEqual(d.day, day)
740
Tim Peters1b6f7a92004-06-20 02:50:16 +0000741 def test_insane_fromtimestamp(self):
742 # It's possible that some platform maps time_t to double,
743 # and that this test will fail there. This test should
744 # exempt such platforms (provided they return reasonable
745 # results!).
746 for insane in -1e200, 1e200:
747 self.assertRaises(ValueError, self.theclass.fromtimestamp,
748 insane)
749
Tim Peters2a799bf2002-12-16 20:18:38 +0000750 def test_today(self):
751 import time
752
753 # We claim that today() is like fromtimestamp(time.time()), so
754 # prove it.
755 for dummy in range(3):
756 today = self.theclass.today()
757 ts = time.time()
758 todayagain = self.theclass.fromtimestamp(ts)
759 if today == todayagain:
760 break
761 # There are several legit reasons that could fail:
762 # 1. It recently became midnight, between the today() and the
763 # time() calls.
764 # 2. The platform time() has such fine resolution that we'll
765 # never get the same value twice.
766 # 3. The platform time() has poor resolution, and we just
767 # happened to call today() right before a resolution quantum
768 # boundary.
769 # 4. The system clock got fiddled between calls.
770 # In any case, wait a little while and try again.
771 time.sleep(0.1)
772
773 # It worked or it didn't. If it didn't, assume it's reason #2, and
774 # let the test pass if they're within half a second of each other.
775 self.failUnless(today == todayagain or
776 abs(todayagain - today) < timedelta(seconds=0.5))
777
778 def test_weekday(self):
779 for i in range(7):
780 # March 4, 2002 is a Monday
781 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
782 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
783 # January 2, 1956 is a Monday
784 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
785 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
786
787 def test_isocalendar(self):
788 # Check examples from
789 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
790 for i in range(7):
791 d = self.theclass(2003, 12, 22+i)
792 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
793 d = self.theclass(2003, 12, 29) + timedelta(i)
794 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
795 d = self.theclass(2004, 1, 5+i)
796 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
797 d = self.theclass(2009, 12, 21+i)
798 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
799 d = self.theclass(2009, 12, 28) + timedelta(i)
800 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
801 d = self.theclass(2010, 1, 4+i)
802 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
803
804 def test_iso_long_years(self):
805 # Calculate long ISO years and compare to table from
806 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
807 ISO_LONG_YEARS_TABLE = """
808 4 32 60 88
809 9 37 65 93
810 15 43 71 99
811 20 48 76
812 26 54 82
813
814 105 133 161 189
815 111 139 167 195
816 116 144 172
817 122 150 178
818 128 156 184
819
820 201 229 257 285
821 207 235 263 291
822 212 240 268 296
823 218 246 274
824 224 252 280
825
826 303 331 359 387
827 308 336 364 392
828 314 342 370 398
829 320 348 376
830 325 353 381
831 """
832 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
833 iso_long_years.sort()
834 L = []
835 for i in range(400):
836 d = self.theclass(2000+i, 12, 31)
837 d1 = self.theclass(1600+i, 12, 31)
838 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
839 if d.isocalendar()[1] == 53:
840 L.append(i)
841 self.assertEqual(L, iso_long_years)
842
843 def test_isoformat(self):
844 t = self.theclass(2, 3, 2)
845 self.assertEqual(t.isoformat(), "0002-03-02")
846
847 def test_ctime(self):
848 t = self.theclass(2002, 3, 2)
849 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
850
851 def test_strftime(self):
852 t = self.theclass(2005, 3, 2)
853 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000854 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000855 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000856
857 self.assertRaises(TypeError, t.strftime) # needs an arg
858 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
859 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
860
861 # A naive object replaces %z and %Z w/ empty strings.
862 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
863
864 def test_resolution_info(self):
865 self.assert_(isinstance(self.theclass.min, self.theclass))
866 self.assert_(isinstance(self.theclass.max, self.theclass))
867 self.assert_(isinstance(self.theclass.resolution, timedelta))
868 self.assert_(self.theclass.max > self.theclass.min)
869
870 def test_extreme_timedelta(self):
871 big = self.theclass.max - self.theclass.min
872 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
873 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
874 # n == 315537897599999999 ~= 2**58.13
875 justasbig = timedelta(0, 0, n)
876 self.assertEqual(big, justasbig)
877 self.assertEqual(self.theclass.min + big, self.theclass.max)
878 self.assertEqual(self.theclass.max - big, self.theclass.min)
879
880 def test_timetuple(self):
881 for i in range(7):
882 # January 2, 1956 is a Monday (0)
883 d = self.theclass(1956, 1, 2+i)
884 t = d.timetuple()
885 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
886 # February 1, 1956 is a Wednesday (2)
887 d = self.theclass(1956, 2, 1+i)
888 t = d.timetuple()
889 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
890 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
891 # of the year.
892 d = self.theclass(1956, 3, 1+i)
893 t = d.timetuple()
894 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
895 self.assertEqual(t.tm_year, 1956)
896 self.assertEqual(t.tm_mon, 3)
897 self.assertEqual(t.tm_mday, 1+i)
898 self.assertEqual(t.tm_hour, 0)
899 self.assertEqual(t.tm_min, 0)
900 self.assertEqual(t.tm_sec, 0)
901 self.assertEqual(t.tm_wday, (3+i)%7)
902 self.assertEqual(t.tm_yday, 61+i)
903 self.assertEqual(t.tm_isdst, -1)
904
905 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000906 args = 6, 7, 23
907 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000908 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000909 green = pickler.dumps(orig, proto)
910 derived = unpickler.loads(green)
911 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000912
913 def test_compare(self):
914 t1 = self.theclass(2, 3, 4)
915 t2 = self.theclass(2, 3, 4)
916 self.failUnless(t1 == t2)
917 self.failUnless(t1 <= t2)
918 self.failUnless(t1 >= t2)
919 self.failUnless(not t1 != t2)
920 self.failUnless(not t1 < t2)
921 self.failUnless(not t1 > t2)
922 self.assertEqual(cmp(t1, t2), 0)
923 self.assertEqual(cmp(t2, t1), 0)
924
925 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
926 t2 = self.theclass(*args) # this is larger than t1
927 self.failUnless(t1 < t2)
928 self.failUnless(t2 > t1)
929 self.failUnless(t1 <= t2)
930 self.failUnless(t2 >= t1)
931 self.failUnless(t1 != t2)
932 self.failUnless(t2 != t1)
933 self.failUnless(not t1 == t2)
934 self.failUnless(not t2 == t1)
935 self.failUnless(not t1 > t2)
936 self.failUnless(not t2 < t1)
937 self.failUnless(not t1 >= t2)
938 self.failUnless(not t2 <= t1)
939 self.assertEqual(cmp(t1, t2), -1)
940 self.assertEqual(cmp(t2, t1), 1)
941
Tim Peters68124bb2003-02-08 03:46:31 +0000942 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000943 self.assertEqual(t1 == badarg, False)
944 self.assertEqual(t1 != badarg, True)
945 self.assertEqual(badarg == t1, False)
946 self.assertEqual(badarg != t1, True)
947
Tim Peters2a799bf2002-12-16 20:18:38 +0000948 self.assertRaises(TypeError, lambda: t1 < badarg)
949 self.assertRaises(TypeError, lambda: t1 > badarg)
950 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000951 self.assertRaises(TypeError, lambda: badarg <= t1)
952 self.assertRaises(TypeError, lambda: badarg < t1)
953 self.assertRaises(TypeError, lambda: badarg > t1)
954 self.assertRaises(TypeError, lambda: badarg >= t1)
955
Tim Peters8d81a012003-01-24 22:36:34 +0000956 def test_mixed_compare(self):
957 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000958
959 # Our class can be compared for equality to other classes
960 self.assertEqual(our == 1, False)
961 self.assertEqual(1 == our, False)
962 self.assertEqual(our != 1, True)
963 self.assertEqual(1 != our, True)
964
965 # But the ordering is undefined
966 self.assertRaises(TypeError, lambda: our < 1)
967 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000968 self.assertRaises(TypeError, cmp, our, 1)
969 self.assertRaises(TypeError, cmp, 1, our)
970
Guido van Rossum19960592006-08-24 17:29:38 +0000971 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +0000972
Guido van Rossum19960592006-08-24 17:29:38 +0000973 class SomeClass:
974 pass
975
976 their = SomeClass()
977 self.assertEqual(our == their, False)
978 self.assertEqual(their == our, False)
979 self.assertEqual(our != their, True)
980 self.assertEqual(their != our, True)
981 self.assertRaises(TypeError, lambda: our < their)
982 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000983 self.assertRaises(TypeError, cmp, our, their)
Guido van Rossum19960592006-08-24 17:29:38 +0000984 self.assertRaises(TypeError, cmp, their, our)
Tim Peters8d81a012003-01-24 22:36:34 +0000985
Guido van Rossum19960592006-08-24 17:29:38 +0000986 # However, if the other class explicitly defines ordering
987 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +0000988
Guido van Rossum19960592006-08-24 17:29:38 +0000989 class LargerThanAnything:
990 def __lt__(self, other):
991 return False
992 def __le__(self, other):
993 return isinstance(other, LargerThanAnything)
994 def __eq__(self, other):
995 return isinstance(other, LargerThanAnything)
996 def __ne__(self, other):
997 return not isinstance(other, LargerThanAnything)
998 def __gt__(self, other):
999 return not isinstance(other, LargerThanAnything)
1000 def __ge__(self, other):
1001 return True
1002
1003 their = LargerThanAnything()
1004 self.assertEqual(our == their, False)
1005 self.assertEqual(their == our, False)
1006 self.assertEqual(our != their, True)
1007 self.assertEqual(their != our, True)
1008 self.assertEqual(our < their, True)
1009 self.assertEqual(their < our, False)
1010 self.assertEqual(cmp(our, their), -1)
1011 self.assertEqual(cmp(their, our), 1)
Tim Peters8d81a012003-01-24 22:36:34 +00001012
Tim Peters2a799bf2002-12-16 20:18:38 +00001013 def test_bool(self):
1014 # All dates are considered true.
1015 self.failUnless(self.theclass.min)
1016 self.failUnless(self.theclass.max)
1017
Tim Petersd6844152002-12-22 20:58:42 +00001018 def test_srftime_out_of_range(self):
1019 # For nasty technical reasons, we can't handle years before 1900.
1020 cls = self.theclass
1021 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1022 for y in 1, 49, 51, 99, 100, 1000, 1899:
1023 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001024
1025 def test_replace(self):
1026 cls = self.theclass
1027 args = [1, 2, 3]
1028 base = cls(*args)
1029 self.assertEqual(base, base.replace())
1030
1031 i = 0
1032 for name, newval in (("year", 2),
1033 ("month", 3),
1034 ("day", 4)):
1035 newargs = args[:]
1036 newargs[i] = newval
1037 expected = cls(*newargs)
1038 got = base.replace(**{name: newval})
1039 self.assertEqual(expected, got)
1040 i += 1
1041
1042 # Out of bounds.
1043 base = cls(2000, 2, 29)
1044 self.assertRaises(ValueError, base.replace, year=2001)
1045
Tim Petersa98924a2003-05-17 05:55:19 +00001046 def test_subclass_date(self):
1047
1048 class C(self.theclass):
1049 theAnswer = 42
1050
1051 def __new__(cls, *args, **kws):
1052 temp = kws.copy()
1053 extra = temp.pop('extra')
1054 result = self.theclass.__new__(cls, *args, **temp)
1055 result.extra = extra
1056 return result
1057
1058 def newmeth(self, start):
1059 return start + self.year + self.month
1060
1061 args = 2003, 4, 14
1062
1063 dt1 = self.theclass(*args)
1064 dt2 = C(*args, **{'extra': 7})
1065
1066 self.assertEqual(dt2.__class__, C)
1067 self.assertEqual(dt2.theAnswer, 42)
1068 self.assertEqual(dt2.extra, 7)
1069 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1070 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1071
Tim Peters604c0132004-06-07 23:04:33 +00001072 def test_pickling_subclass_date(self):
1073
1074 args = 6, 7, 23
1075 orig = SubclassDate(*args)
1076 for pickler, unpickler, proto in pickle_choices:
1077 green = pickler.dumps(orig, proto)
1078 derived = unpickler.loads(green)
1079 self.assertEqual(orig, derived)
1080
Tim Peters3f606292004-03-21 23:38:41 +00001081 def test_backdoor_resistance(self):
1082 # For fast unpickling, the constructor accepts a pickle string.
1083 # This is a low-overhead backdoor. A user can (by intent or
1084 # mistake) pass a string directly, which (if it's the right length)
1085 # will get treated like a pickle, and bypass the normal sanity
1086 # checks in the constructor. This can create insane objects.
1087 # The constructor doesn't want to burn the time to validate all
1088 # fields, but does check the month field. This stops, e.g.,
1089 # datetime.datetime('1995-03-25') from yielding an insane object.
1090 base = '1995-03-25'
1091 if not issubclass(self.theclass, datetime):
1092 base = base[:4]
1093 for month_byte in '9', chr(0), chr(13), '\xff':
1094 self.assertRaises(TypeError, self.theclass,
1095 base[:2] + month_byte + base[3:])
1096 for ord_byte in range(1, 13):
1097 # This shouldn't blow up because of the month byte alone. If
1098 # the implementation changes to do more-careful checking, it may
1099 # blow up because other fields are insane.
1100 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001101
Tim Peters2a799bf2002-12-16 20:18:38 +00001102#############################################################################
1103# datetime tests
1104
Tim Peters604c0132004-06-07 23:04:33 +00001105class SubclassDatetime(datetime):
1106 sub_var = 1
1107
Tim Peters2a799bf2002-12-16 20:18:38 +00001108class TestDateTime(TestDate):
1109
1110 theclass = datetime
1111
1112 def test_basic_attributes(self):
1113 dt = self.theclass(2002, 3, 1, 12, 0)
1114 self.assertEqual(dt.year, 2002)
1115 self.assertEqual(dt.month, 3)
1116 self.assertEqual(dt.day, 1)
1117 self.assertEqual(dt.hour, 12)
1118 self.assertEqual(dt.minute, 0)
1119 self.assertEqual(dt.second, 0)
1120 self.assertEqual(dt.microsecond, 0)
1121
1122 def test_basic_attributes_nonzero(self):
1123 # Make sure all attributes are non-zero so bugs in
1124 # bit-shifting access show up.
1125 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1126 self.assertEqual(dt.year, 2002)
1127 self.assertEqual(dt.month, 3)
1128 self.assertEqual(dt.day, 1)
1129 self.assertEqual(dt.hour, 12)
1130 self.assertEqual(dt.minute, 59)
1131 self.assertEqual(dt.second, 59)
1132 self.assertEqual(dt.microsecond, 8000)
1133
1134 def test_roundtrip(self):
1135 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1136 self.theclass.now()):
1137 # Verify dt -> string -> datetime identity.
1138 s = repr(dt)
1139 self.failUnless(s.startswith('datetime.'))
1140 s = s[9:]
1141 dt2 = eval(s)
1142 self.assertEqual(dt, dt2)
1143
1144 # Verify identity via reconstructing from pieces.
1145 dt2 = self.theclass(dt.year, dt.month, dt.day,
1146 dt.hour, dt.minute, dt.second,
1147 dt.microsecond)
1148 self.assertEqual(dt, dt2)
1149
1150 def test_isoformat(self):
1151 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1152 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1153 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1154 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1155 # str is ISO format with the separator forced to a blank.
1156 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1157
1158 t = self.theclass(2, 3, 2)
1159 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1160 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1161 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1162 # str is ISO format with the separator forced to a blank.
1163 self.assertEqual(str(t), "0002-03-02 00:00:00")
1164
1165 def test_more_ctime(self):
1166 # Test fields that TestDate doesn't touch.
1167 import time
1168
1169 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1170 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1171 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1172 # out. The difference is that t.ctime() produces " 2" for the day,
1173 # but platform ctime() produces "02" for the day. According to
1174 # C99, t.ctime() is correct here.
1175 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1176
1177 # So test a case where that difference doesn't matter.
1178 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1179 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1180
1181 def test_tz_independent_comparing(self):
1182 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1183 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1184 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1185 self.assertEqual(dt1, dt3)
1186 self.assert_(dt2 > dt3)
1187
1188 # Make sure comparison doesn't forget microseconds, and isn't done
1189 # via comparing a float timestamp (an IEEE double doesn't have enough
1190 # precision to span microsecond resolution across years 1 thru 9999,
1191 # so comparing via timestamp necessarily calls some distinct values
1192 # equal).
1193 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1194 us = timedelta(microseconds=1)
1195 dt2 = dt1 + us
1196 self.assertEqual(dt2 - dt1, us)
1197 self.assert_(dt1 < dt2)
1198
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001199 def test_strftime_with_bad_tzname_replace(self):
1200 # verify ok if tzinfo.tzname().replace() returns a non-string
1201 class MyTzInfo(FixedOffset):
1202 def tzname(self, dt):
1203 class MyStr(str):
1204 def replace(self, *args):
1205 return None
1206 return MyStr('name')
1207 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1208 self.assertRaises(TypeError, t.strftime, '%Z')
1209
Tim Peters2a799bf2002-12-16 20:18:38 +00001210 def test_bad_constructor_arguments(self):
1211 # bad years
1212 self.theclass(MINYEAR, 1, 1) # no exception
1213 self.theclass(MAXYEAR, 1, 1) # no exception
1214 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1215 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1216 # bad months
1217 self.theclass(2000, 1, 1) # no exception
1218 self.theclass(2000, 12, 1) # no exception
1219 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1220 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1221 # bad days
1222 self.theclass(2000, 2, 29) # no exception
1223 self.theclass(2004, 2, 29) # no exception
1224 self.theclass(2400, 2, 29) # no exception
1225 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1226 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1227 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1228 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1229 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1230 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1231 # bad hours
1232 self.theclass(2000, 1, 31, 0) # no exception
1233 self.theclass(2000, 1, 31, 23) # no exception
1234 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1235 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1236 # bad minutes
1237 self.theclass(2000, 1, 31, 23, 0) # no exception
1238 self.theclass(2000, 1, 31, 23, 59) # no exception
1239 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1240 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1241 # bad seconds
1242 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1243 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1244 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1245 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1246 # bad microseconds
1247 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1248 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1249 self.assertRaises(ValueError, self.theclass,
1250 2000, 1, 31, 23, 59, 59, -1)
1251 self.assertRaises(ValueError, self.theclass,
1252 2000, 1, 31, 23, 59, 59,
1253 1000000)
1254
1255 def test_hash_equality(self):
1256 d = self.theclass(2000, 12, 31, 23, 30, 17)
1257 e = self.theclass(2000, 12, 31, 23, 30, 17)
1258 self.assertEqual(d, e)
1259 self.assertEqual(hash(d), hash(e))
1260
1261 dic = {d: 1}
1262 dic[e] = 2
1263 self.assertEqual(len(dic), 1)
1264 self.assertEqual(dic[d], 2)
1265 self.assertEqual(dic[e], 2)
1266
1267 d = self.theclass(2001, 1, 1, 0, 5, 17)
1268 e = self.theclass(2001, 1, 1, 0, 5, 17)
1269 self.assertEqual(d, e)
1270 self.assertEqual(hash(d), hash(e))
1271
1272 dic = {d: 1}
1273 dic[e] = 2
1274 self.assertEqual(len(dic), 1)
1275 self.assertEqual(dic[d], 2)
1276 self.assertEqual(dic[e], 2)
1277
1278 def test_computations(self):
1279 a = self.theclass(2002, 1, 31)
1280 b = self.theclass(1956, 1, 31)
1281 diff = a-b
1282 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1283 self.assertEqual(diff.seconds, 0)
1284 self.assertEqual(diff.microseconds, 0)
1285 a = self.theclass(2002, 3, 2, 17, 6)
1286 millisec = timedelta(0, 0, 1000)
1287 hour = timedelta(0, 3600)
1288 day = timedelta(1)
1289 week = timedelta(7)
1290 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1291 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1292 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1293 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1294 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1295 self.assertEqual(a - hour, a + -hour)
1296 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1297 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1298 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1299 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1300 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1301 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1302 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1303 self.assertEqual((a + week) - a, week)
1304 self.assertEqual((a + day) - a, day)
1305 self.assertEqual((a + hour) - a, hour)
1306 self.assertEqual((a + millisec) - a, millisec)
1307 self.assertEqual((a - week) - a, -week)
1308 self.assertEqual((a - day) - a, -day)
1309 self.assertEqual((a - hour) - a, -hour)
1310 self.assertEqual((a - millisec) - a, -millisec)
1311 self.assertEqual(a - (a + week), -week)
1312 self.assertEqual(a - (a + day), -day)
1313 self.assertEqual(a - (a + hour), -hour)
1314 self.assertEqual(a - (a + millisec), -millisec)
1315 self.assertEqual(a - (a - week), week)
1316 self.assertEqual(a - (a - day), day)
1317 self.assertEqual(a - (a - hour), hour)
1318 self.assertEqual(a - (a - millisec), millisec)
1319 self.assertEqual(a + (week + day + hour + millisec),
1320 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1321 self.assertEqual(a + (week + day + hour + millisec),
1322 (((a + week) + day) + hour) + millisec)
1323 self.assertEqual(a - (week + day + hour + millisec),
1324 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1325 self.assertEqual(a - (week + day + hour + millisec),
1326 (((a - week) - day) - hour) - millisec)
1327 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001328 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001329 self.assertRaises(TypeError, lambda: a+i)
1330 self.assertRaises(TypeError, lambda: a-i)
1331 self.assertRaises(TypeError, lambda: i+a)
1332 self.assertRaises(TypeError, lambda: i-a)
1333
1334 # delta - datetime is senseless.
1335 self.assertRaises(TypeError, lambda: day - a)
1336 # mixing datetime and (delta or datetime) via * or // is senseless
1337 self.assertRaises(TypeError, lambda: day * a)
1338 self.assertRaises(TypeError, lambda: a * day)
1339 self.assertRaises(TypeError, lambda: day // a)
1340 self.assertRaises(TypeError, lambda: a // day)
1341 self.assertRaises(TypeError, lambda: a * a)
1342 self.assertRaises(TypeError, lambda: a // a)
1343 # datetime + datetime is senseless
1344 self.assertRaises(TypeError, lambda: a + a)
1345
1346 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001347 args = 6, 7, 23, 20, 59, 1, 64**2
1348 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001349 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001350 green = pickler.dumps(orig, proto)
1351 derived = unpickler.loads(green)
1352 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001353
Guido van Rossum275666f2003-02-07 21:49:01 +00001354 def test_more_pickling(self):
1355 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1356 s = pickle.dumps(a)
1357 b = pickle.loads(s)
1358 self.assertEqual(b.year, 2003)
1359 self.assertEqual(b.month, 2)
1360 self.assertEqual(b.day, 7)
1361
Tim Peters604c0132004-06-07 23:04:33 +00001362 def test_pickling_subclass_datetime(self):
1363 args = 6, 7, 23, 20, 59, 1, 64**2
1364 orig = SubclassDatetime(*args)
1365 for pickler, unpickler, proto in pickle_choices:
1366 green = pickler.dumps(orig, proto)
1367 derived = unpickler.loads(green)
1368 self.assertEqual(orig, derived)
1369
Tim Peters2a799bf2002-12-16 20:18:38 +00001370 def test_more_compare(self):
1371 # The test_compare() inherited from TestDate covers the error cases.
1372 # We just want to test lexicographic ordering on the members datetime
1373 # has that date lacks.
1374 args = [2000, 11, 29, 20, 58, 16, 999998]
1375 t1 = self.theclass(*args)
1376 t2 = self.theclass(*args)
1377 self.failUnless(t1 == t2)
1378 self.failUnless(t1 <= t2)
1379 self.failUnless(t1 >= t2)
1380 self.failUnless(not t1 != t2)
1381 self.failUnless(not t1 < t2)
1382 self.failUnless(not t1 > t2)
1383 self.assertEqual(cmp(t1, t2), 0)
1384 self.assertEqual(cmp(t2, t1), 0)
1385
1386 for i in range(len(args)):
1387 newargs = args[:]
1388 newargs[i] = args[i] + 1
1389 t2 = self.theclass(*newargs) # this is larger than t1
1390 self.failUnless(t1 < t2)
1391 self.failUnless(t2 > t1)
1392 self.failUnless(t1 <= t2)
1393 self.failUnless(t2 >= t1)
1394 self.failUnless(t1 != t2)
1395 self.failUnless(t2 != t1)
1396 self.failUnless(not t1 == t2)
1397 self.failUnless(not t2 == t1)
1398 self.failUnless(not t1 > t2)
1399 self.failUnless(not t2 < t1)
1400 self.failUnless(not t1 >= t2)
1401 self.failUnless(not t2 <= t1)
1402 self.assertEqual(cmp(t1, t2), -1)
1403 self.assertEqual(cmp(t2, t1), 1)
1404
1405
1406 # A helper for timestamp constructor tests.
1407 def verify_field_equality(self, expected, got):
1408 self.assertEqual(expected.tm_year, got.year)
1409 self.assertEqual(expected.tm_mon, got.month)
1410 self.assertEqual(expected.tm_mday, got.day)
1411 self.assertEqual(expected.tm_hour, got.hour)
1412 self.assertEqual(expected.tm_min, got.minute)
1413 self.assertEqual(expected.tm_sec, got.second)
1414
1415 def test_fromtimestamp(self):
1416 import time
1417
1418 ts = time.time()
1419 expected = time.localtime(ts)
1420 got = self.theclass.fromtimestamp(ts)
1421 self.verify_field_equality(expected, got)
1422
1423 def test_utcfromtimestamp(self):
1424 import time
1425
1426 ts = time.time()
1427 expected = time.gmtime(ts)
1428 got = self.theclass.utcfromtimestamp(ts)
1429 self.verify_field_equality(expected, got)
1430
Thomas Wouters477c8d52006-05-27 19:21:47 +00001431 def test_microsecond_rounding(self):
1432 # Test whether fromtimestamp "rounds up" floats that are less
1433 # than one microsecond smaller than an integer.
1434 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1435 self.theclass.fromtimestamp(1))
1436
Tim Peters1b6f7a92004-06-20 02:50:16 +00001437 def test_insane_fromtimestamp(self):
1438 # It's possible that some platform maps time_t to double,
1439 # and that this test will fail there. This test should
1440 # exempt such platforms (provided they return reasonable
1441 # results!).
1442 for insane in -1e200, 1e200:
1443 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1444 insane)
1445
1446 def test_insane_utcfromtimestamp(self):
1447 # It's possible that some platform maps time_t to double,
1448 # and that this test will fail there. This test should
1449 # exempt such platforms (provided they return reasonable
1450 # results!).
1451 for insane in -1e200, 1e200:
1452 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1453 insane)
1454
Tim Peters2a799bf2002-12-16 20:18:38 +00001455 def test_utcnow(self):
1456 import time
1457
1458 # Call it a success if utcnow() and utcfromtimestamp() are within
1459 # a second of each other.
1460 tolerance = timedelta(seconds=1)
1461 for dummy in range(3):
1462 from_now = self.theclass.utcnow()
1463 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1464 if abs(from_timestamp - from_now) <= tolerance:
1465 break
1466 # Else try again a few times.
1467 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1468
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001469 def test_strptime(self):
1470 import time
1471
1472 string = '2004-12-01 13:02:47'
1473 format = '%Y-%m-%d %H:%M:%S'
1474 expected = self.theclass(*(time.strptime(string, format)[0:6]))
1475 got = self.theclass.strptime(string, format)
1476 self.assertEqual(expected, got)
1477
Tim Peters2a799bf2002-12-16 20:18:38 +00001478 def test_more_timetuple(self):
1479 # This tests fields beyond those tested by the TestDate.test_timetuple.
1480 t = self.theclass(2004, 12, 31, 6, 22, 33)
1481 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1482 self.assertEqual(t.timetuple(),
1483 (t.year, t.month, t.day,
1484 t.hour, t.minute, t.second,
1485 t.weekday(),
1486 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1487 -1))
1488 tt = t.timetuple()
1489 self.assertEqual(tt.tm_year, t.year)
1490 self.assertEqual(tt.tm_mon, t.month)
1491 self.assertEqual(tt.tm_mday, t.day)
1492 self.assertEqual(tt.tm_hour, t.hour)
1493 self.assertEqual(tt.tm_min, t.minute)
1494 self.assertEqual(tt.tm_sec, t.second)
1495 self.assertEqual(tt.tm_wday, t.weekday())
1496 self.assertEqual(tt.tm_yday, t.toordinal() -
1497 date(t.year, 1, 1).toordinal() + 1)
1498 self.assertEqual(tt.tm_isdst, -1)
1499
1500 def test_more_strftime(self):
1501 # This tests fields beyond those tested by the TestDate.test_strftime.
1502 t = self.theclass(2004, 12, 31, 6, 22, 33)
1503 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1504 "12 31 04 33 22 06 366")
1505
1506 def test_extract(self):
1507 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1508 self.assertEqual(dt.date(), date(2002, 3, 4))
1509 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1510
1511 def test_combine(self):
1512 d = date(2002, 3, 4)
1513 t = time(18, 45, 3, 1234)
1514 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1515 combine = self.theclass.combine
1516 dt = combine(d, t)
1517 self.assertEqual(dt, expected)
1518
1519 dt = combine(time=t, date=d)
1520 self.assertEqual(dt, expected)
1521
1522 self.assertEqual(d, dt.date())
1523 self.assertEqual(t, dt.time())
1524 self.assertEqual(dt, combine(dt.date(), dt.time()))
1525
1526 self.assertRaises(TypeError, combine) # need an arg
1527 self.assertRaises(TypeError, combine, d) # need two args
1528 self.assertRaises(TypeError, combine, t, d) # args reversed
1529 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1530 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1531
Tim Peters12bf3392002-12-24 05:41:27 +00001532 def test_replace(self):
1533 cls = self.theclass
1534 args = [1, 2, 3, 4, 5, 6, 7]
1535 base = cls(*args)
1536 self.assertEqual(base, base.replace())
1537
1538 i = 0
1539 for name, newval in (("year", 2),
1540 ("month", 3),
1541 ("day", 4),
1542 ("hour", 5),
1543 ("minute", 6),
1544 ("second", 7),
1545 ("microsecond", 8)):
1546 newargs = args[:]
1547 newargs[i] = newval
1548 expected = cls(*newargs)
1549 got = base.replace(**{name: newval})
1550 self.assertEqual(expected, got)
1551 i += 1
1552
1553 # Out of bounds.
1554 base = cls(2000, 2, 29)
1555 self.assertRaises(ValueError, base.replace, year=2001)
1556
Tim Peters80475bb2002-12-25 07:40:55 +00001557 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001558 # Pretty boring! The TZ test is more interesting here. astimezone()
1559 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001560 dt = self.theclass.now()
1561 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001562 self.assertRaises(TypeError, dt.astimezone) # not enough args
1563 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1564 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001565 self.assertRaises(ValueError, dt.astimezone, f) # naive
1566 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001567
Tim Peters52dcce22003-01-23 16:36:11 +00001568 class Bogus(tzinfo):
1569 def utcoffset(self, dt): return None
1570 def dst(self, dt): return timedelta(0)
1571 bog = Bogus()
1572 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1573
1574 class AlsoBogus(tzinfo):
1575 def utcoffset(self, dt): return timedelta(0)
1576 def dst(self, dt): return None
1577 alsobog = AlsoBogus()
1578 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001579
Tim Petersa98924a2003-05-17 05:55:19 +00001580 def test_subclass_datetime(self):
1581
1582 class C(self.theclass):
1583 theAnswer = 42
1584
1585 def __new__(cls, *args, **kws):
1586 temp = kws.copy()
1587 extra = temp.pop('extra')
1588 result = self.theclass.__new__(cls, *args, **temp)
1589 result.extra = extra
1590 return result
1591
1592 def newmeth(self, start):
1593 return start + self.year + self.month + self.second
1594
1595 args = 2003, 4, 14, 12, 13, 41
1596
1597 dt1 = self.theclass(*args)
1598 dt2 = C(*args, **{'extra': 7})
1599
1600 self.assertEqual(dt2.__class__, C)
1601 self.assertEqual(dt2.theAnswer, 42)
1602 self.assertEqual(dt2.extra, 7)
1603 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1604 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1605 dt1.second - 7)
1606
Tim Peters604c0132004-06-07 23:04:33 +00001607class SubclassTime(time):
1608 sub_var = 1
1609
Tim Peters07534a62003-02-07 22:50:28 +00001610class TestTime(HarmlessMixedComparison):
Tim Peters2a799bf2002-12-16 20:18:38 +00001611
1612 theclass = time
1613
1614 def test_basic_attributes(self):
1615 t = self.theclass(12, 0)
1616 self.assertEqual(t.hour, 12)
1617 self.assertEqual(t.minute, 0)
1618 self.assertEqual(t.second, 0)
1619 self.assertEqual(t.microsecond, 0)
1620
1621 def test_basic_attributes_nonzero(self):
1622 # Make sure all attributes are non-zero so bugs in
1623 # bit-shifting access show up.
1624 t = self.theclass(12, 59, 59, 8000)
1625 self.assertEqual(t.hour, 12)
1626 self.assertEqual(t.minute, 59)
1627 self.assertEqual(t.second, 59)
1628 self.assertEqual(t.microsecond, 8000)
1629
1630 def test_roundtrip(self):
1631 t = self.theclass(1, 2, 3, 4)
1632
1633 # Verify t -> string -> time identity.
1634 s = repr(t)
1635 self.failUnless(s.startswith('datetime.'))
1636 s = s[9:]
1637 t2 = eval(s)
1638 self.assertEqual(t, t2)
1639
1640 # Verify identity via reconstructing from pieces.
1641 t2 = self.theclass(t.hour, t.minute, t.second,
1642 t.microsecond)
1643 self.assertEqual(t, t2)
1644
1645 def test_comparing(self):
1646 args = [1, 2, 3, 4]
1647 t1 = self.theclass(*args)
1648 t2 = self.theclass(*args)
1649 self.failUnless(t1 == t2)
1650 self.failUnless(t1 <= t2)
1651 self.failUnless(t1 >= t2)
1652 self.failUnless(not t1 != t2)
1653 self.failUnless(not t1 < t2)
1654 self.failUnless(not t1 > t2)
1655 self.assertEqual(cmp(t1, t2), 0)
1656 self.assertEqual(cmp(t2, t1), 0)
1657
1658 for i in range(len(args)):
1659 newargs = args[:]
1660 newargs[i] = args[i] + 1
1661 t2 = self.theclass(*newargs) # this is larger than t1
1662 self.failUnless(t1 < t2)
1663 self.failUnless(t2 > t1)
1664 self.failUnless(t1 <= t2)
1665 self.failUnless(t2 >= t1)
1666 self.failUnless(t1 != t2)
1667 self.failUnless(t2 != t1)
1668 self.failUnless(not t1 == t2)
1669 self.failUnless(not t2 == t1)
1670 self.failUnless(not t1 > t2)
1671 self.failUnless(not t2 < t1)
1672 self.failUnless(not t1 >= t2)
1673 self.failUnless(not t2 <= t1)
1674 self.assertEqual(cmp(t1, t2), -1)
1675 self.assertEqual(cmp(t2, t1), 1)
1676
Tim Peters68124bb2003-02-08 03:46:31 +00001677 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001678 self.assertEqual(t1 == badarg, False)
1679 self.assertEqual(t1 != badarg, True)
1680 self.assertEqual(badarg == t1, False)
1681 self.assertEqual(badarg != t1, True)
1682
Tim Peters2a799bf2002-12-16 20:18:38 +00001683 self.assertRaises(TypeError, lambda: t1 <= badarg)
1684 self.assertRaises(TypeError, lambda: t1 < badarg)
1685 self.assertRaises(TypeError, lambda: t1 > badarg)
1686 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001687 self.assertRaises(TypeError, lambda: badarg <= t1)
1688 self.assertRaises(TypeError, lambda: badarg < t1)
1689 self.assertRaises(TypeError, lambda: badarg > t1)
1690 self.assertRaises(TypeError, lambda: badarg >= t1)
1691
1692 def test_bad_constructor_arguments(self):
1693 # bad hours
1694 self.theclass(0, 0) # no exception
1695 self.theclass(23, 0) # no exception
1696 self.assertRaises(ValueError, self.theclass, -1, 0)
1697 self.assertRaises(ValueError, self.theclass, 24, 0)
1698 # bad minutes
1699 self.theclass(23, 0) # no exception
1700 self.theclass(23, 59) # no exception
1701 self.assertRaises(ValueError, self.theclass, 23, -1)
1702 self.assertRaises(ValueError, self.theclass, 23, 60)
1703 # bad seconds
1704 self.theclass(23, 59, 0) # no exception
1705 self.theclass(23, 59, 59) # no exception
1706 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1707 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1708 # bad microseconds
1709 self.theclass(23, 59, 59, 0) # no exception
1710 self.theclass(23, 59, 59, 999999) # no exception
1711 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1712 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1713
1714 def test_hash_equality(self):
1715 d = self.theclass(23, 30, 17)
1716 e = self.theclass(23, 30, 17)
1717 self.assertEqual(d, e)
1718 self.assertEqual(hash(d), hash(e))
1719
1720 dic = {d: 1}
1721 dic[e] = 2
1722 self.assertEqual(len(dic), 1)
1723 self.assertEqual(dic[d], 2)
1724 self.assertEqual(dic[e], 2)
1725
1726 d = self.theclass(0, 5, 17)
1727 e = self.theclass(0, 5, 17)
1728 self.assertEqual(d, e)
1729 self.assertEqual(hash(d), hash(e))
1730
1731 dic = {d: 1}
1732 dic[e] = 2
1733 self.assertEqual(len(dic), 1)
1734 self.assertEqual(dic[d], 2)
1735 self.assertEqual(dic[e], 2)
1736
1737 def test_isoformat(self):
1738 t = self.theclass(4, 5, 1, 123)
1739 self.assertEqual(t.isoformat(), "04:05:01.000123")
1740 self.assertEqual(t.isoformat(), str(t))
1741
1742 t = self.theclass()
1743 self.assertEqual(t.isoformat(), "00:00:00")
1744 self.assertEqual(t.isoformat(), str(t))
1745
1746 t = self.theclass(microsecond=1)
1747 self.assertEqual(t.isoformat(), "00:00:00.000001")
1748 self.assertEqual(t.isoformat(), str(t))
1749
1750 t = self.theclass(microsecond=10)
1751 self.assertEqual(t.isoformat(), "00:00:00.000010")
1752 self.assertEqual(t.isoformat(), str(t))
1753
1754 t = self.theclass(microsecond=100)
1755 self.assertEqual(t.isoformat(), "00:00:00.000100")
1756 self.assertEqual(t.isoformat(), str(t))
1757
1758 t = self.theclass(microsecond=1000)
1759 self.assertEqual(t.isoformat(), "00:00:00.001000")
1760 self.assertEqual(t.isoformat(), str(t))
1761
1762 t = self.theclass(microsecond=10000)
1763 self.assertEqual(t.isoformat(), "00:00:00.010000")
1764 self.assertEqual(t.isoformat(), str(t))
1765
1766 t = self.theclass(microsecond=100000)
1767 self.assertEqual(t.isoformat(), "00:00:00.100000")
1768 self.assertEqual(t.isoformat(), str(t))
1769
Thomas Wouterscf297e42007-02-23 15:07:44 +00001770 def test_1653736(self):
1771 # verify it doesn't accept extra keyword arguments
1772 t = self.theclass(second=1)
1773 self.assertRaises(TypeError, t.isoformat, foo=3)
1774
Tim Peters2a799bf2002-12-16 20:18:38 +00001775 def test_strftime(self):
1776 t = self.theclass(1, 2, 3, 4)
1777 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1778 # A naive object replaces %z and %Z with empty strings.
1779 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1780
1781 def test_str(self):
1782 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1783 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1784 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1785 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1786 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1787
1788 def test_repr(self):
1789 name = 'datetime.' + self.theclass.__name__
1790 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1791 "%s(1, 2, 3, 4)" % name)
1792 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1793 "%s(10, 2, 3, 4000)" % name)
1794 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1795 "%s(0, 2, 3, 400000)" % name)
1796 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1797 "%s(12, 2, 3)" % name)
1798 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1799 "%s(23, 15)" % name)
1800
1801 def test_resolution_info(self):
1802 self.assert_(isinstance(self.theclass.min, self.theclass))
1803 self.assert_(isinstance(self.theclass.max, self.theclass))
1804 self.assert_(isinstance(self.theclass.resolution, timedelta))
1805 self.assert_(self.theclass.max > self.theclass.min)
1806
1807 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001808 args = 20, 59, 16, 64**2
1809 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001810 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001811 green = pickler.dumps(orig, proto)
1812 derived = unpickler.loads(green)
1813 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001814
Tim Peters604c0132004-06-07 23:04:33 +00001815 def test_pickling_subclass_time(self):
1816 args = 20, 59, 16, 64**2
1817 orig = SubclassTime(*args)
1818 for pickler, unpickler, proto in pickle_choices:
1819 green = pickler.dumps(orig, proto)
1820 derived = unpickler.loads(green)
1821 self.assertEqual(orig, derived)
1822
Tim Peters2a799bf2002-12-16 20:18:38 +00001823 def test_bool(self):
1824 cls = self.theclass
1825 self.failUnless(cls(1))
1826 self.failUnless(cls(0, 1))
1827 self.failUnless(cls(0, 0, 1))
1828 self.failUnless(cls(0, 0, 0, 1))
1829 self.failUnless(not cls(0))
1830 self.failUnless(not cls())
1831
Tim Peters12bf3392002-12-24 05:41:27 +00001832 def test_replace(self):
1833 cls = self.theclass
1834 args = [1, 2, 3, 4]
1835 base = cls(*args)
1836 self.assertEqual(base, base.replace())
1837
1838 i = 0
1839 for name, newval in (("hour", 5),
1840 ("minute", 6),
1841 ("second", 7),
1842 ("microsecond", 8)):
1843 newargs = args[:]
1844 newargs[i] = newval
1845 expected = cls(*newargs)
1846 got = base.replace(**{name: newval})
1847 self.assertEqual(expected, got)
1848 i += 1
1849
1850 # Out of bounds.
1851 base = cls(1)
1852 self.assertRaises(ValueError, base.replace, hour=24)
1853 self.assertRaises(ValueError, base.replace, minute=-1)
1854 self.assertRaises(ValueError, base.replace, second=100)
1855 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1856
Tim Petersa98924a2003-05-17 05:55:19 +00001857 def test_subclass_time(self):
1858
1859 class C(self.theclass):
1860 theAnswer = 42
1861
1862 def __new__(cls, *args, **kws):
1863 temp = kws.copy()
1864 extra = temp.pop('extra')
1865 result = self.theclass.__new__(cls, *args, **temp)
1866 result.extra = extra
1867 return result
1868
1869 def newmeth(self, start):
1870 return start + self.hour + self.second
1871
1872 args = 4, 5, 6
1873
1874 dt1 = self.theclass(*args)
1875 dt2 = C(*args, **{'extra': 7})
1876
1877 self.assertEqual(dt2.__class__, C)
1878 self.assertEqual(dt2.theAnswer, 42)
1879 self.assertEqual(dt2.extra, 7)
1880 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1881 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1882
Armin Rigof4afb212005-11-07 07:15:48 +00001883 def test_backdoor_resistance(self):
1884 # see TestDate.test_backdoor_resistance().
1885 base = '2:59.0'
1886 for hour_byte in ' ', '9', chr(24), '\xff':
1887 self.assertRaises(TypeError, self.theclass,
1888 hour_byte + base[1:])
1889
Tim Peters855fe882002-12-22 03:43:39 +00001890# A mixin for classes with a tzinfo= argument. Subclasses must define
1891# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001892# must be legit (which is true for time and datetime).
Tim Peters855fe882002-12-22 03:43:39 +00001893class TZInfoBase(unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001894
Tim Petersbad8ff02002-12-30 20:52:32 +00001895 def test_argument_passing(self):
1896 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001897 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001898 class introspective(tzinfo):
1899 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001900 def utcoffset(self, dt):
1901 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001902 dst = utcoffset
1903
1904 obj = cls(1, 2, 3, tzinfo=introspective())
1905
Tim Peters0bf60bd2003-01-08 20:40:01 +00001906 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001907 self.assertEqual(obj.tzname(), expected)
1908
Tim Peters0bf60bd2003-01-08 20:40:01 +00001909 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001910 self.assertEqual(obj.utcoffset(), expected)
1911 self.assertEqual(obj.dst(), expected)
1912
Tim Peters855fe882002-12-22 03:43:39 +00001913 def test_bad_tzinfo_classes(self):
1914 cls = self.theclass
1915 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001916
Tim Peters855fe882002-12-22 03:43:39 +00001917 class NiceTry(object):
1918 def __init__(self): pass
1919 def utcoffset(self, dt): pass
1920 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1921
1922 class BetterTry(tzinfo):
1923 def __init__(self): pass
1924 def utcoffset(self, dt): pass
1925 b = BetterTry()
1926 t = cls(1, 1, 1, tzinfo=b)
1927 self.failUnless(t.tzinfo is b)
1928
1929 def test_utc_offset_out_of_bounds(self):
1930 class Edgy(tzinfo):
1931 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00001932 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00001933 def utcoffset(self, dt):
1934 return self.offset
1935
1936 cls = self.theclass
1937 for offset, legit in ((-1440, False),
1938 (-1439, True),
1939 (1439, True),
1940 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001941 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00001942 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001943 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00001944 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001945 else:
1946 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00001947 if legit:
1948 aofs = abs(offset)
1949 h, m = divmod(aofs, 60)
1950 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001951 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00001952 t = t.timetz()
1953 self.assertEqual(str(t), "01:02:03" + tag)
1954 else:
1955 self.assertRaises(ValueError, str, t)
1956
1957 def test_tzinfo_classes(self):
1958 cls = self.theclass
1959 class C1(tzinfo):
1960 def utcoffset(self, dt): return None
1961 def dst(self, dt): return None
1962 def tzname(self, dt): return None
1963 for t in (cls(1, 1, 1),
1964 cls(1, 1, 1, tzinfo=None),
1965 cls(1, 1, 1, tzinfo=C1())):
1966 self.failUnless(t.utcoffset() is None)
1967 self.failUnless(t.dst() is None)
1968 self.failUnless(t.tzname() is None)
1969
Tim Peters855fe882002-12-22 03:43:39 +00001970 class C3(tzinfo):
1971 def utcoffset(self, dt): return timedelta(minutes=-1439)
1972 def dst(self, dt): return timedelta(minutes=1439)
1973 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001974 t = cls(1, 1, 1, tzinfo=C3())
1975 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
1976 self.assertEqual(t.dst(), timedelta(minutes=1439))
1977 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00001978
1979 # Wrong types.
1980 class C4(tzinfo):
1981 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001982 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00001983 def tzname(self, dt): return 0
1984 t = cls(1, 1, 1, tzinfo=C4())
1985 self.assertRaises(TypeError, t.utcoffset)
1986 self.assertRaises(TypeError, t.dst)
1987 self.assertRaises(TypeError, t.tzname)
1988
1989 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00001990 class C6(tzinfo):
1991 def utcoffset(self, dt): return timedelta(hours=-24)
1992 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00001993 t = cls(1, 1, 1, tzinfo=C6())
1994 self.assertRaises(ValueError, t.utcoffset)
1995 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00001996
1997 # Not a whole number of minutes.
1998 class C7(tzinfo):
1999 def utcoffset(self, dt): return timedelta(seconds=61)
2000 def dst(self, dt): return timedelta(microseconds=-81)
2001 t = cls(1, 1, 1, tzinfo=C7())
2002 self.assertRaises(ValueError, t.utcoffset)
2003 self.assertRaises(ValueError, t.dst)
2004
Tim Peters4c0db782002-12-26 05:01:19 +00002005 def test_aware_compare(self):
2006 cls = self.theclass
2007
Tim Peters60c76e42002-12-27 00:41:11 +00002008 # Ensure that utcoffset() gets ignored if the comparands have
2009 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002010 class OperandDependentOffset(tzinfo):
2011 def utcoffset(self, t):
2012 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002013 # d0 and d1 equal after adjustment
2014 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002015 else:
Tim Peters397301e2003-01-02 21:28:08 +00002016 # d2 off in the weeds
2017 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002018
2019 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2020 d0 = base.replace(minute=3)
2021 d1 = base.replace(minute=9)
2022 d2 = base.replace(minute=11)
2023 for x in d0, d1, d2:
2024 for y in d0, d1, d2:
2025 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002026 expected = cmp(x.minute, y.minute)
2027 self.assertEqual(got, expected)
2028
2029 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002030 # Note that a time can't actually have an operand-depedent offset,
2031 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2032 # so skip this test for time.
2033 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002034 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2035 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2036 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2037 for x in d0, d1, d2:
2038 for y in d0, d1, d2:
2039 got = cmp(x, y)
2040 if (x is d0 or x is d1) and (y is d0 or y is d1):
2041 expected = 0
2042 elif x is y is d2:
2043 expected = 0
2044 elif x is d2:
2045 expected = -1
2046 else:
2047 assert y is d2
2048 expected = 1
2049 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002050
Tim Peters855fe882002-12-22 03:43:39 +00002051
Tim Peters0bf60bd2003-01-08 20:40:01 +00002052# Testing time objects with a non-None tzinfo.
Tim Peters855fe882002-12-22 03:43:39 +00002053class TestTimeTZ(TestTime, TZInfoBase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002054 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002055
2056 def test_empty(self):
2057 t = self.theclass()
2058 self.assertEqual(t.hour, 0)
2059 self.assertEqual(t.minute, 0)
2060 self.assertEqual(t.second, 0)
2061 self.assertEqual(t.microsecond, 0)
2062 self.failUnless(t.tzinfo is None)
2063
Tim Peters2a799bf2002-12-16 20:18:38 +00002064 def test_zones(self):
2065 est = FixedOffset(-300, "EST", 1)
2066 utc = FixedOffset(0, "UTC", -2)
2067 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002068 t1 = time( 7, 47, tzinfo=est)
2069 t2 = time(12, 47, tzinfo=utc)
2070 t3 = time(13, 47, tzinfo=met)
2071 t4 = time(microsecond=40)
2072 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002073
2074 self.assertEqual(t1.tzinfo, est)
2075 self.assertEqual(t2.tzinfo, utc)
2076 self.assertEqual(t3.tzinfo, met)
2077 self.failUnless(t4.tzinfo is None)
2078 self.assertEqual(t5.tzinfo, utc)
2079
Tim Peters855fe882002-12-22 03:43:39 +00002080 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2081 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2082 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002083 self.failUnless(t4.utcoffset() is None)
2084 self.assertRaises(TypeError, t1.utcoffset, "no args")
2085
2086 self.assertEqual(t1.tzname(), "EST")
2087 self.assertEqual(t2.tzname(), "UTC")
2088 self.assertEqual(t3.tzname(), "MET")
2089 self.failUnless(t4.tzname() is None)
2090 self.assertRaises(TypeError, t1.tzname, "no args")
2091
Tim Peters855fe882002-12-22 03:43:39 +00002092 self.assertEqual(t1.dst(), timedelta(minutes=1))
2093 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2094 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002095 self.failUnless(t4.dst() is None)
2096 self.assertRaises(TypeError, t1.dst, "no args")
2097
2098 self.assertEqual(hash(t1), hash(t2))
2099 self.assertEqual(hash(t1), hash(t3))
2100 self.assertEqual(hash(t2), hash(t3))
2101
2102 self.assertEqual(t1, t2)
2103 self.assertEqual(t1, t3)
2104 self.assertEqual(t2, t3)
2105 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2106 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2107 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2108
2109 self.assertEqual(str(t1), "07:47:00-05:00")
2110 self.assertEqual(str(t2), "12:47:00+00:00")
2111 self.assertEqual(str(t3), "13:47:00+01:00")
2112 self.assertEqual(str(t4), "00:00:00.000040")
2113 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2114
2115 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2116 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2117 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2118 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2119 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2120
Tim Peters0bf60bd2003-01-08 20:40:01 +00002121 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002122 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2123 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2124 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2125 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2126 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2127
2128 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2129 "07:47:00 %Z=EST %z=-0500")
2130 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2131 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2132
2133 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002134 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002135 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2136 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2137
Tim Petersb92bb712002-12-21 17:44:07 +00002138 # Check that an invalid tzname result raises an exception.
2139 class Badtzname(tzinfo):
2140 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002141 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002142 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2143 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002144
2145 def test_hash_edge_cases(self):
2146 # Offsets that overflow a basic time.
2147 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2148 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2149 self.assertEqual(hash(t1), hash(t2))
2150
2151 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2152 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2153 self.assertEqual(hash(t1), hash(t2))
2154
Tim Peters2a799bf2002-12-16 20:18:38 +00002155 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002156 # Try one without a tzinfo.
2157 args = 20, 59, 16, 64**2
2158 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002159 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002160 green = pickler.dumps(orig, proto)
2161 derived = unpickler.loads(green)
2162 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002163
2164 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002165 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002166 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002167 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002168 green = pickler.dumps(orig, proto)
2169 derived = unpickler.loads(green)
2170 self.assertEqual(orig, derived)
2171 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2172 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2173 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002174
2175 def test_more_bool(self):
2176 # Test cases with non-None tzinfo.
2177 cls = self.theclass
2178
2179 t = cls(0, tzinfo=FixedOffset(-300, ""))
2180 self.failUnless(t)
2181
2182 t = cls(5, tzinfo=FixedOffset(-300, ""))
2183 self.failUnless(t)
2184
2185 t = cls(5, tzinfo=FixedOffset(300, ""))
2186 self.failUnless(not t)
2187
2188 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2189 self.failUnless(not t)
2190
2191 # Mostly ensuring this doesn't overflow internally.
2192 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2193 self.failUnless(t)
2194
2195 # But this should yield a value error -- the utcoffset is bogus.
2196 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2197 self.assertRaises(ValueError, lambda: bool(t))
2198
2199 # Likewise.
2200 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2201 self.assertRaises(ValueError, lambda: bool(t))
2202
Tim Peters12bf3392002-12-24 05:41:27 +00002203 def test_replace(self):
2204 cls = self.theclass
2205 z100 = FixedOffset(100, "+100")
2206 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2207 args = [1, 2, 3, 4, z100]
2208 base = cls(*args)
2209 self.assertEqual(base, base.replace())
2210
2211 i = 0
2212 for name, newval in (("hour", 5),
2213 ("minute", 6),
2214 ("second", 7),
2215 ("microsecond", 8),
2216 ("tzinfo", zm200)):
2217 newargs = args[:]
2218 newargs[i] = newval
2219 expected = cls(*newargs)
2220 got = base.replace(**{name: newval})
2221 self.assertEqual(expected, got)
2222 i += 1
2223
2224 # Ensure we can get rid of a tzinfo.
2225 self.assertEqual(base.tzname(), "+100")
2226 base2 = base.replace(tzinfo=None)
2227 self.failUnless(base2.tzinfo is None)
2228 self.failUnless(base2.tzname() is None)
2229
2230 # Ensure we can add one.
2231 base3 = base2.replace(tzinfo=z100)
2232 self.assertEqual(base, base3)
2233 self.failUnless(base.tzinfo is base3.tzinfo)
2234
2235 # Out of bounds.
2236 base = cls(1)
2237 self.assertRaises(ValueError, base.replace, hour=24)
2238 self.assertRaises(ValueError, base.replace, minute=-1)
2239 self.assertRaises(ValueError, base.replace, second=100)
2240 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2241
Tim Peters60c76e42002-12-27 00:41:11 +00002242 def test_mixed_compare(self):
2243 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002244 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002245 self.assertEqual(t1, t2)
2246 t2 = t2.replace(tzinfo=None)
2247 self.assertEqual(t1, t2)
2248 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2249 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002250 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2251 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002252
Tim Peters0bf60bd2003-01-08 20:40:01 +00002253 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002254 class Varies(tzinfo):
2255 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002256 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002257 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002258 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002259 return self.offset
2260
2261 v = Varies()
2262 t1 = t2.replace(tzinfo=v)
2263 t2 = t2.replace(tzinfo=v)
2264 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2265 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2266 self.assertEqual(t1, t2)
2267
2268 # But if they're not identical, it isn't ignored.
2269 t2 = t2.replace(tzinfo=Varies())
2270 self.failUnless(t1 < t2) # t1's offset counter still going up
2271
Tim Petersa98924a2003-05-17 05:55:19 +00002272 def test_subclass_timetz(self):
2273
2274 class C(self.theclass):
2275 theAnswer = 42
2276
2277 def __new__(cls, *args, **kws):
2278 temp = kws.copy()
2279 extra = temp.pop('extra')
2280 result = self.theclass.__new__(cls, *args, **temp)
2281 result.extra = extra
2282 return result
2283
2284 def newmeth(self, start):
2285 return start + self.hour + self.second
2286
2287 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2288
2289 dt1 = self.theclass(*args)
2290 dt2 = C(*args, **{'extra': 7})
2291
2292 self.assertEqual(dt2.__class__, C)
2293 self.assertEqual(dt2.theAnswer, 42)
2294 self.assertEqual(dt2.extra, 7)
2295 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2296 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2297
Tim Peters4c0db782002-12-26 05:01:19 +00002298
Tim Peters0bf60bd2003-01-08 20:40:01 +00002299# Testing datetime objects with a non-None tzinfo.
2300
Tim Peters855fe882002-12-22 03:43:39 +00002301class TestDateTimeTZ(TestDateTime, TZInfoBase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002302 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002303
2304 def test_trivial(self):
2305 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2306 self.assertEqual(dt.year, 1)
2307 self.assertEqual(dt.month, 2)
2308 self.assertEqual(dt.day, 3)
2309 self.assertEqual(dt.hour, 4)
2310 self.assertEqual(dt.minute, 5)
2311 self.assertEqual(dt.second, 6)
2312 self.assertEqual(dt.microsecond, 7)
2313 self.assertEqual(dt.tzinfo, None)
2314
2315 def test_even_more_compare(self):
2316 # The test_compare() and test_more_compare() inherited from TestDate
2317 # and TestDateTime covered non-tzinfo cases.
2318
2319 # Smallest possible after UTC adjustment.
2320 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2321 # Largest possible after UTC adjustment.
2322 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2323 tzinfo=FixedOffset(-1439, ""))
2324
2325 # Make sure those compare correctly, and w/o overflow.
2326 self.failUnless(t1 < t2)
2327 self.failUnless(t1 != t2)
2328 self.failUnless(t2 > t1)
2329
2330 self.failUnless(t1 == t1)
2331 self.failUnless(t2 == t2)
2332
2333 # Equal afer adjustment.
2334 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2335 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2336 self.assertEqual(t1, t2)
2337
2338 # Change t1 not to subtract a minute, and t1 should be larger.
2339 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2340 self.failUnless(t1 > t2)
2341
2342 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2343 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2344 self.failUnless(t1 < t2)
2345
2346 # Back to the original t1, but make seconds resolve it.
2347 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2348 second=1)
2349 self.failUnless(t1 > t2)
2350
2351 # Likewise, but make microseconds resolve it.
2352 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2353 microsecond=1)
2354 self.failUnless(t1 > t2)
2355
2356 # Make t2 naive and it should fail.
2357 t2 = self.theclass.min
2358 self.assertRaises(TypeError, lambda: t1 == t2)
2359 self.assertEqual(t2, t2)
2360
2361 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2362 class Naive(tzinfo):
2363 def utcoffset(self, dt): return None
2364 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2365 self.assertRaises(TypeError, lambda: t1 == t2)
2366 self.assertEqual(t2, t2)
2367
2368 # OTOH, it's OK to compare two of these mixing the two ways of being
2369 # naive.
2370 t1 = self.theclass(5, 6, 7)
2371 self.assertEqual(t1, t2)
2372
2373 # Try a bogus uctoffset.
2374 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002375 def utcoffset(self, dt):
2376 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002377 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2378 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002379 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002380
Tim Peters2a799bf2002-12-16 20:18:38 +00002381 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002382 # Try one without a tzinfo.
2383 args = 6, 7, 23, 20, 59, 1, 64**2
2384 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002385 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002386 green = pickler.dumps(orig, proto)
2387 derived = unpickler.loads(green)
2388 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002389
2390 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002391 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002392 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002393 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002394 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002395 green = pickler.dumps(orig, proto)
2396 derived = unpickler.loads(green)
2397 self.assertEqual(orig, derived)
2398 self.failUnless(isinstance(derived.tzinfo,
2399 PicklableFixedOffset))
2400 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2401 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002402
2403 def test_extreme_hashes(self):
2404 # If an attempt is made to hash these via subtracting the offset
2405 # then hashing a datetime object, OverflowError results. The
2406 # Python implementation used to blow up here.
2407 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2408 hash(t)
2409 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2410 tzinfo=FixedOffset(-1439, ""))
2411 hash(t)
2412
2413 # OTOH, an OOB offset should blow up.
2414 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2415 self.assertRaises(ValueError, hash, t)
2416
2417 def test_zones(self):
2418 est = FixedOffset(-300, "EST")
2419 utc = FixedOffset(0, "UTC")
2420 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002421 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2422 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2423 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002424 self.assertEqual(t1.tzinfo, est)
2425 self.assertEqual(t2.tzinfo, utc)
2426 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002427 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2428 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2429 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002430 self.assertEqual(t1.tzname(), "EST")
2431 self.assertEqual(t2.tzname(), "UTC")
2432 self.assertEqual(t3.tzname(), "MET")
2433 self.assertEqual(hash(t1), hash(t2))
2434 self.assertEqual(hash(t1), hash(t3))
2435 self.assertEqual(hash(t2), hash(t3))
2436 self.assertEqual(t1, t2)
2437 self.assertEqual(t1, t3)
2438 self.assertEqual(t2, t3)
2439 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2440 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2441 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002442 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002443 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2444 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2445 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2446
2447 def test_combine(self):
2448 met = FixedOffset(60, "MET")
2449 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002450 tz = time(18, 45, 3, 1234, tzinfo=met)
2451 dt = datetime.combine(d, tz)
2452 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002453 tzinfo=met))
2454
2455 def test_extract(self):
2456 met = FixedOffset(60, "MET")
2457 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2458 self.assertEqual(dt.date(), date(2002, 3, 4))
2459 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002460 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002461
2462 def test_tz_aware_arithmetic(self):
2463 import random
2464
2465 now = self.theclass.now()
2466 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002467 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002468 nowaware = self.theclass.combine(now.date(), timeaware)
2469 self.failUnless(nowaware.tzinfo is tz55)
2470 self.assertEqual(nowaware.timetz(), timeaware)
2471
2472 # Can't mix aware and non-aware.
2473 self.assertRaises(TypeError, lambda: now - nowaware)
2474 self.assertRaises(TypeError, lambda: nowaware - now)
2475
Tim Peters0bf60bd2003-01-08 20:40:01 +00002476 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002477 self.assertRaises(TypeError, lambda: now + nowaware)
2478 self.assertRaises(TypeError, lambda: nowaware + now)
2479 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2480
2481 # Subtracting should yield 0.
2482 self.assertEqual(now - now, timedelta(0))
2483 self.assertEqual(nowaware - nowaware, timedelta(0))
2484
2485 # Adding a delta should preserve tzinfo.
2486 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2487 nowawareplus = nowaware + delta
2488 self.failUnless(nowaware.tzinfo is tz55)
2489 nowawareplus2 = delta + nowaware
2490 self.failUnless(nowawareplus2.tzinfo is tz55)
2491 self.assertEqual(nowawareplus, nowawareplus2)
2492
2493 # that - delta should be what we started with, and that - what we
2494 # started with should be delta.
2495 diff = nowawareplus - delta
2496 self.failUnless(diff.tzinfo is tz55)
2497 self.assertEqual(nowaware, diff)
2498 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2499 self.assertEqual(nowawareplus - nowaware, delta)
2500
2501 # Make up a random timezone.
2502 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002503 # Attach it to nowawareplus.
2504 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002505 self.failUnless(nowawareplus.tzinfo is tzr)
2506 # Make sure the difference takes the timezone adjustments into account.
2507 got = nowaware - nowawareplus
2508 # Expected: (nowaware base - nowaware offset) -
2509 # (nowawareplus base - nowawareplus offset) =
2510 # (nowaware base - nowawareplus base) +
2511 # (nowawareplus offset - nowaware offset) =
2512 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002513 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002514 self.assertEqual(got, expected)
2515
2516 # Try max possible difference.
2517 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2518 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2519 tzinfo=FixedOffset(-1439, "max"))
2520 maxdiff = max - min
2521 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2522 timedelta(minutes=2*1439))
2523
2524 def test_tzinfo_now(self):
2525 meth = self.theclass.now
2526 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2527 base = meth()
2528 # Try with and without naming the keyword.
2529 off42 = FixedOffset(42, "42")
2530 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002531 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002532 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002533 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002534 # Bad argument with and w/o naming the keyword.
2535 self.assertRaises(TypeError, meth, 16)
2536 self.assertRaises(TypeError, meth, tzinfo=16)
2537 # Bad keyword name.
2538 self.assertRaises(TypeError, meth, tinfo=off42)
2539 # Too many args.
2540 self.assertRaises(TypeError, meth, off42, off42)
2541
Tim Peters10cadce2003-01-23 19:58:02 +00002542 # We don't know which time zone we're in, and don't have a tzinfo
2543 # class to represent it, so seeing whether a tz argument actually
2544 # does a conversion is tricky.
2545 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2546 utc = FixedOffset(0, "utc", 0)
2547 for dummy in range(3):
2548 now = datetime.now(weirdtz)
2549 self.failUnless(now.tzinfo is weirdtz)
2550 utcnow = datetime.utcnow().replace(tzinfo=utc)
2551 now2 = utcnow.astimezone(weirdtz)
2552 if abs(now - now2) < timedelta(seconds=30):
2553 break
2554 # Else the code is broken, or more than 30 seconds passed between
2555 # calls; assuming the latter, just try again.
2556 else:
2557 # Three strikes and we're out.
2558 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2559
Tim Peters2a799bf2002-12-16 20:18:38 +00002560 def test_tzinfo_fromtimestamp(self):
2561 import time
2562 meth = self.theclass.fromtimestamp
2563 ts = time.time()
2564 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2565 base = meth(ts)
2566 # Try with and without naming the keyword.
2567 off42 = FixedOffset(42, "42")
2568 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002569 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002570 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002571 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002572 # Bad argument with and w/o naming the keyword.
2573 self.assertRaises(TypeError, meth, ts, 16)
2574 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2575 # Bad keyword name.
2576 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2577 # Too many args.
2578 self.assertRaises(TypeError, meth, ts, off42, off42)
2579 # Too few args.
2580 self.assertRaises(TypeError, meth)
2581
Tim Peters2a44a8d2003-01-23 20:53:10 +00002582 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002583 timestamp = 1000000000
2584 utcdatetime = datetime.utcfromtimestamp(timestamp)
2585 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2586 # But on some flavor of Mac, it's nowhere near that. So we can't have
2587 # any idea here what time that actually is, we can only test that
2588 # relative changes match.
2589 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2590 tz = FixedOffset(utcoffset, "tz", 0)
2591 expected = utcdatetime + utcoffset
2592 got = datetime.fromtimestamp(timestamp, tz)
2593 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002594
Tim Peters2a799bf2002-12-16 20:18:38 +00002595 def test_tzinfo_utcnow(self):
2596 meth = self.theclass.utcnow
2597 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2598 base = meth()
2599 # Try with and without naming the keyword; for whatever reason,
2600 # utcnow() doesn't accept a tzinfo argument.
2601 off42 = FixedOffset(42, "42")
2602 self.assertRaises(TypeError, meth, off42)
2603 self.assertRaises(TypeError, meth, tzinfo=off42)
2604
2605 def test_tzinfo_utcfromtimestamp(self):
2606 import time
2607 meth = self.theclass.utcfromtimestamp
2608 ts = time.time()
2609 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2610 base = meth(ts)
2611 # Try with and without naming the keyword; for whatever reason,
2612 # utcfromtimestamp() doesn't accept a tzinfo argument.
2613 off42 = FixedOffset(42, "42")
2614 self.assertRaises(TypeError, meth, ts, off42)
2615 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2616
2617 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002618 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002619 # DST flag.
2620 class DST(tzinfo):
2621 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002622 if isinstance(dstvalue, int):
2623 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002624 self.dstvalue = dstvalue
2625 def dst(self, dt):
2626 return self.dstvalue
2627
2628 cls = self.theclass
2629 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2630 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2631 t = d.timetuple()
2632 self.assertEqual(1, t.tm_year)
2633 self.assertEqual(1, t.tm_mon)
2634 self.assertEqual(1, t.tm_mday)
2635 self.assertEqual(10, t.tm_hour)
2636 self.assertEqual(20, t.tm_min)
2637 self.assertEqual(30, t.tm_sec)
2638 self.assertEqual(0, t.tm_wday)
2639 self.assertEqual(1, t.tm_yday)
2640 self.assertEqual(flag, t.tm_isdst)
2641
2642 # dst() returns wrong type.
2643 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2644
2645 # dst() at the edge.
2646 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2647 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2648
2649 # dst() out of range.
2650 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2651 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2652
2653 def test_utctimetuple(self):
2654 class DST(tzinfo):
2655 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002656 if isinstance(dstvalue, int):
2657 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002658 self.dstvalue = dstvalue
2659 def dst(self, dt):
2660 return self.dstvalue
2661
2662 cls = self.theclass
2663 # This can't work: DST didn't implement utcoffset.
2664 self.assertRaises(NotImplementedError,
2665 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2666
2667 class UOFS(DST):
2668 def __init__(self, uofs, dofs=None):
2669 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002670 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002671 def utcoffset(self, dt):
2672 return self.uofs
2673
2674 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2675 # in effect for a UTC time.
2676 for dstvalue in -33, 33, 0, None:
2677 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2678 t = d.utctimetuple()
2679 self.assertEqual(d.year, t.tm_year)
2680 self.assertEqual(d.month, t.tm_mon)
2681 self.assertEqual(d.day, t.tm_mday)
2682 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2683 self.assertEqual(13, t.tm_min)
2684 self.assertEqual(d.second, t.tm_sec)
2685 self.assertEqual(d.weekday(), t.tm_wday)
2686 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2687 t.tm_yday)
2688 self.assertEqual(0, t.tm_isdst)
2689
2690 # At the edges, UTC adjustment can normalize into years out-of-range
2691 # for a datetime object. Ensure that a correct timetuple is
2692 # created anyway.
2693 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2694 # That goes back 1 minute less than a full day.
2695 t = tiny.utctimetuple()
2696 self.assertEqual(t.tm_year, MINYEAR-1)
2697 self.assertEqual(t.tm_mon, 12)
2698 self.assertEqual(t.tm_mday, 31)
2699 self.assertEqual(t.tm_hour, 0)
2700 self.assertEqual(t.tm_min, 1)
2701 self.assertEqual(t.tm_sec, 37)
2702 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2703 self.assertEqual(t.tm_isdst, 0)
2704
2705 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2706 # That goes forward 1 minute less than a full day.
2707 t = huge.utctimetuple()
2708 self.assertEqual(t.tm_year, MAXYEAR+1)
2709 self.assertEqual(t.tm_mon, 1)
2710 self.assertEqual(t.tm_mday, 1)
2711 self.assertEqual(t.tm_hour, 23)
2712 self.assertEqual(t.tm_min, 58)
2713 self.assertEqual(t.tm_sec, 37)
2714 self.assertEqual(t.tm_yday, 1)
2715 self.assertEqual(t.tm_isdst, 0)
2716
2717 def test_tzinfo_isoformat(self):
2718 zero = FixedOffset(0, "+00:00")
2719 plus = FixedOffset(220, "+03:40")
2720 minus = FixedOffset(-231, "-03:51")
2721 unknown = FixedOffset(None, "")
2722
2723 cls = self.theclass
2724 datestr = '0001-02-03'
2725 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002726 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002727 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2728 timestr = '04:05:59' + (us and '.987001' or '')
2729 ofsstr = ofs is not None and d.tzname() or ''
2730 tailstr = timestr + ofsstr
2731 iso = d.isoformat()
2732 self.assertEqual(iso, datestr + 'T' + tailstr)
2733 self.assertEqual(iso, d.isoformat('T'))
2734 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2735 self.assertEqual(str(d), datestr + ' ' + tailstr)
2736
Tim Peters12bf3392002-12-24 05:41:27 +00002737 def test_replace(self):
2738 cls = self.theclass
2739 z100 = FixedOffset(100, "+100")
2740 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2741 args = [1, 2, 3, 4, 5, 6, 7, z100]
2742 base = cls(*args)
2743 self.assertEqual(base, base.replace())
2744
2745 i = 0
2746 for name, newval in (("year", 2),
2747 ("month", 3),
2748 ("day", 4),
2749 ("hour", 5),
2750 ("minute", 6),
2751 ("second", 7),
2752 ("microsecond", 8),
2753 ("tzinfo", zm200)):
2754 newargs = args[:]
2755 newargs[i] = newval
2756 expected = cls(*newargs)
2757 got = base.replace(**{name: newval})
2758 self.assertEqual(expected, got)
2759 i += 1
2760
2761 # Ensure we can get rid of a tzinfo.
2762 self.assertEqual(base.tzname(), "+100")
2763 base2 = base.replace(tzinfo=None)
2764 self.failUnless(base2.tzinfo is None)
2765 self.failUnless(base2.tzname() is None)
2766
2767 # Ensure we can add one.
2768 base3 = base2.replace(tzinfo=z100)
2769 self.assertEqual(base, base3)
2770 self.failUnless(base.tzinfo is base3.tzinfo)
2771
2772 # Out of bounds.
2773 base = cls(2000, 2, 29)
2774 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002775
Tim Peters80475bb2002-12-25 07:40:55 +00002776 def test_more_astimezone(self):
2777 # The inherited test_astimezone covered some trivial and error cases.
2778 fnone = FixedOffset(None, "None")
2779 f44m = FixedOffset(44, "44")
2780 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2781
Tim Peters10cadce2003-01-23 19:58:02 +00002782 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002783 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002784 # Replacing with degenerate tzinfo raises an exception.
2785 self.assertRaises(ValueError, dt.astimezone, fnone)
2786 # Ditto with None tz.
2787 self.assertRaises(TypeError, dt.astimezone, None)
2788 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002789 x = dt.astimezone(dt.tzinfo)
2790 self.failUnless(x.tzinfo is f44m)
2791 self.assertEqual(x.date(), dt.date())
2792 self.assertEqual(x.time(), dt.time())
2793
2794 # Replacing with different tzinfo does adjust.
2795 got = dt.astimezone(fm5h)
2796 self.failUnless(got.tzinfo is fm5h)
2797 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2798 expected = dt - dt.utcoffset() # in effect, convert to UTC
2799 expected += fm5h.utcoffset(dt) # and from there to local time
2800 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2801 self.assertEqual(got.date(), expected.date())
2802 self.assertEqual(got.time(), expected.time())
2803 self.assertEqual(got.timetz(), expected.timetz())
2804 self.failUnless(got.tzinfo is expected.tzinfo)
2805 self.assertEqual(got, expected)
2806
Tim Peters4c0db782002-12-26 05:01:19 +00002807 def test_aware_subtract(self):
2808 cls = self.theclass
2809
Tim Peters60c76e42002-12-27 00:41:11 +00002810 # Ensure that utcoffset() is ignored when the operands have the
2811 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002812 class OperandDependentOffset(tzinfo):
2813 def utcoffset(self, t):
2814 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002815 # d0 and d1 equal after adjustment
2816 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002817 else:
Tim Peters397301e2003-01-02 21:28:08 +00002818 # d2 off in the weeds
2819 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002820
2821 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2822 d0 = base.replace(minute=3)
2823 d1 = base.replace(minute=9)
2824 d2 = base.replace(minute=11)
2825 for x in d0, d1, d2:
2826 for y in d0, d1, d2:
2827 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002828 expected = timedelta(minutes=x.minute - y.minute)
2829 self.assertEqual(got, expected)
2830
2831 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2832 # ignored.
2833 base = cls(8, 9, 10, 11, 12, 13, 14)
2834 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2835 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2836 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2837 for x in d0, d1, d2:
2838 for y in d0, d1, d2:
2839 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002840 if (x is d0 or x is d1) and (y is d0 or y is d1):
2841 expected = timedelta(0)
2842 elif x is y is d2:
2843 expected = timedelta(0)
2844 elif x is d2:
2845 expected = timedelta(minutes=(11-59)-0)
2846 else:
2847 assert y is d2
2848 expected = timedelta(minutes=0-(11-59))
2849 self.assertEqual(got, expected)
2850
Tim Peters60c76e42002-12-27 00:41:11 +00002851 def test_mixed_compare(self):
2852 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002853 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002854 self.assertEqual(t1, t2)
2855 t2 = t2.replace(tzinfo=None)
2856 self.assertEqual(t1, t2)
2857 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2858 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002859 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2860 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002861
Tim Peters0bf60bd2003-01-08 20:40:01 +00002862 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002863 class Varies(tzinfo):
2864 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002865 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002866 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002867 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002868 return self.offset
2869
2870 v = Varies()
2871 t1 = t2.replace(tzinfo=v)
2872 t2 = t2.replace(tzinfo=v)
2873 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2874 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2875 self.assertEqual(t1, t2)
2876
2877 # But if they're not identical, it isn't ignored.
2878 t2 = t2.replace(tzinfo=Varies())
2879 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002880
Tim Petersa98924a2003-05-17 05:55:19 +00002881 def test_subclass_datetimetz(self):
2882
2883 class C(self.theclass):
2884 theAnswer = 42
2885
2886 def __new__(cls, *args, **kws):
2887 temp = kws.copy()
2888 extra = temp.pop('extra')
2889 result = self.theclass.__new__(cls, *args, **temp)
2890 result.extra = extra
2891 return result
2892
2893 def newmeth(self, start):
2894 return start + self.hour + self.year
2895
2896 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2897
2898 dt1 = self.theclass(*args)
2899 dt2 = C(*args, **{'extra': 7})
2900
2901 self.assertEqual(dt2.__class__, C)
2902 self.assertEqual(dt2.theAnswer, 42)
2903 self.assertEqual(dt2.extra, 7)
2904 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2905 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2906
Tim Peters621818b2002-12-29 23:44:49 +00002907# Pain to set up DST-aware tzinfo classes.
2908
2909def first_sunday_on_or_after(dt):
2910 days_to_go = 6 - dt.weekday()
2911 if days_to_go:
2912 dt += timedelta(days_to_go)
2913 return dt
2914
2915ZERO = timedelta(0)
2916HOUR = timedelta(hours=1)
2917DAY = timedelta(days=1)
2918# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2919DSTSTART = datetime(1, 4, 1, 2)
2920# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002921# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2922# being standard time on that day, there is no spelling in local time of
2923# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2924DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002925
2926class USTimeZone(tzinfo):
2927
2928 def __init__(self, hours, reprname, stdname, dstname):
2929 self.stdoffset = timedelta(hours=hours)
2930 self.reprname = reprname
2931 self.stdname = stdname
2932 self.dstname = dstname
2933
2934 def __repr__(self):
2935 return self.reprname
2936
2937 def tzname(self, dt):
2938 if self.dst(dt):
2939 return self.dstname
2940 else:
2941 return self.stdname
2942
2943 def utcoffset(self, dt):
2944 return self.stdoffset + self.dst(dt)
2945
2946 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00002947 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00002948 # An exception instead may be sensible here, in one or more of
2949 # the cases.
2950 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00002951 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00002952
2953 # Find first Sunday in April.
2954 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
2955 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
2956
2957 # Find last Sunday in October.
2958 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
2959 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
2960
Tim Peters621818b2002-12-29 23:44:49 +00002961 # Can't compare naive to aware objects, so strip the timezone from
2962 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00002963 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00002964 return HOUR
2965 else:
2966 return ZERO
2967
Tim Peters521fc152002-12-31 17:36:56 +00002968Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
2969Central = USTimeZone(-6, "Central", "CST", "CDT")
2970Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
2971Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00002972utc_real = FixedOffset(0, "UTC", 0)
2973# For better test coverage, we want another flavor of UTC that's west of
2974# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00002975utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00002976
2977class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00002978 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002979 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00002980 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002981
Tim Peters0bf60bd2003-01-08 20:40:01 +00002982 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00002983
Tim Peters521fc152002-12-31 17:36:56 +00002984 # Check a time that's inside DST.
2985 def checkinside(self, dt, tz, utc, dston, dstoff):
2986 self.assertEqual(dt.dst(), HOUR)
2987
2988 # Conversion to our own timezone is always an identity.
2989 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00002990
2991 asutc = dt.astimezone(utc)
2992 there_and_back = asutc.astimezone(tz)
2993
2994 # Conversion to UTC and back isn't always an identity here,
2995 # because there are redundant spellings (in local time) of
2996 # UTC time when DST begins: the clock jumps from 1:59:59
2997 # to 3:00:00, and a local time of 2:MM:SS doesn't really
2998 # make sense then. The classes above treat 2:MM:SS as
2999 # daylight time then (it's "after 2am"), really an alias
3000 # for 1:MM:SS standard time. The latter form is what
3001 # conversion back from UTC produces.
3002 if dt.date() == dston.date() and dt.hour == 2:
3003 # We're in the redundant hour, and coming back from
3004 # UTC gives the 1:MM:SS standard-time spelling.
3005 self.assertEqual(there_and_back + HOUR, dt)
3006 # Although during was considered to be in daylight
3007 # time, there_and_back is not.
3008 self.assertEqual(there_and_back.dst(), ZERO)
3009 # They're the same times in UTC.
3010 self.assertEqual(there_and_back.astimezone(utc),
3011 dt.astimezone(utc))
3012 else:
3013 # We're not in the redundant hour.
3014 self.assertEqual(dt, there_and_back)
3015
Tim Peters327098a2003-01-20 22:54:38 +00003016 # Because we have a redundant spelling when DST begins, there is
3017 # (unforunately) an hour when DST ends that can't be spelled at all in
3018 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3019 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3020 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3021 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3022 # expressed in local time. Nevertheless, we want conversion back
3023 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003024 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003025 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003026 if dt.date() == dstoff.date() and dt.hour == 0:
3027 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003028 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003029 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3030 nexthour_utc += HOUR
3031 nexthour_tz = nexthour_utc.astimezone(tz)
3032 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003033 else:
Tim Peters327098a2003-01-20 22:54:38 +00003034 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003035
3036 # Check a time that's outside DST.
3037 def checkoutside(self, dt, tz, utc):
3038 self.assertEqual(dt.dst(), ZERO)
3039
3040 # Conversion to our own timezone is always an identity.
3041 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003042
3043 # Converting to UTC and back is an identity too.
3044 asutc = dt.astimezone(utc)
3045 there_and_back = asutc.astimezone(tz)
3046 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003047
Tim Peters1024bf82002-12-30 17:09:40 +00003048 def convert_between_tz_and_utc(self, tz, utc):
3049 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003050 # Because 1:MM on the day DST ends is taken as being standard time,
3051 # there is no spelling in tz for the last hour of daylight time.
3052 # For purposes of the test, the last hour of DST is 0:MM, which is
3053 # taken as being daylight time (and 1:MM is taken as being standard
3054 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003055 dstoff = self.dstoff.replace(tzinfo=tz)
3056 for delta in (timedelta(weeks=13),
3057 DAY,
3058 HOUR,
3059 timedelta(minutes=1),
3060 timedelta(microseconds=1)):
3061
Tim Peters521fc152002-12-31 17:36:56 +00003062 self.checkinside(dston, tz, utc, dston, dstoff)
3063 for during in dston + delta, dstoff - delta:
3064 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003065
Tim Peters521fc152002-12-31 17:36:56 +00003066 self.checkoutside(dstoff, tz, utc)
3067 for outside in dston - delta, dstoff + delta:
3068 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003069
Tim Peters621818b2002-12-29 23:44:49 +00003070 def test_easy(self):
3071 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003072 self.convert_between_tz_and_utc(Eastern, utc_real)
3073 self.convert_between_tz_and_utc(Pacific, utc_real)
3074 self.convert_between_tz_and_utc(Eastern, utc_fake)
3075 self.convert_between_tz_and_utc(Pacific, utc_fake)
3076 # The next is really dancing near the edge. It works because
3077 # Pacific and Eastern are far enough apart that their "problem
3078 # hours" don't overlap.
3079 self.convert_between_tz_and_utc(Eastern, Pacific)
3080 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003081 # OTOH, these fail! Don't enable them. The difficulty is that
3082 # the edge case tests assume that every hour is representable in
3083 # the "utc" class. This is always true for a fixed-offset tzinfo
3084 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3085 # For these adjacent DST-aware time zones, the range of time offsets
3086 # tested ends up creating hours in the one that aren't representable
3087 # in the other. For the same reason, we would see failures in the
3088 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3089 # offset deltas in convert_between_tz_and_utc().
3090 #
3091 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3092 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003093
Tim Petersf3615152003-01-01 21:51:37 +00003094 def test_tricky(self):
3095 # 22:00 on day before daylight starts.
3096 fourback = self.dston - timedelta(hours=4)
3097 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003098 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003099 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3100 # 2", we should get the 3 spelling.
3101 # If we plug 22:00 the day before into Eastern, it "looks like std
3102 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3103 # to 22:00 lands on 2:00, which makes no sense in local time (the
3104 # local clock jumps from 1 to 3). The point here is to make sure we
3105 # get the 3 spelling.
3106 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003107 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003108 self.assertEqual(expected, got)
3109
3110 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3111 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003112 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003113 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3114 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3115 # spelling.
3116 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003117 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003118 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003119
Tim Petersadf64202003-01-04 06:03:15 +00003120 # Now on the day DST ends, we want "repeat an hour" behavior.
3121 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3122 # EST 23:MM 0:MM 1:MM 2:MM
3123 # EDT 0:MM 1:MM 2:MM 3:MM
3124 # wall 0:MM 1:MM 1:MM 2:MM against these
3125 for utc in utc_real, utc_fake:
3126 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003127 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003128 # Convert that to UTC.
3129 first_std_hour -= tz.utcoffset(None)
3130 # Adjust for possibly fake UTC.
3131 asutc = first_std_hour + utc.utcoffset(None)
3132 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3133 # tz=Eastern.
3134 asutcbase = asutc.replace(tzinfo=utc)
3135 for tzhour in (0, 1, 1, 2):
3136 expectedbase = self.dstoff.replace(hour=tzhour)
3137 for minute in 0, 30, 59:
3138 expected = expectedbase.replace(minute=minute)
3139 asutc = asutcbase.replace(minute=minute)
3140 astz = asutc.astimezone(tz)
3141 self.assertEqual(astz.replace(tzinfo=None), expected)
3142 asutcbase += HOUR
3143
3144
Tim Peters710fb152003-01-02 19:35:54 +00003145 def test_bogus_dst(self):
3146 class ok(tzinfo):
3147 def utcoffset(self, dt): return HOUR
3148 def dst(self, dt): return HOUR
3149
3150 now = self.theclass.now().replace(tzinfo=utc_real)
3151 # Doesn't blow up.
3152 now.astimezone(ok())
3153
3154 # Does blow up.
3155 class notok(ok):
3156 def dst(self, dt): return None
3157 self.assertRaises(ValueError, now.astimezone, notok())
3158
Tim Peters52dcce22003-01-23 16:36:11 +00003159 def test_fromutc(self):
3160 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3161 now = datetime.utcnow().replace(tzinfo=utc_real)
3162 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3163 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3164 enow = Eastern.fromutc(now) # doesn't blow up
3165 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3166 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3167 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3168
3169 # Always converts UTC to standard time.
3170 class FauxUSTimeZone(USTimeZone):
3171 def fromutc(self, dt):
3172 return dt + self.stdoffset
3173 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3174
3175 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3176 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3177 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3178
3179 # Check around DST start.
3180 start = self.dston.replace(hour=4, tzinfo=Eastern)
3181 fstart = start.replace(tzinfo=FEastern)
3182 for wall in 23, 0, 1, 3, 4, 5:
3183 expected = start.replace(hour=wall)
3184 if wall == 23:
3185 expected -= timedelta(days=1)
3186 got = Eastern.fromutc(start)
3187 self.assertEqual(expected, got)
3188
3189 expected = fstart + FEastern.stdoffset
3190 got = FEastern.fromutc(fstart)
3191 self.assertEqual(expected, got)
3192
3193 # Ensure astimezone() calls fromutc() too.
3194 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3195 self.assertEqual(expected, got)
3196
3197 start += HOUR
3198 fstart += HOUR
3199
3200 # Check around DST end.
3201 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3202 fstart = start.replace(tzinfo=FEastern)
3203 for wall in 0, 1, 1, 2, 3, 4:
3204 expected = start.replace(hour=wall)
3205 got = Eastern.fromutc(start)
3206 self.assertEqual(expected, got)
3207
3208 expected = fstart + FEastern.stdoffset
3209 got = FEastern.fromutc(fstart)
3210 self.assertEqual(expected, got)
3211
3212 # Ensure astimezone() calls fromutc() too.
3213 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3214 self.assertEqual(expected, got)
3215
3216 start += HOUR
3217 fstart += HOUR
3218
Tim Peters710fb152003-01-02 19:35:54 +00003219
Tim Peters528ca532004-09-16 01:30:50 +00003220#############################################################################
3221# oddballs
3222
3223class Oddballs(unittest.TestCase):
3224
3225 def test_bug_1028306(self):
3226 # Trying to compare a date to a datetime should act like a mixed-
3227 # type comparison, despite that datetime is a subclass of date.
3228 as_date = date.today()
3229 as_datetime = datetime.combine(as_date, time())
3230 self.assert_(as_date != as_datetime)
3231 self.assert_(as_datetime != as_date)
3232 self.assert_(not as_date == as_datetime)
3233 self.assert_(not as_datetime == as_date)
3234 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3235 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3236 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3237 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3238 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3239 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3240 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3241 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3242
3243 # Neverthelss, comparison should work with the base-class (date)
3244 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003245 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003246 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003247 as_different = as_datetime.replace(day= different_day)
3248 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003249
3250 # And date should compare with other subclasses of date. If a
3251 # subclass wants to stop this, it's up to the subclass to do so.
3252 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3253 self.assertEqual(as_date, date_sc)
3254 self.assertEqual(date_sc, as_date)
3255
3256 # Ditto for datetimes.
3257 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3258 as_date.day, 0, 0, 0)
3259 self.assertEqual(as_datetime, datetime_sc)
3260 self.assertEqual(datetime_sc, as_datetime)
3261
Tim Peterscfd4a8b2002-12-16 21:12:37 +00003262def test_suite():
Tim Peters2a799bf2002-12-16 20:18:38 +00003263 allsuites = [unittest.makeSuite(klass, 'test')
3264 for klass in (TestModule,
3265 TestTZInfo,
3266 TestTimeDelta,
3267 TestDateOnly,
3268 TestDate,
3269 TestDateTime,
3270 TestTime,
3271 TestTimeTZ,
3272 TestDateTimeTZ,
Tim Peters621818b2002-12-29 23:44:49 +00003273 TestTimezoneConversions,
Tim Peters528ca532004-09-16 01:30:50 +00003274 Oddballs,
Tim Peters2a799bf2002-12-16 20:18:38 +00003275 )
3276 ]
3277 return unittest.TestSuite(allsuites)
3278
3279def test_main():
3280 import gc
3281 import sys
3282
Tim Peterscfd4a8b2002-12-16 21:12:37 +00003283 thesuite = test_suite()
Tim Peters2a799bf2002-12-16 20:18:38 +00003284 lastrc = None
3285 while True:
3286 test_support.run_suite(thesuite)
3287 if 1: # change to 0, under a debug build, for some leak detection
3288 break
3289 gc.collect()
3290 if gc.garbage:
3291 raise SystemError("gc.garbage not empty after test run: %r" %
3292 gc.garbage)
3293 if hasattr(sys, 'gettotalrefcount'):
3294 thisrc = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +00003295 print('*' * 10, 'total refs:', thisrc, end=' ', file=sys.stderr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003296 if lastrc:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00003297 print('delta:', thisrc - lastrc, file=sys.stderr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003298 else:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00003299 print(file=sys.stderr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003300 lastrc = thisrc
3301
3302if __name__ == "__main__":
3303 test_main()