blob: cf7051bc0df8bca4f1990bdbeaf0f29a43ae2491 [file] [log] [blame]
Tim Peters0bf60bd2003-01-08 20:40:01 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
Tim Peters2a799bf2002-12-16 20:18:38 +00005
Guido van Rossumd8faa362007-04-27 19:54:29 +00006import os
Tim Peters2a799bf2002-12-16 20:18:38 +00007import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00008import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00009import unittest
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000010try:
11 import cPickle
12except ImportError:
13 cPickle = None
Tim Peters2a799bf2002-12-16 20:18:38 +000014
15from test import test_support
16
17from datetime import MINYEAR, MAXYEAR
18from datetime import timedelta
19from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000020from datetime import time
21from datetime import date, datetime
22
Tim Peters35ad6412003-02-05 04:08:07 +000023pickle_choices = [(pickler, unpickler, proto)
Nick Coghlan650f0d02007-04-15 12:05:43 +000024 for pickler in (pickle, cPickle)
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000025 if pickler is not None
Nick Coghlan650f0d02007-04-15 12:05:43 +000026 for unpickler in (pickle, cPickle)
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000027 if unpickler is not None
Tim Peters35ad6412003-02-05 04:08:07 +000028 for proto in range(3)]
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000029if cPickle is None:
30 assert len(pickle_choices) == 3
31else:
32 assert len(pickle_choices) == 2*2*3
Guido van Rossum177e41a2003-01-30 22:06:23 +000033
Tim Peters68124bb2003-02-08 03:46:31 +000034# An arbitrary collection of objects of non-datetime types, for testing
35# mixed-type comparisons.
Guido van Rossume2a383d2007-01-15 16:59:06 +000036OTHERSTUFF = (10, 10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000037
Tim Peters2a799bf2002-12-16 20:18:38 +000038
39#############################################################################
40# module tests
41
42class TestModule(unittest.TestCase):
43
44 def test_constants(self):
45 import datetime
46 self.assertEqual(datetime.MINYEAR, 1)
47 self.assertEqual(datetime.MAXYEAR, 9999)
48
49#############################################################################
50# tzinfo tests
51
52class FixedOffset(tzinfo):
53 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000054 if isinstance(offset, int):
55 offset = timedelta(minutes=offset)
56 if isinstance(dstoffset, int):
57 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000058 self.__offset = offset
59 self.__name = name
60 self.__dstoffset = dstoffset
61 def __repr__(self):
62 return self.__name.lower()
63 def utcoffset(self, dt):
64 return self.__offset
65 def tzname(self, dt):
66 return self.__name
67 def dst(self, dt):
68 return self.__dstoffset
69
Tim Petersfb8472c2002-12-21 05:04:42 +000070class PicklableFixedOffset(FixedOffset):
71 def __init__(self, offset=None, name=None, dstoffset=None):
72 FixedOffset.__init__(self, offset, name, dstoffset)
73
Tim Peters2a799bf2002-12-16 20:18:38 +000074class TestTZInfo(unittest.TestCase):
75
76 def test_non_abstractness(self):
77 # In order to allow subclasses to get pickled, the C implementation
78 # wasn't able to get away with having __init__ raise
79 # NotImplementedError.
80 useless = tzinfo()
81 dt = datetime.max
82 self.assertRaises(NotImplementedError, useless.tzname, dt)
83 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
84 self.assertRaises(NotImplementedError, useless.dst, dt)
85
86 def test_subclass_must_override(self):
87 class NotEnough(tzinfo):
88 def __init__(self, offset, name):
89 self.__offset = offset
90 self.__name = name
91 self.failUnless(issubclass(NotEnough, tzinfo))
92 ne = NotEnough(3, "NotByALongShot")
93 self.failUnless(isinstance(ne, tzinfo))
94
95 dt = datetime.now()
96 self.assertRaises(NotImplementedError, ne.tzname, dt)
97 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
98 self.assertRaises(NotImplementedError, ne.dst, dt)
99
100 def test_normal(self):
101 fo = FixedOffset(3, "Three")
102 self.failUnless(isinstance(fo, tzinfo))
103 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000104 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000105 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000106 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000107
108 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000109 # There's no point to pickling tzinfo objects on their own (they
110 # carry no data), but they need to be picklable anyway else
111 # concrete subclasses can't be pickled.
112 orig = tzinfo.__new__(tzinfo)
113 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000114 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000115 green = pickler.dumps(orig, proto)
116 derived = unpickler.loads(green)
117 self.failUnless(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000118
119 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000120 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000121 offset = timedelta(minutes=-300)
122 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000123 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000124 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000125 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000126 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000127 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000128 green = pickler.dumps(orig, proto)
129 derived = unpickler.loads(green)
130 self.failUnless(isinstance(derived, tzinfo))
131 self.failUnless(type(derived) is PicklableFixedOffset)
132 self.assertEqual(derived.utcoffset(None), offset)
133 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000134
135#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000136# Base clase for testing a particular aspect of timedelta, time, date and
137# datetime comparisons.
138
Guido van Rossumd8faa362007-04-27 19:54:29 +0000139class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000140 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
141
142 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
143 # legit constructor.
144
145 def test_harmless_mixed_comparison(self):
146 me = self.theclass(1, 1, 1)
147
148 self.failIf(me == ())
149 self.failUnless(me != ())
150 self.failIf(() == me)
151 self.failUnless(() != me)
152
Guido van Rossume2a383d2007-01-15 16:59:06 +0000153 self.failUnless(me in [1, 20, [], me])
154 self.failIf(me not in [1, 20, [], me])
Tim Peters07534a62003-02-07 22:50:28 +0000155
Guido van Rossume2a383d2007-01-15 16:59:06 +0000156 self.failUnless([] in [me, 1, 20, []])
157 self.failIf([] not in [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000158
159 def test_harmful_mixed_comparison(self):
160 me = self.theclass(1, 1, 1)
161
162 self.assertRaises(TypeError, lambda: me < ())
163 self.assertRaises(TypeError, lambda: me <= ())
164 self.assertRaises(TypeError, lambda: me > ())
165 self.assertRaises(TypeError, lambda: me >= ())
166
167 self.assertRaises(TypeError, lambda: () < me)
168 self.assertRaises(TypeError, lambda: () <= me)
169 self.assertRaises(TypeError, lambda: () > me)
170 self.assertRaises(TypeError, lambda: () >= me)
171
172 self.assertRaises(TypeError, cmp, (), me)
173 self.assertRaises(TypeError, cmp, me, ())
174
175#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000176# timedelta tests
177
Guido van Rossumd8faa362007-04-27 19:54:29 +0000178class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000179
180 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000181
182 def test_constructor(self):
183 eq = self.assertEqual
184 td = timedelta
185
186 # Check keyword args to constructor
187 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
188 milliseconds=0, microseconds=0))
189 eq(td(1), td(days=1))
190 eq(td(0, 1), td(seconds=1))
191 eq(td(0, 0, 1), td(microseconds=1))
192 eq(td(weeks=1), td(days=7))
193 eq(td(days=1), td(hours=24))
194 eq(td(hours=1), td(minutes=60))
195 eq(td(minutes=1), td(seconds=60))
196 eq(td(seconds=1), td(milliseconds=1000))
197 eq(td(milliseconds=1), td(microseconds=1000))
198
199 # Check float args to constructor
200 eq(td(weeks=1.0/7), td(days=1))
201 eq(td(days=1.0/24), td(hours=1))
202 eq(td(hours=1.0/60), td(minutes=1))
203 eq(td(minutes=1.0/60), td(seconds=1))
204 eq(td(seconds=0.001), td(milliseconds=1))
205 eq(td(milliseconds=0.001), td(microseconds=1))
206
207 def test_computations(self):
208 eq = self.assertEqual
209 td = timedelta
210
211 a = td(7) # One week
212 b = td(0, 60) # One minute
213 c = td(0, 0, 1000) # One millisecond
214 eq(a+b+c, td(7, 60, 1000))
215 eq(a-b, td(6, 24*3600 - 60))
216 eq(-a, td(-7))
217 eq(+a, td(7))
218 eq(-b, td(-1, 24*3600 - 60))
219 eq(-c, td(-1, 24*3600 - 1, 999000))
220 eq(abs(a), a)
221 eq(abs(-a), a)
222 eq(td(6, 24*3600), a)
223 eq(td(0, 0, 60*1000000), b)
224 eq(a*10, td(70))
225 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000226 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000227 eq(b*10, td(0, 600))
228 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000229 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000230 eq(c*10, td(0, 0, 10000))
231 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000232 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000233 eq(a*-1, -a)
234 eq(b*-2, -b-b)
235 eq(c*-2, -c+-c)
236 eq(b*(60*24), (b*60)*24)
237 eq(b*(60*24), (60*b)*24)
238 eq(c*1000, td(0, 1))
239 eq(1000*c, td(0, 1))
240 eq(a//7, td(1))
241 eq(b//10, td(0, 6))
242 eq(c//1000, td(0, 0, 1))
243 eq(a//10, td(0, 7*24*360))
244 eq(a//3600000, td(0, 0, 7*24*1000))
245
246 def test_disallowed_computations(self):
247 a = timedelta(42)
248
249 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000250 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000251 self.assertRaises(TypeError, lambda: a+i)
252 self.assertRaises(TypeError, lambda: a-i)
253 self.assertRaises(TypeError, lambda: i+a)
254 self.assertRaises(TypeError, lambda: i-a)
255
256 # Mul/div by float isn't supported.
257 x = 2.3
258 self.assertRaises(TypeError, lambda: a*x)
259 self.assertRaises(TypeError, lambda: x*a)
260 self.assertRaises(TypeError, lambda: a/x)
261 self.assertRaises(TypeError, lambda: x/a)
262 self.assertRaises(TypeError, lambda: a // x)
263 self.assertRaises(TypeError, lambda: x // a)
264
265 # Divison of int by timedelta doesn't make sense.
266 # Division by zero doesn't make sense.
Guido van Rossume2a383d2007-01-15 16:59:06 +0000267 for zero in 0, 0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000268 self.assertRaises(TypeError, lambda: zero // a)
269 self.assertRaises(ZeroDivisionError, lambda: a // zero)
270
271 def test_basic_attributes(self):
272 days, seconds, us = 1, 7, 31
273 td = timedelta(days, seconds, us)
274 self.assertEqual(td.days, days)
275 self.assertEqual(td.seconds, seconds)
276 self.assertEqual(td.microseconds, us)
277
278 def test_carries(self):
279 t1 = timedelta(days=100,
280 weeks=-7,
281 hours=-24*(100-49),
282 minutes=-3,
283 seconds=12,
284 microseconds=(3*60 - 12) * 1e6 + 1)
285 t2 = timedelta(microseconds=1)
286 self.assertEqual(t1, t2)
287
288 def test_hash_equality(self):
289 t1 = timedelta(days=100,
290 weeks=-7,
291 hours=-24*(100-49),
292 minutes=-3,
293 seconds=12,
294 microseconds=(3*60 - 12) * 1000000)
295 t2 = timedelta()
296 self.assertEqual(hash(t1), hash(t2))
297
298 t1 += timedelta(weeks=7)
299 t2 += timedelta(days=7*7)
300 self.assertEqual(t1, t2)
301 self.assertEqual(hash(t1), hash(t2))
302
303 d = {t1: 1}
304 d[t2] = 2
305 self.assertEqual(len(d), 1)
306 self.assertEqual(d[t1], 2)
307
308 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000309 args = 12, 34, 56
310 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000311 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000312 green = pickler.dumps(orig, proto)
313 derived = unpickler.loads(green)
314 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000315
316 def test_compare(self):
317 t1 = timedelta(2, 3, 4)
318 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000319 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000320 self.failUnless(t1 <= t2)
321 self.failUnless(t1 >= t2)
322 self.failUnless(not t1 != t2)
323 self.failUnless(not t1 < t2)
324 self.failUnless(not t1 > t2)
325 self.assertEqual(cmp(t1, t2), 0)
326 self.assertEqual(cmp(t2, t1), 0)
327
328 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
329 t2 = timedelta(*args) # this is larger than t1
330 self.failUnless(t1 < t2)
331 self.failUnless(t2 > t1)
332 self.failUnless(t1 <= t2)
333 self.failUnless(t2 >= t1)
334 self.failUnless(t1 != t2)
335 self.failUnless(t2 != t1)
336 self.failUnless(not t1 == t2)
337 self.failUnless(not t2 == t1)
338 self.failUnless(not t1 > t2)
339 self.failUnless(not t2 < t1)
340 self.failUnless(not t1 >= t2)
341 self.failUnless(not t2 <= t1)
342 self.assertEqual(cmp(t1, t2), -1)
343 self.assertEqual(cmp(t2, t1), 1)
344
Tim Peters68124bb2003-02-08 03:46:31 +0000345 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000346 self.assertEqual(t1 == badarg, False)
347 self.assertEqual(t1 != badarg, True)
348 self.assertEqual(badarg == t1, False)
349 self.assertEqual(badarg != t1, True)
350
Tim Peters2a799bf2002-12-16 20:18:38 +0000351 self.assertRaises(TypeError, lambda: t1 <= badarg)
352 self.assertRaises(TypeError, lambda: t1 < badarg)
353 self.assertRaises(TypeError, lambda: t1 > badarg)
354 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000355 self.assertRaises(TypeError, lambda: badarg <= t1)
356 self.assertRaises(TypeError, lambda: badarg < t1)
357 self.assertRaises(TypeError, lambda: badarg > t1)
358 self.assertRaises(TypeError, lambda: badarg >= t1)
359
360 def test_str(self):
361 td = timedelta
362 eq = self.assertEqual
363
364 eq(str(td(1)), "1 day, 0:00:00")
365 eq(str(td(-1)), "-1 day, 0:00:00")
366 eq(str(td(2)), "2 days, 0:00:00")
367 eq(str(td(-2)), "-2 days, 0:00:00")
368
369 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
370 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
371 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
372 "-210 days, 23:12:34")
373
374 eq(str(td(milliseconds=1)), "0:00:00.001000")
375 eq(str(td(microseconds=3)), "0:00:00.000003")
376
377 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
378 microseconds=999999)),
379 "999999999 days, 23:59:59.999999")
380
381 def test_roundtrip(self):
382 for td in (timedelta(days=999999999, hours=23, minutes=59,
383 seconds=59, microseconds=999999),
384 timedelta(days=-999999999),
385 timedelta(days=1, seconds=2, microseconds=3)):
386
387 # Verify td -> string -> td identity.
388 s = repr(td)
389 self.failUnless(s.startswith('datetime.'))
390 s = s[9:]
391 td2 = eval(s)
392 self.assertEqual(td, td2)
393
394 # Verify identity via reconstructing from pieces.
395 td2 = timedelta(td.days, td.seconds, td.microseconds)
396 self.assertEqual(td, td2)
397
398 def test_resolution_info(self):
399 self.assert_(isinstance(timedelta.min, timedelta))
400 self.assert_(isinstance(timedelta.max, timedelta))
401 self.assert_(isinstance(timedelta.resolution, timedelta))
402 self.assert_(timedelta.max > timedelta.min)
403 self.assertEqual(timedelta.min, timedelta(-999999999))
404 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
405 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
406
407 def test_overflow(self):
408 tiny = timedelta.resolution
409
410 td = timedelta.min + tiny
411 td -= tiny # no problem
412 self.assertRaises(OverflowError, td.__sub__, tiny)
413 self.assertRaises(OverflowError, td.__add__, -tiny)
414
415 td = timedelta.max - tiny
416 td += tiny # no problem
417 self.assertRaises(OverflowError, td.__add__, tiny)
418 self.assertRaises(OverflowError, td.__sub__, -tiny)
419
420 self.assertRaises(OverflowError, lambda: -timedelta.max)
421
422 def test_microsecond_rounding(self):
423 td = timedelta
424 eq = self.assertEqual
425
426 # Single-field rounding.
427 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
428 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
429 eq(td(milliseconds=0.6/1000), td(microseconds=1))
430 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
431
432 # Rounding due to contributions from more than one field.
433 us_per_hour = 3600e6
434 us_per_day = us_per_hour * 24
435 eq(td(days=.4/us_per_day), td(0))
436 eq(td(hours=.2/us_per_hour), td(0))
437 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
438
439 eq(td(days=-.4/us_per_day), td(0))
440 eq(td(hours=-.2/us_per_hour), td(0))
441 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
442
443 def test_massive_normalization(self):
444 td = timedelta(microseconds=-1)
445 self.assertEqual((td.days, td.seconds, td.microseconds),
446 (-1, 24*3600-1, 999999))
447
448 def test_bool(self):
449 self.failUnless(timedelta(1))
450 self.failUnless(timedelta(0, 1))
451 self.failUnless(timedelta(0, 0, 1))
452 self.failUnless(timedelta(microseconds=1))
453 self.failUnless(not timedelta(0))
454
Tim Petersb0c854d2003-05-17 15:57:00 +0000455 def test_subclass_timedelta(self):
456
457 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000458 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000459 def from_td(td):
460 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000461
462 def as_hours(self):
463 sum = (self.days * 24 +
464 self.seconds / 3600.0 +
465 self.microseconds / 3600e6)
466 return round(sum)
467
468 t1 = T(days=1)
469 self.assert_(type(t1) is T)
470 self.assertEqual(t1.as_hours(), 24)
471
472 t2 = T(days=-1, seconds=-3600)
473 self.assert_(type(t2) is T)
474 self.assertEqual(t2.as_hours(), -25)
475
476 t3 = t1 + t2
477 self.assert_(type(t3) is timedelta)
478 t4 = T.from_td(t3)
479 self.assert_(type(t4) is T)
480 self.assertEqual(t3.days, t4.days)
481 self.assertEqual(t3.seconds, t4.seconds)
482 self.assertEqual(t3.microseconds, t4.microseconds)
483 self.assertEqual(str(t3), str(t4))
484 self.assertEqual(t4.as_hours(), -1)
485
Tim Peters2a799bf2002-12-16 20:18:38 +0000486#############################################################################
487# date tests
488
489class TestDateOnly(unittest.TestCase):
490 # Tests here won't pass if also run on datetime objects, so don't
491 # subclass this to test datetimes too.
492
493 def test_delta_non_days_ignored(self):
494 dt = date(2000, 1, 2)
495 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
496 microseconds=5)
497 days = timedelta(delta.days)
498 self.assertEqual(days, timedelta(1))
499
500 dt2 = dt + delta
501 self.assertEqual(dt2, dt + days)
502
503 dt2 = delta + dt
504 self.assertEqual(dt2, dt + days)
505
506 dt2 = dt - delta
507 self.assertEqual(dt2, dt - days)
508
509 delta = -delta
510 days = timedelta(delta.days)
511 self.assertEqual(days, timedelta(-2))
512
513 dt2 = dt + delta
514 self.assertEqual(dt2, dt + days)
515
516 dt2 = delta + dt
517 self.assertEqual(dt2, dt + days)
518
519 dt2 = dt - delta
520 self.assertEqual(dt2, dt - days)
521
Tim Peters604c0132004-06-07 23:04:33 +0000522class SubclassDate(date):
523 sub_var = 1
524
Guido van Rossumd8faa362007-04-27 19:54:29 +0000525class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000526 # Tests here should pass for both dates and datetimes, except for a
527 # few tests that TestDateTime overrides.
528
529 theclass = date
530
531 def test_basic_attributes(self):
532 dt = self.theclass(2002, 3, 1)
533 self.assertEqual(dt.year, 2002)
534 self.assertEqual(dt.month, 3)
535 self.assertEqual(dt.day, 1)
536
537 def test_roundtrip(self):
538 for dt in (self.theclass(1, 2, 3),
539 self.theclass.today()):
540 # Verify dt -> string -> date identity.
541 s = repr(dt)
542 self.failUnless(s.startswith('datetime.'))
543 s = s[9:]
544 dt2 = eval(s)
545 self.assertEqual(dt, dt2)
546
547 # Verify identity via reconstructing from pieces.
548 dt2 = self.theclass(dt.year, dt.month, dt.day)
549 self.assertEqual(dt, dt2)
550
551 def test_ordinal_conversions(self):
552 # Check some fixed values.
553 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
554 (1, 12, 31, 365),
555 (2, 1, 1, 366),
556 # first example from "Calendrical Calculations"
557 (1945, 11, 12, 710347)]:
558 d = self.theclass(y, m, d)
559 self.assertEqual(n, d.toordinal())
560 fromord = self.theclass.fromordinal(n)
561 self.assertEqual(d, fromord)
562 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000563 # if we're checking something fancier than a date, verify
564 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000565 self.assertEqual(fromord.hour, 0)
566 self.assertEqual(fromord.minute, 0)
567 self.assertEqual(fromord.second, 0)
568 self.assertEqual(fromord.microsecond, 0)
569
Tim Peters0bf60bd2003-01-08 20:40:01 +0000570 # Check first and last days of year spottily across the whole
571 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000572 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000573 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
574 d = self.theclass(year, 1, 1)
575 n = d.toordinal()
576 d2 = self.theclass.fromordinal(n)
577 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000578 # Verify that moving back a day gets to the end of year-1.
579 if year > 1:
580 d = self.theclass.fromordinal(n-1)
581 d2 = self.theclass(year-1, 12, 31)
582 self.assertEqual(d, d2)
583 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000584
585 # Test every day in a leap-year and a non-leap year.
586 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
587 for year, isleap in (2000, True), (2002, False):
588 n = self.theclass(year, 1, 1).toordinal()
589 for month, maxday in zip(range(1, 13), dim):
590 if month == 2 and isleap:
591 maxday += 1
592 for day in range(1, maxday+1):
593 d = self.theclass(year, month, day)
594 self.assertEqual(d.toordinal(), n)
595 self.assertEqual(d, self.theclass.fromordinal(n))
596 n += 1
597
598 def test_extreme_ordinals(self):
599 a = self.theclass.min
600 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
601 aord = a.toordinal()
602 b = a.fromordinal(aord)
603 self.assertEqual(a, b)
604
605 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
606
607 b = a + timedelta(days=1)
608 self.assertEqual(b.toordinal(), aord + 1)
609 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
610
611 a = self.theclass.max
612 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
613 aord = a.toordinal()
614 b = a.fromordinal(aord)
615 self.assertEqual(a, b)
616
617 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
618
619 b = a - timedelta(days=1)
620 self.assertEqual(b.toordinal(), aord - 1)
621 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
622
623 def test_bad_constructor_arguments(self):
624 # bad years
625 self.theclass(MINYEAR, 1, 1) # no exception
626 self.theclass(MAXYEAR, 1, 1) # no exception
627 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
628 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
629 # bad months
630 self.theclass(2000, 1, 1) # no exception
631 self.theclass(2000, 12, 1) # no exception
632 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
633 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
634 # bad days
635 self.theclass(2000, 2, 29) # no exception
636 self.theclass(2004, 2, 29) # no exception
637 self.theclass(2400, 2, 29) # no exception
638 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
639 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
640 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
641 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
642 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
643 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
644
645 def test_hash_equality(self):
646 d = self.theclass(2000, 12, 31)
647 # same thing
648 e = self.theclass(2000, 12, 31)
649 self.assertEqual(d, e)
650 self.assertEqual(hash(d), hash(e))
651
652 dic = {d: 1}
653 dic[e] = 2
654 self.assertEqual(len(dic), 1)
655 self.assertEqual(dic[d], 2)
656 self.assertEqual(dic[e], 2)
657
658 d = self.theclass(2001, 1, 1)
659 # same thing
660 e = self.theclass(2001, 1, 1)
661 self.assertEqual(d, e)
662 self.assertEqual(hash(d), hash(e))
663
664 dic = {d: 1}
665 dic[e] = 2
666 self.assertEqual(len(dic), 1)
667 self.assertEqual(dic[d], 2)
668 self.assertEqual(dic[e], 2)
669
670 def test_computations(self):
671 a = self.theclass(2002, 1, 31)
672 b = self.theclass(1956, 1, 31)
673
674 diff = a-b
675 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
676 self.assertEqual(diff.seconds, 0)
677 self.assertEqual(diff.microseconds, 0)
678
679 day = timedelta(1)
680 week = timedelta(7)
681 a = self.theclass(2002, 3, 2)
682 self.assertEqual(a + day, self.theclass(2002, 3, 3))
683 self.assertEqual(day + a, self.theclass(2002, 3, 3))
684 self.assertEqual(a - day, self.theclass(2002, 3, 1))
685 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
686 self.assertEqual(a + week, self.theclass(2002, 3, 9))
687 self.assertEqual(a - week, self.theclass(2002, 2, 23))
688 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
689 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
690 self.assertEqual((a + week) - a, week)
691 self.assertEqual((a + day) - a, day)
692 self.assertEqual((a - week) - a, -week)
693 self.assertEqual((a - day) - a, -day)
694 self.assertEqual(a - (a + week), -week)
695 self.assertEqual(a - (a + day), -day)
696 self.assertEqual(a - (a - week), week)
697 self.assertEqual(a - (a - day), day)
698
699 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000700 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000701 self.assertRaises(TypeError, lambda: a+i)
702 self.assertRaises(TypeError, lambda: a-i)
703 self.assertRaises(TypeError, lambda: i+a)
704 self.assertRaises(TypeError, lambda: i-a)
705
706 # delta - date is senseless.
707 self.assertRaises(TypeError, lambda: day - a)
708 # mixing date and (delta or date) via * or // is senseless
709 self.assertRaises(TypeError, lambda: day * a)
710 self.assertRaises(TypeError, lambda: a * day)
711 self.assertRaises(TypeError, lambda: day // a)
712 self.assertRaises(TypeError, lambda: a // day)
713 self.assertRaises(TypeError, lambda: a * a)
714 self.assertRaises(TypeError, lambda: a // a)
715 # date + date is senseless
716 self.assertRaises(TypeError, lambda: a + a)
717
718 def test_overflow(self):
719 tiny = self.theclass.resolution
720
721 dt = self.theclass.min + tiny
722 dt -= tiny # no problem
723 self.assertRaises(OverflowError, dt.__sub__, tiny)
724 self.assertRaises(OverflowError, dt.__add__, -tiny)
725
726 dt = self.theclass.max - tiny
727 dt += tiny # no problem
728 self.assertRaises(OverflowError, dt.__add__, tiny)
729 self.assertRaises(OverflowError, dt.__sub__, -tiny)
730
731 def test_fromtimestamp(self):
732 import time
733
734 # Try an arbitrary fixed value.
735 year, month, day = 1999, 9, 19
736 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
737 d = self.theclass.fromtimestamp(ts)
738 self.assertEqual(d.year, year)
739 self.assertEqual(d.month, month)
740 self.assertEqual(d.day, day)
741
Tim Peters1b6f7a92004-06-20 02:50:16 +0000742 def test_insane_fromtimestamp(self):
743 # It's possible that some platform maps time_t to double,
744 # and that this test will fail there. This test should
745 # exempt such platforms (provided they return reasonable
746 # results!).
747 for insane in -1e200, 1e200:
748 self.assertRaises(ValueError, self.theclass.fromtimestamp,
749 insane)
750
Tim Peters2a799bf2002-12-16 20:18:38 +0000751 def test_today(self):
752 import time
753
754 # We claim that today() is like fromtimestamp(time.time()), so
755 # prove it.
756 for dummy in range(3):
757 today = self.theclass.today()
758 ts = time.time()
759 todayagain = self.theclass.fromtimestamp(ts)
760 if today == todayagain:
761 break
762 # There are several legit reasons that could fail:
763 # 1. It recently became midnight, between the today() and the
764 # time() calls.
765 # 2. The platform time() has such fine resolution that we'll
766 # never get the same value twice.
767 # 3. The platform time() has poor resolution, and we just
768 # happened to call today() right before a resolution quantum
769 # boundary.
770 # 4. The system clock got fiddled between calls.
771 # In any case, wait a little while and try again.
772 time.sleep(0.1)
773
774 # It worked or it didn't. If it didn't, assume it's reason #2, and
775 # let the test pass if they're within half a second of each other.
776 self.failUnless(today == todayagain or
777 abs(todayagain - today) < timedelta(seconds=0.5))
778
779 def test_weekday(self):
780 for i in range(7):
781 # March 4, 2002 is a Monday
782 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
783 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
784 # January 2, 1956 is a Monday
785 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
786 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
787
788 def test_isocalendar(self):
789 # Check examples from
790 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
791 for i in range(7):
792 d = self.theclass(2003, 12, 22+i)
793 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
794 d = self.theclass(2003, 12, 29) + timedelta(i)
795 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
796 d = self.theclass(2004, 1, 5+i)
797 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
798 d = self.theclass(2009, 12, 21+i)
799 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
800 d = self.theclass(2009, 12, 28) + timedelta(i)
801 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
802 d = self.theclass(2010, 1, 4+i)
803 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
804
805 def test_iso_long_years(self):
806 # Calculate long ISO years and compare to table from
807 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
808 ISO_LONG_YEARS_TABLE = """
809 4 32 60 88
810 9 37 65 93
811 15 43 71 99
812 20 48 76
813 26 54 82
814
815 105 133 161 189
816 111 139 167 195
817 116 144 172
818 122 150 178
819 128 156 184
820
821 201 229 257 285
822 207 235 263 291
823 212 240 268 296
824 218 246 274
825 224 252 280
826
827 303 331 359 387
828 308 336 364 392
829 314 342 370 398
830 320 348 376
831 325 353 381
832 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000833 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000834 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)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000916 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000917 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.
Martin v. Löwis10a60b32007-07-18 02:28:27 +00001100 self.theclass(bytes(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)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001377 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001378 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
Guido van Rossumd8faa362007-04-27 19:54:29 +00001455 def test_negative_float_fromtimestamp(self):
1456 # Windows doesn't accept negative timestamps
1457 if os.name == "nt":
1458 return
1459 # The result is tz-dependent; at least test that this doesn't
1460 # fail (like it did before bug 1646728 was fixed).
1461 self.theclass.fromtimestamp(-1.05)
1462
1463 def test_negative_float_utcfromtimestamp(self):
1464 # Windows doesn't accept negative timestamps
1465 if os.name == "nt":
1466 return
1467 d = self.theclass.utcfromtimestamp(-1.05)
1468 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1469
Tim Peters2a799bf2002-12-16 20:18:38 +00001470 def test_utcnow(self):
1471 import time
1472
1473 # Call it a success if utcnow() and utcfromtimestamp() are within
1474 # a second of each other.
1475 tolerance = timedelta(seconds=1)
1476 for dummy in range(3):
1477 from_now = self.theclass.utcnow()
1478 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1479 if abs(from_timestamp - from_now) <= tolerance:
1480 break
1481 # Else try again a few times.
1482 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1483
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001484 def test_strptime(self):
1485 import time
1486
1487 string = '2004-12-01 13:02:47'
1488 format = '%Y-%m-%d %H:%M:%S'
1489 expected = self.theclass(*(time.strptime(string, format)[0:6]))
1490 got = self.theclass.strptime(string, format)
1491 self.assertEqual(expected, got)
1492
Tim Peters2a799bf2002-12-16 20:18:38 +00001493 def test_more_timetuple(self):
1494 # This tests fields beyond those tested by the TestDate.test_timetuple.
1495 t = self.theclass(2004, 12, 31, 6, 22, 33)
1496 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1497 self.assertEqual(t.timetuple(),
1498 (t.year, t.month, t.day,
1499 t.hour, t.minute, t.second,
1500 t.weekday(),
1501 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1502 -1))
1503 tt = t.timetuple()
1504 self.assertEqual(tt.tm_year, t.year)
1505 self.assertEqual(tt.tm_mon, t.month)
1506 self.assertEqual(tt.tm_mday, t.day)
1507 self.assertEqual(tt.tm_hour, t.hour)
1508 self.assertEqual(tt.tm_min, t.minute)
1509 self.assertEqual(tt.tm_sec, t.second)
1510 self.assertEqual(tt.tm_wday, t.weekday())
1511 self.assertEqual(tt.tm_yday, t.toordinal() -
1512 date(t.year, 1, 1).toordinal() + 1)
1513 self.assertEqual(tt.tm_isdst, -1)
1514
1515 def test_more_strftime(self):
1516 # This tests fields beyond those tested by the TestDate.test_strftime.
1517 t = self.theclass(2004, 12, 31, 6, 22, 33)
1518 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1519 "12 31 04 33 22 06 366")
1520
1521 def test_extract(self):
1522 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1523 self.assertEqual(dt.date(), date(2002, 3, 4))
1524 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1525
1526 def test_combine(self):
1527 d = date(2002, 3, 4)
1528 t = time(18, 45, 3, 1234)
1529 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1530 combine = self.theclass.combine
1531 dt = combine(d, t)
1532 self.assertEqual(dt, expected)
1533
1534 dt = combine(time=t, date=d)
1535 self.assertEqual(dt, expected)
1536
1537 self.assertEqual(d, dt.date())
1538 self.assertEqual(t, dt.time())
1539 self.assertEqual(dt, combine(dt.date(), dt.time()))
1540
1541 self.assertRaises(TypeError, combine) # need an arg
1542 self.assertRaises(TypeError, combine, d) # need two args
1543 self.assertRaises(TypeError, combine, t, d) # args reversed
1544 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1545 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1546
Tim Peters12bf3392002-12-24 05:41:27 +00001547 def test_replace(self):
1548 cls = self.theclass
1549 args = [1, 2, 3, 4, 5, 6, 7]
1550 base = cls(*args)
1551 self.assertEqual(base, base.replace())
1552
1553 i = 0
1554 for name, newval in (("year", 2),
1555 ("month", 3),
1556 ("day", 4),
1557 ("hour", 5),
1558 ("minute", 6),
1559 ("second", 7),
1560 ("microsecond", 8)):
1561 newargs = args[:]
1562 newargs[i] = newval
1563 expected = cls(*newargs)
1564 got = base.replace(**{name: newval})
1565 self.assertEqual(expected, got)
1566 i += 1
1567
1568 # Out of bounds.
1569 base = cls(2000, 2, 29)
1570 self.assertRaises(ValueError, base.replace, year=2001)
1571
Tim Peters80475bb2002-12-25 07:40:55 +00001572 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001573 # Pretty boring! The TZ test is more interesting here. astimezone()
1574 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001575 dt = self.theclass.now()
1576 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001577 self.assertRaises(TypeError, dt.astimezone) # not enough args
1578 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1579 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001580 self.assertRaises(ValueError, dt.astimezone, f) # naive
1581 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001582
Tim Peters52dcce22003-01-23 16:36:11 +00001583 class Bogus(tzinfo):
1584 def utcoffset(self, dt): return None
1585 def dst(self, dt): return timedelta(0)
1586 bog = Bogus()
1587 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1588
1589 class AlsoBogus(tzinfo):
1590 def utcoffset(self, dt): return timedelta(0)
1591 def dst(self, dt): return None
1592 alsobog = AlsoBogus()
1593 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001594
Tim Petersa98924a2003-05-17 05:55:19 +00001595 def test_subclass_datetime(self):
1596
1597 class C(self.theclass):
1598 theAnswer = 42
1599
1600 def __new__(cls, *args, **kws):
1601 temp = kws.copy()
1602 extra = temp.pop('extra')
1603 result = self.theclass.__new__(cls, *args, **temp)
1604 result.extra = extra
1605 return result
1606
1607 def newmeth(self, start):
1608 return start + self.year + self.month + self.second
1609
1610 args = 2003, 4, 14, 12, 13, 41
1611
1612 dt1 = self.theclass(*args)
1613 dt2 = C(*args, **{'extra': 7})
1614
1615 self.assertEqual(dt2.__class__, C)
1616 self.assertEqual(dt2.theAnswer, 42)
1617 self.assertEqual(dt2.extra, 7)
1618 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1619 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1620 dt1.second - 7)
1621
Tim Peters604c0132004-06-07 23:04:33 +00001622class SubclassTime(time):
1623 sub_var = 1
1624
Guido van Rossumd8faa362007-04-27 19:54:29 +00001625class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001626
1627 theclass = time
1628
1629 def test_basic_attributes(self):
1630 t = self.theclass(12, 0)
1631 self.assertEqual(t.hour, 12)
1632 self.assertEqual(t.minute, 0)
1633 self.assertEqual(t.second, 0)
1634 self.assertEqual(t.microsecond, 0)
1635
1636 def test_basic_attributes_nonzero(self):
1637 # Make sure all attributes are non-zero so bugs in
1638 # bit-shifting access show up.
1639 t = self.theclass(12, 59, 59, 8000)
1640 self.assertEqual(t.hour, 12)
1641 self.assertEqual(t.minute, 59)
1642 self.assertEqual(t.second, 59)
1643 self.assertEqual(t.microsecond, 8000)
1644
1645 def test_roundtrip(self):
1646 t = self.theclass(1, 2, 3, 4)
1647
1648 # Verify t -> string -> time identity.
1649 s = repr(t)
1650 self.failUnless(s.startswith('datetime.'))
1651 s = s[9:]
1652 t2 = eval(s)
1653 self.assertEqual(t, t2)
1654
1655 # Verify identity via reconstructing from pieces.
1656 t2 = self.theclass(t.hour, t.minute, t.second,
1657 t.microsecond)
1658 self.assertEqual(t, t2)
1659
1660 def test_comparing(self):
1661 args = [1, 2, 3, 4]
1662 t1 = self.theclass(*args)
1663 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001664 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001665 self.failUnless(t1 <= t2)
1666 self.failUnless(t1 >= t2)
1667 self.failUnless(not t1 != t2)
1668 self.failUnless(not t1 < t2)
1669 self.failUnless(not t1 > t2)
1670 self.assertEqual(cmp(t1, t2), 0)
1671 self.assertEqual(cmp(t2, t1), 0)
1672
1673 for i in range(len(args)):
1674 newargs = args[:]
1675 newargs[i] = args[i] + 1
1676 t2 = self.theclass(*newargs) # this is larger than t1
1677 self.failUnless(t1 < t2)
1678 self.failUnless(t2 > t1)
1679 self.failUnless(t1 <= t2)
1680 self.failUnless(t2 >= t1)
1681 self.failUnless(t1 != t2)
1682 self.failUnless(t2 != t1)
1683 self.failUnless(not t1 == t2)
1684 self.failUnless(not t2 == t1)
1685 self.failUnless(not t1 > t2)
1686 self.failUnless(not t2 < t1)
1687 self.failUnless(not t1 >= t2)
1688 self.failUnless(not t2 <= t1)
1689 self.assertEqual(cmp(t1, t2), -1)
1690 self.assertEqual(cmp(t2, t1), 1)
1691
Tim Peters68124bb2003-02-08 03:46:31 +00001692 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001693 self.assertEqual(t1 == badarg, False)
1694 self.assertEqual(t1 != badarg, True)
1695 self.assertEqual(badarg == t1, False)
1696 self.assertEqual(badarg != t1, True)
1697
Tim Peters2a799bf2002-12-16 20:18:38 +00001698 self.assertRaises(TypeError, lambda: t1 <= badarg)
1699 self.assertRaises(TypeError, lambda: t1 < badarg)
1700 self.assertRaises(TypeError, lambda: t1 > badarg)
1701 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001702 self.assertRaises(TypeError, lambda: badarg <= t1)
1703 self.assertRaises(TypeError, lambda: badarg < t1)
1704 self.assertRaises(TypeError, lambda: badarg > t1)
1705 self.assertRaises(TypeError, lambda: badarg >= t1)
1706
1707 def test_bad_constructor_arguments(self):
1708 # bad hours
1709 self.theclass(0, 0) # no exception
1710 self.theclass(23, 0) # no exception
1711 self.assertRaises(ValueError, self.theclass, -1, 0)
1712 self.assertRaises(ValueError, self.theclass, 24, 0)
1713 # bad minutes
1714 self.theclass(23, 0) # no exception
1715 self.theclass(23, 59) # no exception
1716 self.assertRaises(ValueError, self.theclass, 23, -1)
1717 self.assertRaises(ValueError, self.theclass, 23, 60)
1718 # bad seconds
1719 self.theclass(23, 59, 0) # no exception
1720 self.theclass(23, 59, 59) # no exception
1721 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1722 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1723 # bad microseconds
1724 self.theclass(23, 59, 59, 0) # no exception
1725 self.theclass(23, 59, 59, 999999) # no exception
1726 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1727 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1728
1729 def test_hash_equality(self):
1730 d = self.theclass(23, 30, 17)
1731 e = self.theclass(23, 30, 17)
1732 self.assertEqual(d, e)
1733 self.assertEqual(hash(d), hash(e))
1734
1735 dic = {d: 1}
1736 dic[e] = 2
1737 self.assertEqual(len(dic), 1)
1738 self.assertEqual(dic[d], 2)
1739 self.assertEqual(dic[e], 2)
1740
1741 d = self.theclass(0, 5, 17)
1742 e = self.theclass(0, 5, 17)
1743 self.assertEqual(d, e)
1744 self.assertEqual(hash(d), hash(e))
1745
1746 dic = {d: 1}
1747 dic[e] = 2
1748 self.assertEqual(len(dic), 1)
1749 self.assertEqual(dic[d], 2)
1750 self.assertEqual(dic[e], 2)
1751
1752 def test_isoformat(self):
1753 t = self.theclass(4, 5, 1, 123)
1754 self.assertEqual(t.isoformat(), "04:05:01.000123")
1755 self.assertEqual(t.isoformat(), str(t))
1756
1757 t = self.theclass()
1758 self.assertEqual(t.isoformat(), "00:00:00")
1759 self.assertEqual(t.isoformat(), str(t))
1760
1761 t = self.theclass(microsecond=1)
1762 self.assertEqual(t.isoformat(), "00:00:00.000001")
1763 self.assertEqual(t.isoformat(), str(t))
1764
1765 t = self.theclass(microsecond=10)
1766 self.assertEqual(t.isoformat(), "00:00:00.000010")
1767 self.assertEqual(t.isoformat(), str(t))
1768
1769 t = self.theclass(microsecond=100)
1770 self.assertEqual(t.isoformat(), "00:00:00.000100")
1771 self.assertEqual(t.isoformat(), str(t))
1772
1773 t = self.theclass(microsecond=1000)
1774 self.assertEqual(t.isoformat(), "00:00:00.001000")
1775 self.assertEqual(t.isoformat(), str(t))
1776
1777 t = self.theclass(microsecond=10000)
1778 self.assertEqual(t.isoformat(), "00:00:00.010000")
1779 self.assertEqual(t.isoformat(), str(t))
1780
1781 t = self.theclass(microsecond=100000)
1782 self.assertEqual(t.isoformat(), "00:00:00.100000")
1783 self.assertEqual(t.isoformat(), str(t))
1784
Thomas Wouterscf297e42007-02-23 15:07:44 +00001785 def test_1653736(self):
1786 # verify it doesn't accept extra keyword arguments
1787 t = self.theclass(second=1)
1788 self.assertRaises(TypeError, t.isoformat, foo=3)
1789
Tim Peters2a799bf2002-12-16 20:18:38 +00001790 def test_strftime(self):
1791 t = self.theclass(1, 2, 3, 4)
1792 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1793 # A naive object replaces %z and %Z with empty strings.
1794 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1795
1796 def test_str(self):
1797 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1798 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1799 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1800 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1801 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1802
1803 def test_repr(self):
1804 name = 'datetime.' + self.theclass.__name__
1805 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1806 "%s(1, 2, 3, 4)" % name)
1807 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1808 "%s(10, 2, 3, 4000)" % name)
1809 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1810 "%s(0, 2, 3, 400000)" % name)
1811 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1812 "%s(12, 2, 3)" % name)
1813 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1814 "%s(23, 15)" % name)
1815
1816 def test_resolution_info(self):
1817 self.assert_(isinstance(self.theclass.min, self.theclass))
1818 self.assert_(isinstance(self.theclass.max, self.theclass))
1819 self.assert_(isinstance(self.theclass.resolution, timedelta))
1820 self.assert_(self.theclass.max > self.theclass.min)
1821
1822 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001823 args = 20, 59, 16, 64**2
1824 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001825 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001826 green = pickler.dumps(orig, proto)
1827 derived = unpickler.loads(green)
1828 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001829
Tim Peters604c0132004-06-07 23:04:33 +00001830 def test_pickling_subclass_time(self):
1831 args = 20, 59, 16, 64**2
1832 orig = SubclassTime(*args)
1833 for pickler, unpickler, proto in pickle_choices:
1834 green = pickler.dumps(orig, proto)
1835 derived = unpickler.loads(green)
1836 self.assertEqual(orig, derived)
1837
Tim Peters2a799bf2002-12-16 20:18:38 +00001838 def test_bool(self):
1839 cls = self.theclass
1840 self.failUnless(cls(1))
1841 self.failUnless(cls(0, 1))
1842 self.failUnless(cls(0, 0, 1))
1843 self.failUnless(cls(0, 0, 0, 1))
1844 self.failUnless(not cls(0))
1845 self.failUnless(not cls())
1846
Tim Peters12bf3392002-12-24 05:41:27 +00001847 def test_replace(self):
1848 cls = self.theclass
1849 args = [1, 2, 3, 4]
1850 base = cls(*args)
1851 self.assertEqual(base, base.replace())
1852
1853 i = 0
1854 for name, newval in (("hour", 5),
1855 ("minute", 6),
1856 ("second", 7),
1857 ("microsecond", 8)):
1858 newargs = args[:]
1859 newargs[i] = newval
1860 expected = cls(*newargs)
1861 got = base.replace(**{name: newval})
1862 self.assertEqual(expected, got)
1863 i += 1
1864
1865 # Out of bounds.
1866 base = cls(1)
1867 self.assertRaises(ValueError, base.replace, hour=24)
1868 self.assertRaises(ValueError, base.replace, minute=-1)
1869 self.assertRaises(ValueError, base.replace, second=100)
1870 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1871
Tim Petersa98924a2003-05-17 05:55:19 +00001872 def test_subclass_time(self):
1873
1874 class C(self.theclass):
1875 theAnswer = 42
1876
1877 def __new__(cls, *args, **kws):
1878 temp = kws.copy()
1879 extra = temp.pop('extra')
1880 result = self.theclass.__new__(cls, *args, **temp)
1881 result.extra = extra
1882 return result
1883
1884 def newmeth(self, start):
1885 return start + self.hour + self.second
1886
1887 args = 4, 5, 6
1888
1889 dt1 = self.theclass(*args)
1890 dt2 = C(*args, **{'extra': 7})
1891
1892 self.assertEqual(dt2.__class__, C)
1893 self.assertEqual(dt2.theAnswer, 42)
1894 self.assertEqual(dt2.extra, 7)
1895 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1896 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1897
Armin Rigof4afb212005-11-07 07:15:48 +00001898 def test_backdoor_resistance(self):
1899 # see TestDate.test_backdoor_resistance().
1900 base = '2:59.0'
1901 for hour_byte in ' ', '9', chr(24), '\xff':
1902 self.assertRaises(TypeError, self.theclass,
1903 hour_byte + base[1:])
1904
Tim Peters855fe882002-12-22 03:43:39 +00001905# A mixin for classes with a tzinfo= argument. Subclasses must define
1906# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001907# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001908class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001909
Tim Petersbad8ff02002-12-30 20:52:32 +00001910 def test_argument_passing(self):
1911 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001912 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001913 class introspective(tzinfo):
1914 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001915 def utcoffset(self, dt):
1916 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001917 dst = utcoffset
1918
1919 obj = cls(1, 2, 3, tzinfo=introspective())
1920
Tim Peters0bf60bd2003-01-08 20:40:01 +00001921 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001922 self.assertEqual(obj.tzname(), expected)
1923
Tim Peters0bf60bd2003-01-08 20:40:01 +00001924 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001925 self.assertEqual(obj.utcoffset(), expected)
1926 self.assertEqual(obj.dst(), expected)
1927
Tim Peters855fe882002-12-22 03:43:39 +00001928 def test_bad_tzinfo_classes(self):
1929 cls = self.theclass
1930 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001931
Tim Peters855fe882002-12-22 03:43:39 +00001932 class NiceTry(object):
1933 def __init__(self): pass
1934 def utcoffset(self, dt): pass
1935 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1936
1937 class BetterTry(tzinfo):
1938 def __init__(self): pass
1939 def utcoffset(self, dt): pass
1940 b = BetterTry()
1941 t = cls(1, 1, 1, tzinfo=b)
1942 self.failUnless(t.tzinfo is b)
1943
1944 def test_utc_offset_out_of_bounds(self):
1945 class Edgy(tzinfo):
1946 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00001947 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00001948 def utcoffset(self, dt):
1949 return self.offset
1950
1951 cls = self.theclass
1952 for offset, legit in ((-1440, False),
1953 (-1439, True),
1954 (1439, True),
1955 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001956 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00001957 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001958 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00001959 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001960 else:
1961 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00001962 if legit:
1963 aofs = abs(offset)
1964 h, m = divmod(aofs, 60)
1965 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001966 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00001967 t = t.timetz()
1968 self.assertEqual(str(t), "01:02:03" + tag)
1969 else:
1970 self.assertRaises(ValueError, str, t)
1971
1972 def test_tzinfo_classes(self):
1973 cls = self.theclass
1974 class C1(tzinfo):
1975 def utcoffset(self, dt): return None
1976 def dst(self, dt): return None
1977 def tzname(self, dt): return None
1978 for t in (cls(1, 1, 1),
1979 cls(1, 1, 1, tzinfo=None),
1980 cls(1, 1, 1, tzinfo=C1())):
1981 self.failUnless(t.utcoffset() is None)
1982 self.failUnless(t.dst() is None)
1983 self.failUnless(t.tzname() is None)
1984
Tim Peters855fe882002-12-22 03:43:39 +00001985 class C3(tzinfo):
1986 def utcoffset(self, dt): return timedelta(minutes=-1439)
1987 def dst(self, dt): return timedelta(minutes=1439)
1988 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001989 t = cls(1, 1, 1, tzinfo=C3())
1990 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
1991 self.assertEqual(t.dst(), timedelta(minutes=1439))
1992 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00001993
1994 # Wrong types.
1995 class C4(tzinfo):
1996 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001997 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00001998 def tzname(self, dt): return 0
1999 t = cls(1, 1, 1, tzinfo=C4())
2000 self.assertRaises(TypeError, t.utcoffset)
2001 self.assertRaises(TypeError, t.dst)
2002 self.assertRaises(TypeError, t.tzname)
2003
2004 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002005 class C6(tzinfo):
2006 def utcoffset(self, dt): return timedelta(hours=-24)
2007 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002008 t = cls(1, 1, 1, tzinfo=C6())
2009 self.assertRaises(ValueError, t.utcoffset)
2010 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002011
2012 # Not a whole number of minutes.
2013 class C7(tzinfo):
2014 def utcoffset(self, dt): return timedelta(seconds=61)
2015 def dst(self, dt): return timedelta(microseconds=-81)
2016 t = cls(1, 1, 1, tzinfo=C7())
2017 self.assertRaises(ValueError, t.utcoffset)
2018 self.assertRaises(ValueError, t.dst)
2019
Tim Peters4c0db782002-12-26 05:01:19 +00002020 def test_aware_compare(self):
2021 cls = self.theclass
2022
Tim Peters60c76e42002-12-27 00:41:11 +00002023 # Ensure that utcoffset() gets ignored if the comparands have
2024 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002025 class OperandDependentOffset(tzinfo):
2026 def utcoffset(self, t):
2027 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002028 # d0 and d1 equal after adjustment
2029 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002030 else:
Tim Peters397301e2003-01-02 21:28:08 +00002031 # d2 off in the weeds
2032 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002033
2034 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2035 d0 = base.replace(minute=3)
2036 d1 = base.replace(minute=9)
2037 d2 = base.replace(minute=11)
2038 for x in d0, d1, d2:
2039 for y in d0, d1, d2:
2040 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002041 expected = cmp(x.minute, y.minute)
2042 self.assertEqual(got, expected)
2043
2044 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002045 # Note that a time can't actually have an operand-depedent offset,
2046 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2047 # so skip this test for time.
2048 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002049 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2050 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2051 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2052 for x in d0, d1, d2:
2053 for y in d0, d1, d2:
2054 got = cmp(x, y)
2055 if (x is d0 or x is d1) and (y is d0 or y is d1):
2056 expected = 0
2057 elif x is y is d2:
2058 expected = 0
2059 elif x is d2:
2060 expected = -1
2061 else:
2062 assert y is d2
2063 expected = 1
2064 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002065
Tim Peters855fe882002-12-22 03:43:39 +00002066
Tim Peters0bf60bd2003-01-08 20:40:01 +00002067# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002068class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002069 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002070
2071 def test_empty(self):
2072 t = self.theclass()
2073 self.assertEqual(t.hour, 0)
2074 self.assertEqual(t.minute, 0)
2075 self.assertEqual(t.second, 0)
2076 self.assertEqual(t.microsecond, 0)
2077 self.failUnless(t.tzinfo is None)
2078
Tim Peters2a799bf2002-12-16 20:18:38 +00002079 def test_zones(self):
2080 est = FixedOffset(-300, "EST", 1)
2081 utc = FixedOffset(0, "UTC", -2)
2082 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002083 t1 = time( 7, 47, tzinfo=est)
2084 t2 = time(12, 47, tzinfo=utc)
2085 t3 = time(13, 47, tzinfo=met)
2086 t4 = time(microsecond=40)
2087 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002088
2089 self.assertEqual(t1.tzinfo, est)
2090 self.assertEqual(t2.tzinfo, utc)
2091 self.assertEqual(t3.tzinfo, met)
2092 self.failUnless(t4.tzinfo is None)
2093 self.assertEqual(t5.tzinfo, utc)
2094
Tim Peters855fe882002-12-22 03:43:39 +00002095 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2096 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2097 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002098 self.failUnless(t4.utcoffset() is None)
2099 self.assertRaises(TypeError, t1.utcoffset, "no args")
2100
2101 self.assertEqual(t1.tzname(), "EST")
2102 self.assertEqual(t2.tzname(), "UTC")
2103 self.assertEqual(t3.tzname(), "MET")
2104 self.failUnless(t4.tzname() is None)
2105 self.assertRaises(TypeError, t1.tzname, "no args")
2106
Tim Peters855fe882002-12-22 03:43:39 +00002107 self.assertEqual(t1.dst(), timedelta(minutes=1))
2108 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2109 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002110 self.failUnless(t4.dst() is None)
2111 self.assertRaises(TypeError, t1.dst, "no args")
2112
2113 self.assertEqual(hash(t1), hash(t2))
2114 self.assertEqual(hash(t1), hash(t3))
2115 self.assertEqual(hash(t2), hash(t3))
2116
2117 self.assertEqual(t1, t2)
2118 self.assertEqual(t1, t3)
2119 self.assertEqual(t2, t3)
2120 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2121 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2122 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2123
2124 self.assertEqual(str(t1), "07:47:00-05:00")
2125 self.assertEqual(str(t2), "12:47:00+00:00")
2126 self.assertEqual(str(t3), "13:47:00+01:00")
2127 self.assertEqual(str(t4), "00:00:00.000040")
2128 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2129
2130 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2131 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2132 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2133 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2134 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2135
Tim Peters0bf60bd2003-01-08 20:40:01 +00002136 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002137 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2138 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2139 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2140 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2141 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2142
2143 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2144 "07:47:00 %Z=EST %z=-0500")
2145 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2146 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2147
2148 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002149 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002150 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2151 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2152
Tim Petersb92bb712002-12-21 17:44:07 +00002153 # Check that an invalid tzname result raises an exception.
2154 class Badtzname(tzinfo):
2155 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002156 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002157 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2158 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002159
2160 def test_hash_edge_cases(self):
2161 # Offsets that overflow a basic time.
2162 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2163 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2164 self.assertEqual(hash(t1), hash(t2))
2165
2166 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2167 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2168 self.assertEqual(hash(t1), hash(t2))
2169
Tim Peters2a799bf2002-12-16 20:18:38 +00002170 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002171 # Try one without a tzinfo.
2172 args = 20, 59, 16, 64**2
2173 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002174 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002175 green = pickler.dumps(orig, proto)
2176 derived = unpickler.loads(green)
2177 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002178
2179 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002180 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002181 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002182 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002183 green = pickler.dumps(orig, proto)
2184 derived = unpickler.loads(green)
2185 self.assertEqual(orig, derived)
2186 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2187 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2188 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002189
2190 def test_more_bool(self):
2191 # Test cases with non-None tzinfo.
2192 cls = self.theclass
2193
2194 t = cls(0, tzinfo=FixedOffset(-300, ""))
2195 self.failUnless(t)
2196
2197 t = cls(5, tzinfo=FixedOffset(-300, ""))
2198 self.failUnless(t)
2199
2200 t = cls(5, tzinfo=FixedOffset(300, ""))
2201 self.failUnless(not t)
2202
2203 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2204 self.failUnless(not t)
2205
2206 # Mostly ensuring this doesn't overflow internally.
2207 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2208 self.failUnless(t)
2209
2210 # But this should yield a value error -- the utcoffset is bogus.
2211 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2212 self.assertRaises(ValueError, lambda: bool(t))
2213
2214 # Likewise.
2215 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2216 self.assertRaises(ValueError, lambda: bool(t))
2217
Tim Peters12bf3392002-12-24 05:41:27 +00002218 def test_replace(self):
2219 cls = self.theclass
2220 z100 = FixedOffset(100, "+100")
2221 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2222 args = [1, 2, 3, 4, z100]
2223 base = cls(*args)
2224 self.assertEqual(base, base.replace())
2225
2226 i = 0
2227 for name, newval in (("hour", 5),
2228 ("minute", 6),
2229 ("second", 7),
2230 ("microsecond", 8),
2231 ("tzinfo", zm200)):
2232 newargs = args[:]
2233 newargs[i] = newval
2234 expected = cls(*newargs)
2235 got = base.replace(**{name: newval})
2236 self.assertEqual(expected, got)
2237 i += 1
2238
2239 # Ensure we can get rid of a tzinfo.
2240 self.assertEqual(base.tzname(), "+100")
2241 base2 = base.replace(tzinfo=None)
2242 self.failUnless(base2.tzinfo is None)
2243 self.failUnless(base2.tzname() is None)
2244
2245 # Ensure we can add one.
2246 base3 = base2.replace(tzinfo=z100)
2247 self.assertEqual(base, base3)
2248 self.failUnless(base.tzinfo is base3.tzinfo)
2249
2250 # Out of bounds.
2251 base = cls(1)
2252 self.assertRaises(ValueError, base.replace, hour=24)
2253 self.assertRaises(ValueError, base.replace, minute=-1)
2254 self.assertRaises(ValueError, base.replace, second=100)
2255 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2256
Tim Peters60c76e42002-12-27 00:41:11 +00002257 def test_mixed_compare(self):
2258 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002259 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002260 self.assertEqual(t1, t2)
2261 t2 = t2.replace(tzinfo=None)
2262 self.assertEqual(t1, t2)
2263 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2264 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002265 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2266 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002267
Tim Peters0bf60bd2003-01-08 20:40:01 +00002268 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002269 class Varies(tzinfo):
2270 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002271 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002272 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002273 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002274 return self.offset
2275
2276 v = Varies()
2277 t1 = t2.replace(tzinfo=v)
2278 t2 = t2.replace(tzinfo=v)
2279 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2280 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2281 self.assertEqual(t1, t2)
2282
2283 # But if they're not identical, it isn't ignored.
2284 t2 = t2.replace(tzinfo=Varies())
2285 self.failUnless(t1 < t2) # t1's offset counter still going up
2286
Tim Petersa98924a2003-05-17 05:55:19 +00002287 def test_subclass_timetz(self):
2288
2289 class C(self.theclass):
2290 theAnswer = 42
2291
2292 def __new__(cls, *args, **kws):
2293 temp = kws.copy()
2294 extra = temp.pop('extra')
2295 result = self.theclass.__new__(cls, *args, **temp)
2296 result.extra = extra
2297 return result
2298
2299 def newmeth(self, start):
2300 return start + self.hour + self.second
2301
2302 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2303
2304 dt1 = self.theclass(*args)
2305 dt2 = C(*args, **{'extra': 7})
2306
2307 self.assertEqual(dt2.__class__, C)
2308 self.assertEqual(dt2.theAnswer, 42)
2309 self.assertEqual(dt2.extra, 7)
2310 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2311 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2312
Tim Peters4c0db782002-12-26 05:01:19 +00002313
Tim Peters0bf60bd2003-01-08 20:40:01 +00002314# Testing datetime objects with a non-None tzinfo.
2315
Guido van Rossumd8faa362007-04-27 19:54:29 +00002316class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002317 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002318
2319 def test_trivial(self):
2320 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2321 self.assertEqual(dt.year, 1)
2322 self.assertEqual(dt.month, 2)
2323 self.assertEqual(dt.day, 3)
2324 self.assertEqual(dt.hour, 4)
2325 self.assertEqual(dt.minute, 5)
2326 self.assertEqual(dt.second, 6)
2327 self.assertEqual(dt.microsecond, 7)
2328 self.assertEqual(dt.tzinfo, None)
2329
2330 def test_even_more_compare(self):
2331 # The test_compare() and test_more_compare() inherited from TestDate
2332 # and TestDateTime covered non-tzinfo cases.
2333
2334 # Smallest possible after UTC adjustment.
2335 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2336 # Largest possible after UTC adjustment.
2337 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2338 tzinfo=FixedOffset(-1439, ""))
2339
2340 # Make sure those compare correctly, and w/o overflow.
2341 self.failUnless(t1 < t2)
2342 self.failUnless(t1 != t2)
2343 self.failUnless(t2 > t1)
2344
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002345 self.assertEqual(t1, t1)
2346 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002347
2348 # Equal afer adjustment.
2349 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2350 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2351 self.assertEqual(t1, t2)
2352
2353 # Change t1 not to subtract a minute, and t1 should be larger.
2354 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2355 self.failUnless(t1 > t2)
2356
2357 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2358 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2359 self.failUnless(t1 < t2)
2360
2361 # Back to the original t1, but make seconds resolve it.
2362 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2363 second=1)
2364 self.failUnless(t1 > t2)
2365
2366 # Likewise, but make microseconds resolve it.
2367 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2368 microsecond=1)
2369 self.failUnless(t1 > t2)
2370
2371 # Make t2 naive and it should fail.
2372 t2 = self.theclass.min
2373 self.assertRaises(TypeError, lambda: t1 == t2)
2374 self.assertEqual(t2, t2)
2375
2376 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2377 class Naive(tzinfo):
2378 def utcoffset(self, dt): return None
2379 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2380 self.assertRaises(TypeError, lambda: t1 == t2)
2381 self.assertEqual(t2, t2)
2382
2383 # OTOH, it's OK to compare two of these mixing the two ways of being
2384 # naive.
2385 t1 = self.theclass(5, 6, 7)
2386 self.assertEqual(t1, t2)
2387
2388 # Try a bogus uctoffset.
2389 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002390 def utcoffset(self, dt):
2391 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002392 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2393 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002394 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002395
Tim Peters2a799bf2002-12-16 20:18:38 +00002396 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002397 # Try one without a tzinfo.
2398 args = 6, 7, 23, 20, 59, 1, 64**2
2399 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002400 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002401 green = pickler.dumps(orig, proto)
2402 derived = unpickler.loads(green)
2403 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002404
2405 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002406 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002407 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002408 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002409 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002410 green = pickler.dumps(orig, proto)
2411 derived = unpickler.loads(green)
2412 self.assertEqual(orig, derived)
2413 self.failUnless(isinstance(derived.tzinfo,
2414 PicklableFixedOffset))
2415 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2416 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002417
2418 def test_extreme_hashes(self):
2419 # If an attempt is made to hash these via subtracting the offset
2420 # then hashing a datetime object, OverflowError results. The
2421 # Python implementation used to blow up here.
2422 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2423 hash(t)
2424 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2425 tzinfo=FixedOffset(-1439, ""))
2426 hash(t)
2427
2428 # OTOH, an OOB offset should blow up.
2429 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2430 self.assertRaises(ValueError, hash, t)
2431
2432 def test_zones(self):
2433 est = FixedOffset(-300, "EST")
2434 utc = FixedOffset(0, "UTC")
2435 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002436 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2437 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2438 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002439 self.assertEqual(t1.tzinfo, est)
2440 self.assertEqual(t2.tzinfo, utc)
2441 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002442 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2443 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2444 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002445 self.assertEqual(t1.tzname(), "EST")
2446 self.assertEqual(t2.tzname(), "UTC")
2447 self.assertEqual(t3.tzname(), "MET")
2448 self.assertEqual(hash(t1), hash(t2))
2449 self.assertEqual(hash(t1), hash(t3))
2450 self.assertEqual(hash(t2), hash(t3))
2451 self.assertEqual(t1, t2)
2452 self.assertEqual(t1, t3)
2453 self.assertEqual(t2, t3)
2454 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2455 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2456 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002457 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002458 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2459 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2460 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2461
2462 def test_combine(self):
2463 met = FixedOffset(60, "MET")
2464 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002465 tz = time(18, 45, 3, 1234, tzinfo=met)
2466 dt = datetime.combine(d, tz)
2467 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002468 tzinfo=met))
2469
2470 def test_extract(self):
2471 met = FixedOffset(60, "MET")
2472 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2473 self.assertEqual(dt.date(), date(2002, 3, 4))
2474 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002475 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002476
2477 def test_tz_aware_arithmetic(self):
2478 import random
2479
2480 now = self.theclass.now()
2481 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002482 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002483 nowaware = self.theclass.combine(now.date(), timeaware)
2484 self.failUnless(nowaware.tzinfo is tz55)
2485 self.assertEqual(nowaware.timetz(), timeaware)
2486
2487 # Can't mix aware and non-aware.
2488 self.assertRaises(TypeError, lambda: now - nowaware)
2489 self.assertRaises(TypeError, lambda: nowaware - now)
2490
Tim Peters0bf60bd2003-01-08 20:40:01 +00002491 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002492 self.assertRaises(TypeError, lambda: now + nowaware)
2493 self.assertRaises(TypeError, lambda: nowaware + now)
2494 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2495
2496 # Subtracting should yield 0.
2497 self.assertEqual(now - now, timedelta(0))
2498 self.assertEqual(nowaware - nowaware, timedelta(0))
2499
2500 # Adding a delta should preserve tzinfo.
2501 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2502 nowawareplus = nowaware + delta
2503 self.failUnless(nowaware.tzinfo is tz55)
2504 nowawareplus2 = delta + nowaware
2505 self.failUnless(nowawareplus2.tzinfo is tz55)
2506 self.assertEqual(nowawareplus, nowawareplus2)
2507
2508 # that - delta should be what we started with, and that - what we
2509 # started with should be delta.
2510 diff = nowawareplus - delta
2511 self.failUnless(diff.tzinfo is tz55)
2512 self.assertEqual(nowaware, diff)
2513 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2514 self.assertEqual(nowawareplus - nowaware, delta)
2515
2516 # Make up a random timezone.
2517 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002518 # Attach it to nowawareplus.
2519 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002520 self.failUnless(nowawareplus.tzinfo is tzr)
2521 # Make sure the difference takes the timezone adjustments into account.
2522 got = nowaware - nowawareplus
2523 # Expected: (nowaware base - nowaware offset) -
2524 # (nowawareplus base - nowawareplus offset) =
2525 # (nowaware base - nowawareplus base) +
2526 # (nowawareplus offset - nowaware offset) =
2527 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002528 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002529 self.assertEqual(got, expected)
2530
2531 # Try max possible difference.
2532 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2533 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2534 tzinfo=FixedOffset(-1439, "max"))
2535 maxdiff = max - min
2536 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2537 timedelta(minutes=2*1439))
2538
2539 def test_tzinfo_now(self):
2540 meth = self.theclass.now
2541 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2542 base = meth()
2543 # Try with and without naming the keyword.
2544 off42 = FixedOffset(42, "42")
2545 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002546 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002547 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002548 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002549 # Bad argument with and w/o naming the keyword.
2550 self.assertRaises(TypeError, meth, 16)
2551 self.assertRaises(TypeError, meth, tzinfo=16)
2552 # Bad keyword name.
2553 self.assertRaises(TypeError, meth, tinfo=off42)
2554 # Too many args.
2555 self.assertRaises(TypeError, meth, off42, off42)
2556
Tim Peters10cadce2003-01-23 19:58:02 +00002557 # We don't know which time zone we're in, and don't have a tzinfo
2558 # class to represent it, so seeing whether a tz argument actually
2559 # does a conversion is tricky.
2560 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2561 utc = FixedOffset(0, "utc", 0)
2562 for dummy in range(3):
2563 now = datetime.now(weirdtz)
2564 self.failUnless(now.tzinfo is weirdtz)
2565 utcnow = datetime.utcnow().replace(tzinfo=utc)
2566 now2 = utcnow.astimezone(weirdtz)
2567 if abs(now - now2) < timedelta(seconds=30):
2568 break
2569 # Else the code is broken, or more than 30 seconds passed between
2570 # calls; assuming the latter, just try again.
2571 else:
2572 # Three strikes and we're out.
2573 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2574
Tim Peters2a799bf2002-12-16 20:18:38 +00002575 def test_tzinfo_fromtimestamp(self):
2576 import time
2577 meth = self.theclass.fromtimestamp
2578 ts = time.time()
2579 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2580 base = meth(ts)
2581 # Try with and without naming the keyword.
2582 off42 = FixedOffset(42, "42")
2583 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002584 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002585 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002586 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002587 # Bad argument with and w/o naming the keyword.
2588 self.assertRaises(TypeError, meth, ts, 16)
2589 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2590 # Bad keyword name.
2591 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2592 # Too many args.
2593 self.assertRaises(TypeError, meth, ts, off42, off42)
2594 # Too few args.
2595 self.assertRaises(TypeError, meth)
2596
Tim Peters2a44a8d2003-01-23 20:53:10 +00002597 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002598 timestamp = 1000000000
2599 utcdatetime = datetime.utcfromtimestamp(timestamp)
2600 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2601 # But on some flavor of Mac, it's nowhere near that. So we can't have
2602 # any idea here what time that actually is, we can only test that
2603 # relative changes match.
2604 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2605 tz = FixedOffset(utcoffset, "tz", 0)
2606 expected = utcdatetime + utcoffset
2607 got = datetime.fromtimestamp(timestamp, tz)
2608 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002609
Tim Peters2a799bf2002-12-16 20:18:38 +00002610 def test_tzinfo_utcnow(self):
2611 meth = self.theclass.utcnow
2612 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2613 base = meth()
2614 # Try with and without naming the keyword; for whatever reason,
2615 # utcnow() doesn't accept a tzinfo argument.
2616 off42 = FixedOffset(42, "42")
2617 self.assertRaises(TypeError, meth, off42)
2618 self.assertRaises(TypeError, meth, tzinfo=off42)
2619
2620 def test_tzinfo_utcfromtimestamp(self):
2621 import time
2622 meth = self.theclass.utcfromtimestamp
2623 ts = time.time()
2624 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2625 base = meth(ts)
2626 # Try with and without naming the keyword; for whatever reason,
2627 # utcfromtimestamp() doesn't accept a tzinfo argument.
2628 off42 = FixedOffset(42, "42")
2629 self.assertRaises(TypeError, meth, ts, off42)
2630 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2631
2632 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002633 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002634 # DST flag.
2635 class DST(tzinfo):
2636 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002637 if isinstance(dstvalue, int):
2638 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002639 self.dstvalue = dstvalue
2640 def dst(self, dt):
2641 return self.dstvalue
2642
2643 cls = self.theclass
2644 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2645 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2646 t = d.timetuple()
2647 self.assertEqual(1, t.tm_year)
2648 self.assertEqual(1, t.tm_mon)
2649 self.assertEqual(1, t.tm_mday)
2650 self.assertEqual(10, t.tm_hour)
2651 self.assertEqual(20, t.tm_min)
2652 self.assertEqual(30, t.tm_sec)
2653 self.assertEqual(0, t.tm_wday)
2654 self.assertEqual(1, t.tm_yday)
2655 self.assertEqual(flag, t.tm_isdst)
2656
2657 # dst() returns wrong type.
2658 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2659
2660 # dst() at the edge.
2661 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2662 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2663
2664 # dst() out of range.
2665 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2666 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2667
2668 def test_utctimetuple(self):
2669 class DST(tzinfo):
2670 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002671 if isinstance(dstvalue, int):
2672 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002673 self.dstvalue = dstvalue
2674 def dst(self, dt):
2675 return self.dstvalue
2676
2677 cls = self.theclass
2678 # This can't work: DST didn't implement utcoffset.
2679 self.assertRaises(NotImplementedError,
2680 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2681
2682 class UOFS(DST):
2683 def __init__(self, uofs, dofs=None):
2684 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002685 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002686 def utcoffset(self, dt):
2687 return self.uofs
2688
2689 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2690 # in effect for a UTC time.
2691 for dstvalue in -33, 33, 0, None:
2692 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2693 t = d.utctimetuple()
2694 self.assertEqual(d.year, t.tm_year)
2695 self.assertEqual(d.month, t.tm_mon)
2696 self.assertEqual(d.day, t.tm_mday)
2697 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2698 self.assertEqual(13, t.tm_min)
2699 self.assertEqual(d.second, t.tm_sec)
2700 self.assertEqual(d.weekday(), t.tm_wday)
2701 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2702 t.tm_yday)
2703 self.assertEqual(0, t.tm_isdst)
2704
2705 # At the edges, UTC adjustment can normalize into years out-of-range
2706 # for a datetime object. Ensure that a correct timetuple is
2707 # created anyway.
2708 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2709 # That goes back 1 minute less than a full day.
2710 t = tiny.utctimetuple()
2711 self.assertEqual(t.tm_year, MINYEAR-1)
2712 self.assertEqual(t.tm_mon, 12)
2713 self.assertEqual(t.tm_mday, 31)
2714 self.assertEqual(t.tm_hour, 0)
2715 self.assertEqual(t.tm_min, 1)
2716 self.assertEqual(t.tm_sec, 37)
2717 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2718 self.assertEqual(t.tm_isdst, 0)
2719
2720 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2721 # That goes forward 1 minute less than a full day.
2722 t = huge.utctimetuple()
2723 self.assertEqual(t.tm_year, MAXYEAR+1)
2724 self.assertEqual(t.tm_mon, 1)
2725 self.assertEqual(t.tm_mday, 1)
2726 self.assertEqual(t.tm_hour, 23)
2727 self.assertEqual(t.tm_min, 58)
2728 self.assertEqual(t.tm_sec, 37)
2729 self.assertEqual(t.tm_yday, 1)
2730 self.assertEqual(t.tm_isdst, 0)
2731
2732 def test_tzinfo_isoformat(self):
2733 zero = FixedOffset(0, "+00:00")
2734 plus = FixedOffset(220, "+03:40")
2735 minus = FixedOffset(-231, "-03:51")
2736 unknown = FixedOffset(None, "")
2737
2738 cls = self.theclass
2739 datestr = '0001-02-03'
2740 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002741 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002742 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2743 timestr = '04:05:59' + (us and '.987001' or '')
2744 ofsstr = ofs is not None and d.tzname() or ''
2745 tailstr = timestr + ofsstr
2746 iso = d.isoformat()
2747 self.assertEqual(iso, datestr + 'T' + tailstr)
2748 self.assertEqual(iso, d.isoformat('T'))
2749 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002750 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002751 self.assertEqual(str(d), datestr + ' ' + tailstr)
2752
Tim Peters12bf3392002-12-24 05:41:27 +00002753 def test_replace(self):
2754 cls = self.theclass
2755 z100 = FixedOffset(100, "+100")
2756 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2757 args = [1, 2, 3, 4, 5, 6, 7, z100]
2758 base = cls(*args)
2759 self.assertEqual(base, base.replace())
2760
2761 i = 0
2762 for name, newval in (("year", 2),
2763 ("month", 3),
2764 ("day", 4),
2765 ("hour", 5),
2766 ("minute", 6),
2767 ("second", 7),
2768 ("microsecond", 8),
2769 ("tzinfo", zm200)):
2770 newargs = args[:]
2771 newargs[i] = newval
2772 expected = cls(*newargs)
2773 got = base.replace(**{name: newval})
2774 self.assertEqual(expected, got)
2775 i += 1
2776
2777 # Ensure we can get rid of a tzinfo.
2778 self.assertEqual(base.tzname(), "+100")
2779 base2 = base.replace(tzinfo=None)
2780 self.failUnless(base2.tzinfo is None)
2781 self.failUnless(base2.tzname() is None)
2782
2783 # Ensure we can add one.
2784 base3 = base2.replace(tzinfo=z100)
2785 self.assertEqual(base, base3)
2786 self.failUnless(base.tzinfo is base3.tzinfo)
2787
2788 # Out of bounds.
2789 base = cls(2000, 2, 29)
2790 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002791
Tim Peters80475bb2002-12-25 07:40:55 +00002792 def test_more_astimezone(self):
2793 # The inherited test_astimezone covered some trivial and error cases.
2794 fnone = FixedOffset(None, "None")
2795 f44m = FixedOffset(44, "44")
2796 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2797
Tim Peters10cadce2003-01-23 19:58:02 +00002798 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002799 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002800 # Replacing with degenerate tzinfo raises an exception.
2801 self.assertRaises(ValueError, dt.astimezone, fnone)
2802 # Ditto with None tz.
2803 self.assertRaises(TypeError, dt.astimezone, None)
2804 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002805 x = dt.astimezone(dt.tzinfo)
2806 self.failUnless(x.tzinfo is f44m)
2807 self.assertEqual(x.date(), dt.date())
2808 self.assertEqual(x.time(), dt.time())
2809
2810 # Replacing with different tzinfo does adjust.
2811 got = dt.astimezone(fm5h)
2812 self.failUnless(got.tzinfo is fm5h)
2813 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2814 expected = dt - dt.utcoffset() # in effect, convert to UTC
2815 expected += fm5h.utcoffset(dt) # and from there to local time
2816 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2817 self.assertEqual(got.date(), expected.date())
2818 self.assertEqual(got.time(), expected.time())
2819 self.assertEqual(got.timetz(), expected.timetz())
2820 self.failUnless(got.tzinfo is expected.tzinfo)
2821 self.assertEqual(got, expected)
2822
Tim Peters4c0db782002-12-26 05:01:19 +00002823 def test_aware_subtract(self):
2824 cls = self.theclass
2825
Tim Peters60c76e42002-12-27 00:41:11 +00002826 # Ensure that utcoffset() is ignored when the operands have the
2827 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002828 class OperandDependentOffset(tzinfo):
2829 def utcoffset(self, t):
2830 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002831 # d0 and d1 equal after adjustment
2832 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002833 else:
Tim Peters397301e2003-01-02 21:28:08 +00002834 # d2 off in the weeds
2835 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002836
2837 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2838 d0 = base.replace(minute=3)
2839 d1 = base.replace(minute=9)
2840 d2 = base.replace(minute=11)
2841 for x in d0, d1, d2:
2842 for y in d0, d1, d2:
2843 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002844 expected = timedelta(minutes=x.minute - y.minute)
2845 self.assertEqual(got, expected)
2846
2847 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2848 # ignored.
2849 base = cls(8, 9, 10, 11, 12, 13, 14)
2850 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2851 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2852 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2853 for x in d0, d1, d2:
2854 for y in d0, d1, d2:
2855 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002856 if (x is d0 or x is d1) and (y is d0 or y is d1):
2857 expected = timedelta(0)
2858 elif x is y is d2:
2859 expected = timedelta(0)
2860 elif x is d2:
2861 expected = timedelta(minutes=(11-59)-0)
2862 else:
2863 assert y is d2
2864 expected = timedelta(minutes=0-(11-59))
2865 self.assertEqual(got, expected)
2866
Tim Peters60c76e42002-12-27 00:41:11 +00002867 def test_mixed_compare(self):
2868 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002869 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002870 self.assertEqual(t1, t2)
2871 t2 = t2.replace(tzinfo=None)
2872 self.assertEqual(t1, t2)
2873 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2874 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002875 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2876 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002877
Tim Peters0bf60bd2003-01-08 20:40:01 +00002878 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002879 class Varies(tzinfo):
2880 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002881 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002882 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002883 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002884 return self.offset
2885
2886 v = Varies()
2887 t1 = t2.replace(tzinfo=v)
2888 t2 = t2.replace(tzinfo=v)
2889 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2890 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2891 self.assertEqual(t1, t2)
2892
2893 # But if they're not identical, it isn't ignored.
2894 t2 = t2.replace(tzinfo=Varies())
2895 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002896
Tim Petersa98924a2003-05-17 05:55:19 +00002897 def test_subclass_datetimetz(self):
2898
2899 class C(self.theclass):
2900 theAnswer = 42
2901
2902 def __new__(cls, *args, **kws):
2903 temp = kws.copy()
2904 extra = temp.pop('extra')
2905 result = self.theclass.__new__(cls, *args, **temp)
2906 result.extra = extra
2907 return result
2908
2909 def newmeth(self, start):
2910 return start + self.hour + self.year
2911
2912 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2913
2914 dt1 = self.theclass(*args)
2915 dt2 = C(*args, **{'extra': 7})
2916
2917 self.assertEqual(dt2.__class__, C)
2918 self.assertEqual(dt2.theAnswer, 42)
2919 self.assertEqual(dt2.extra, 7)
2920 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2921 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2922
Tim Peters621818b2002-12-29 23:44:49 +00002923# Pain to set up DST-aware tzinfo classes.
2924
2925def first_sunday_on_or_after(dt):
2926 days_to_go = 6 - dt.weekday()
2927 if days_to_go:
2928 dt += timedelta(days_to_go)
2929 return dt
2930
2931ZERO = timedelta(0)
2932HOUR = timedelta(hours=1)
2933DAY = timedelta(days=1)
2934# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2935DSTSTART = datetime(1, 4, 1, 2)
2936# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002937# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2938# being standard time on that day, there is no spelling in local time of
2939# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2940DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002941
2942class USTimeZone(tzinfo):
2943
2944 def __init__(self, hours, reprname, stdname, dstname):
2945 self.stdoffset = timedelta(hours=hours)
2946 self.reprname = reprname
2947 self.stdname = stdname
2948 self.dstname = dstname
2949
2950 def __repr__(self):
2951 return self.reprname
2952
2953 def tzname(self, dt):
2954 if self.dst(dt):
2955 return self.dstname
2956 else:
2957 return self.stdname
2958
2959 def utcoffset(self, dt):
2960 return self.stdoffset + self.dst(dt)
2961
2962 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00002963 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00002964 # An exception instead may be sensible here, in one or more of
2965 # the cases.
2966 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00002967 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00002968
2969 # Find first Sunday in April.
2970 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
2971 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
2972
2973 # Find last Sunday in October.
2974 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
2975 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
2976
Tim Peters621818b2002-12-29 23:44:49 +00002977 # Can't compare naive to aware objects, so strip the timezone from
2978 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00002979 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00002980 return HOUR
2981 else:
2982 return ZERO
2983
Tim Peters521fc152002-12-31 17:36:56 +00002984Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
2985Central = USTimeZone(-6, "Central", "CST", "CDT")
2986Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
2987Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00002988utc_real = FixedOffset(0, "UTC", 0)
2989# For better test coverage, we want another flavor of UTC that's west of
2990# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00002991utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00002992
2993class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00002994 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002995 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00002996 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002997
Tim Peters0bf60bd2003-01-08 20:40:01 +00002998 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00002999
Tim Peters521fc152002-12-31 17:36:56 +00003000 # Check a time that's inside DST.
3001 def checkinside(self, dt, tz, utc, dston, dstoff):
3002 self.assertEqual(dt.dst(), HOUR)
3003
3004 # Conversion to our own timezone is always an identity.
3005 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003006
3007 asutc = dt.astimezone(utc)
3008 there_and_back = asutc.astimezone(tz)
3009
3010 # Conversion to UTC and back isn't always an identity here,
3011 # because there are redundant spellings (in local time) of
3012 # UTC time when DST begins: the clock jumps from 1:59:59
3013 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3014 # make sense then. The classes above treat 2:MM:SS as
3015 # daylight time then (it's "after 2am"), really an alias
3016 # for 1:MM:SS standard time. The latter form is what
3017 # conversion back from UTC produces.
3018 if dt.date() == dston.date() and dt.hour == 2:
3019 # We're in the redundant hour, and coming back from
3020 # UTC gives the 1:MM:SS standard-time spelling.
3021 self.assertEqual(there_and_back + HOUR, dt)
3022 # Although during was considered to be in daylight
3023 # time, there_and_back is not.
3024 self.assertEqual(there_and_back.dst(), ZERO)
3025 # They're the same times in UTC.
3026 self.assertEqual(there_and_back.astimezone(utc),
3027 dt.astimezone(utc))
3028 else:
3029 # We're not in the redundant hour.
3030 self.assertEqual(dt, there_and_back)
3031
Tim Peters327098a2003-01-20 22:54:38 +00003032 # Because we have a redundant spelling when DST begins, there is
3033 # (unforunately) an hour when DST ends that can't be spelled at all in
3034 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3035 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3036 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3037 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3038 # expressed in local time. Nevertheless, we want conversion back
3039 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003040 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003041 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003042 if dt.date() == dstoff.date() and dt.hour == 0:
3043 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003044 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003045 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3046 nexthour_utc += HOUR
3047 nexthour_tz = nexthour_utc.astimezone(tz)
3048 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003049 else:
Tim Peters327098a2003-01-20 22:54:38 +00003050 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003051
3052 # Check a time that's outside DST.
3053 def checkoutside(self, dt, tz, utc):
3054 self.assertEqual(dt.dst(), ZERO)
3055
3056 # Conversion to our own timezone is always an identity.
3057 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003058
3059 # Converting to UTC and back is an identity too.
3060 asutc = dt.astimezone(utc)
3061 there_and_back = asutc.astimezone(tz)
3062 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003063
Tim Peters1024bf82002-12-30 17:09:40 +00003064 def convert_between_tz_and_utc(self, tz, utc):
3065 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003066 # Because 1:MM on the day DST ends is taken as being standard time,
3067 # there is no spelling in tz for the last hour of daylight time.
3068 # For purposes of the test, the last hour of DST is 0:MM, which is
3069 # taken as being daylight time (and 1:MM is taken as being standard
3070 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003071 dstoff = self.dstoff.replace(tzinfo=tz)
3072 for delta in (timedelta(weeks=13),
3073 DAY,
3074 HOUR,
3075 timedelta(minutes=1),
3076 timedelta(microseconds=1)):
3077
Tim Peters521fc152002-12-31 17:36:56 +00003078 self.checkinside(dston, tz, utc, dston, dstoff)
3079 for during in dston + delta, dstoff - delta:
3080 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003081
Tim Peters521fc152002-12-31 17:36:56 +00003082 self.checkoutside(dstoff, tz, utc)
3083 for outside in dston - delta, dstoff + delta:
3084 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003085
Tim Peters621818b2002-12-29 23:44:49 +00003086 def test_easy(self):
3087 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003088 self.convert_between_tz_and_utc(Eastern, utc_real)
3089 self.convert_between_tz_and_utc(Pacific, utc_real)
3090 self.convert_between_tz_and_utc(Eastern, utc_fake)
3091 self.convert_between_tz_and_utc(Pacific, utc_fake)
3092 # The next is really dancing near the edge. It works because
3093 # Pacific and Eastern are far enough apart that their "problem
3094 # hours" don't overlap.
3095 self.convert_between_tz_and_utc(Eastern, Pacific)
3096 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003097 # OTOH, these fail! Don't enable them. The difficulty is that
3098 # the edge case tests assume that every hour is representable in
3099 # the "utc" class. This is always true for a fixed-offset tzinfo
3100 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3101 # For these adjacent DST-aware time zones, the range of time offsets
3102 # tested ends up creating hours in the one that aren't representable
3103 # in the other. For the same reason, we would see failures in the
3104 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3105 # offset deltas in convert_between_tz_and_utc().
3106 #
3107 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3108 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003109
Tim Petersf3615152003-01-01 21:51:37 +00003110 def test_tricky(self):
3111 # 22:00 on day before daylight starts.
3112 fourback = self.dston - timedelta(hours=4)
3113 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003114 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003115 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3116 # 2", we should get the 3 spelling.
3117 # If we plug 22:00 the day before into Eastern, it "looks like std
3118 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3119 # to 22:00 lands on 2:00, which makes no sense in local time (the
3120 # local clock jumps from 1 to 3). The point here is to make sure we
3121 # get the 3 spelling.
3122 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003123 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003124 self.assertEqual(expected, got)
3125
3126 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3127 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003128 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003129 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3130 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3131 # spelling.
3132 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003133 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003134 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003135
Tim Petersadf64202003-01-04 06:03:15 +00003136 # Now on the day DST ends, we want "repeat an hour" behavior.
3137 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3138 # EST 23:MM 0:MM 1:MM 2:MM
3139 # EDT 0:MM 1:MM 2:MM 3:MM
3140 # wall 0:MM 1:MM 1:MM 2:MM against these
3141 for utc in utc_real, utc_fake:
3142 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003143 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003144 # Convert that to UTC.
3145 first_std_hour -= tz.utcoffset(None)
3146 # Adjust for possibly fake UTC.
3147 asutc = first_std_hour + utc.utcoffset(None)
3148 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3149 # tz=Eastern.
3150 asutcbase = asutc.replace(tzinfo=utc)
3151 for tzhour in (0, 1, 1, 2):
3152 expectedbase = self.dstoff.replace(hour=tzhour)
3153 for minute in 0, 30, 59:
3154 expected = expectedbase.replace(minute=minute)
3155 asutc = asutcbase.replace(minute=minute)
3156 astz = asutc.astimezone(tz)
3157 self.assertEqual(astz.replace(tzinfo=None), expected)
3158 asutcbase += HOUR
3159
3160
Tim Peters710fb152003-01-02 19:35:54 +00003161 def test_bogus_dst(self):
3162 class ok(tzinfo):
3163 def utcoffset(self, dt): return HOUR
3164 def dst(self, dt): return HOUR
3165
3166 now = self.theclass.now().replace(tzinfo=utc_real)
3167 # Doesn't blow up.
3168 now.astimezone(ok())
3169
3170 # Does blow up.
3171 class notok(ok):
3172 def dst(self, dt): return None
3173 self.assertRaises(ValueError, now.astimezone, notok())
3174
Tim Peters52dcce22003-01-23 16:36:11 +00003175 def test_fromutc(self):
3176 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3177 now = datetime.utcnow().replace(tzinfo=utc_real)
3178 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3179 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3180 enow = Eastern.fromutc(now) # doesn't blow up
3181 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3182 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3183 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3184
3185 # Always converts UTC to standard time.
3186 class FauxUSTimeZone(USTimeZone):
3187 def fromutc(self, dt):
3188 return dt + self.stdoffset
3189 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3190
3191 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3192 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3193 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3194
3195 # Check around DST start.
3196 start = self.dston.replace(hour=4, tzinfo=Eastern)
3197 fstart = start.replace(tzinfo=FEastern)
3198 for wall in 23, 0, 1, 3, 4, 5:
3199 expected = start.replace(hour=wall)
3200 if wall == 23:
3201 expected -= timedelta(days=1)
3202 got = Eastern.fromutc(start)
3203 self.assertEqual(expected, got)
3204
3205 expected = fstart + FEastern.stdoffset
3206 got = FEastern.fromutc(fstart)
3207 self.assertEqual(expected, got)
3208
3209 # Ensure astimezone() calls fromutc() too.
3210 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3211 self.assertEqual(expected, got)
3212
3213 start += HOUR
3214 fstart += HOUR
3215
3216 # Check around DST end.
3217 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3218 fstart = start.replace(tzinfo=FEastern)
3219 for wall in 0, 1, 1, 2, 3, 4:
3220 expected = start.replace(hour=wall)
3221 got = Eastern.fromutc(start)
3222 self.assertEqual(expected, got)
3223
3224 expected = fstart + FEastern.stdoffset
3225 got = FEastern.fromutc(fstart)
3226 self.assertEqual(expected, got)
3227
3228 # Ensure astimezone() calls fromutc() too.
3229 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3230 self.assertEqual(expected, got)
3231
3232 start += HOUR
3233 fstart += HOUR
3234
Tim Peters710fb152003-01-02 19:35:54 +00003235
Tim Peters528ca532004-09-16 01:30:50 +00003236#############################################################################
3237# oddballs
3238
3239class Oddballs(unittest.TestCase):
3240
3241 def test_bug_1028306(self):
3242 # Trying to compare a date to a datetime should act like a mixed-
3243 # type comparison, despite that datetime is a subclass of date.
3244 as_date = date.today()
3245 as_datetime = datetime.combine(as_date, time())
3246 self.assert_(as_date != as_datetime)
3247 self.assert_(as_datetime != as_date)
3248 self.assert_(not as_date == as_datetime)
3249 self.assert_(not as_datetime == as_date)
3250 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3251 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3252 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3253 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3254 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3255 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3256 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3257 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3258
3259 # Neverthelss, comparison should work with the base-class (date)
3260 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003261 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003262 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003263 as_different = as_datetime.replace(day= different_day)
3264 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003265
3266 # And date should compare with other subclasses of date. If a
3267 # subclass wants to stop this, it's up to the subclass to do so.
3268 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3269 self.assertEqual(as_date, date_sc)
3270 self.assertEqual(date_sc, as_date)
3271
3272 # Ditto for datetimes.
3273 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3274 as_date.day, 0, 0, 0)
3275 self.assertEqual(as_datetime, datetime_sc)
3276 self.assertEqual(datetime_sc, as_datetime)
3277
Tim Peters2a799bf2002-12-16 20:18:38 +00003278def test_main():
Guido van Rossumd8faa362007-04-27 19:54:29 +00003279 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003280
3281if __name__ == "__main__":
3282 test_main()