blob: b827658579fcae906c43bc6bec34d829f3dac95c [file] [log] [blame]
Tim Peters0bf60bd2003-01-08 20:40:01 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
Tim Peters2a799bf2002-12-16 20:18:38 +00005
Guido van Rossumd8faa362007-04-27 19:54:29 +00006import os
Tim Peters2a799bf2002-12-16 20:18:38 +00007import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00008import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00009import unittest
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000010try:
11 import cPickle
12except ImportError:
13 cPickle = None
Tim Peters2a799bf2002-12-16 20:18:38 +000014
15from test import test_support
16
17from datetime import MINYEAR, MAXYEAR
18from datetime import timedelta
19from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000020from datetime import time
21from datetime import date, datetime
22
Tim Peters35ad6412003-02-05 04:08:07 +000023pickle_choices = [(pickler, unpickler, proto)
Nick Coghlan650f0d02007-04-15 12:05:43 +000024 for pickler in (pickle, cPickle)
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000025 if pickler is not None
Nick Coghlan650f0d02007-04-15 12:05:43 +000026 for unpickler in (pickle, cPickle)
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000027 if unpickler is not None
Tim Peters35ad6412003-02-05 04:08:07 +000028 for proto in range(3)]
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000029if cPickle is None:
30 assert len(pickle_choices) == 3
31else:
32 assert len(pickle_choices) == 2*2*3
Guido van Rossum177e41a2003-01-30 22:06:23 +000033
Tim Peters68124bb2003-02-08 03:46:31 +000034# An arbitrary collection of objects of non-datetime types, for testing
35# mixed-type comparisons.
Guido van Rossume2a383d2007-01-15 16:59:06 +000036OTHERSTUFF = (10, 10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000037
Tim Peters2a799bf2002-12-16 20:18:38 +000038
39#############################################################################
40# module tests
41
42class TestModule(unittest.TestCase):
43
44 def test_constants(self):
45 import datetime
46 self.assertEqual(datetime.MINYEAR, 1)
47 self.assertEqual(datetime.MAXYEAR, 9999)
48
49#############################################################################
50# tzinfo tests
51
52class FixedOffset(tzinfo):
53 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000054 if isinstance(offset, int):
55 offset = timedelta(minutes=offset)
56 if isinstance(dstoffset, int):
57 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000058 self.__offset = offset
59 self.__name = name
60 self.__dstoffset = dstoffset
61 def __repr__(self):
62 return self.__name.lower()
63 def utcoffset(self, dt):
64 return self.__offset
65 def tzname(self, dt):
66 return self.__name
67 def dst(self, dt):
68 return self.__dstoffset
69
Tim Petersfb8472c2002-12-21 05:04:42 +000070class PicklableFixedOffset(FixedOffset):
71 def __init__(self, offset=None, name=None, dstoffset=None):
72 FixedOffset.__init__(self, offset, name, dstoffset)
73
Tim Peters2a799bf2002-12-16 20:18:38 +000074class TestTZInfo(unittest.TestCase):
75
76 def test_non_abstractness(self):
77 # In order to allow subclasses to get pickled, the C implementation
78 # wasn't able to get away with having __init__ raise
79 # NotImplementedError.
80 useless = tzinfo()
81 dt = datetime.max
82 self.assertRaises(NotImplementedError, useless.tzname, dt)
83 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
84 self.assertRaises(NotImplementedError, useless.dst, dt)
85
86 def test_subclass_must_override(self):
87 class NotEnough(tzinfo):
88 def __init__(self, offset, name):
89 self.__offset = offset
90 self.__name = name
91 self.failUnless(issubclass(NotEnough, tzinfo))
92 ne = NotEnough(3, "NotByALongShot")
93 self.failUnless(isinstance(ne, tzinfo))
94
95 dt = datetime.now()
96 self.assertRaises(NotImplementedError, ne.tzname, dt)
97 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
98 self.assertRaises(NotImplementedError, ne.dst, dt)
99
100 def test_normal(self):
101 fo = FixedOffset(3, "Three")
102 self.failUnless(isinstance(fo, tzinfo))
103 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000104 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000105 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000106 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000107
108 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000109 # There's no point to pickling tzinfo objects on their own (they
110 # carry no data), but they need to be picklable anyway else
111 # concrete subclasses can't be pickled.
112 orig = tzinfo.__new__(tzinfo)
113 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000114 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000115 green = pickler.dumps(orig, proto)
116 derived = unpickler.loads(green)
117 self.failUnless(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000118
119 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000120 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000121 offset = timedelta(minutes=-300)
122 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000123 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000124 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000125 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000126 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000127 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000128 green = pickler.dumps(orig, proto)
129 derived = unpickler.loads(green)
130 self.failUnless(isinstance(derived, tzinfo))
131 self.failUnless(type(derived) is PicklableFixedOffset)
132 self.assertEqual(derived.utcoffset(None), offset)
133 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000134
135#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000136# Base clase for testing a particular aspect of timedelta, time, date and
137# datetime comparisons.
138
Guido van Rossumd8faa362007-04-27 19:54:29 +0000139class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000140 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
141
142 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
143 # legit constructor.
144
145 def test_harmless_mixed_comparison(self):
146 me = self.theclass(1, 1, 1)
147
148 self.failIf(me == ())
149 self.failUnless(me != ())
150 self.failIf(() == me)
151 self.failUnless(() != me)
152
Guido van Rossume2a383d2007-01-15 16:59:06 +0000153 self.failUnless(me in [1, 20, [], me])
154 self.failIf(me not in [1, 20, [], me])
Tim Peters07534a62003-02-07 22:50:28 +0000155
Guido van Rossume2a383d2007-01-15 16:59:06 +0000156 self.failUnless([] in [me, 1, 20, []])
157 self.failIf([] not in [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000158
159 def test_harmful_mixed_comparison(self):
160 me = self.theclass(1, 1, 1)
161
162 self.assertRaises(TypeError, lambda: me < ())
163 self.assertRaises(TypeError, lambda: me <= ())
164 self.assertRaises(TypeError, lambda: me > ())
165 self.assertRaises(TypeError, lambda: me >= ())
166
167 self.assertRaises(TypeError, lambda: () < me)
168 self.assertRaises(TypeError, lambda: () <= me)
169 self.assertRaises(TypeError, lambda: () > me)
170 self.assertRaises(TypeError, lambda: () >= me)
171
172 self.assertRaises(TypeError, cmp, (), me)
173 self.assertRaises(TypeError, cmp, me, ())
174
175#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000176# timedelta tests
177
Guido van Rossumd8faa362007-04-27 19:54:29 +0000178class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000179
180 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000181
182 def test_constructor(self):
183 eq = self.assertEqual
184 td = timedelta
185
186 # Check keyword args to constructor
187 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
188 milliseconds=0, microseconds=0))
189 eq(td(1), td(days=1))
190 eq(td(0, 1), td(seconds=1))
191 eq(td(0, 0, 1), td(microseconds=1))
192 eq(td(weeks=1), td(days=7))
193 eq(td(days=1), td(hours=24))
194 eq(td(hours=1), td(minutes=60))
195 eq(td(minutes=1), td(seconds=60))
196 eq(td(seconds=1), td(milliseconds=1000))
197 eq(td(milliseconds=1), td(microseconds=1000))
198
199 # Check float args to constructor
200 eq(td(weeks=1.0/7), td(days=1))
201 eq(td(days=1.0/24), td(hours=1))
202 eq(td(hours=1.0/60), td(minutes=1))
203 eq(td(minutes=1.0/60), td(seconds=1))
204 eq(td(seconds=0.001), td(milliseconds=1))
205 eq(td(milliseconds=0.001), td(microseconds=1))
206
207 def test_computations(self):
208 eq = self.assertEqual
209 td = timedelta
210
211 a = td(7) # One week
212 b = td(0, 60) # One minute
213 c = td(0, 0, 1000) # One millisecond
214 eq(a+b+c, td(7, 60, 1000))
215 eq(a-b, td(6, 24*3600 - 60))
216 eq(-a, td(-7))
217 eq(+a, td(7))
218 eq(-b, td(-1, 24*3600 - 60))
219 eq(-c, td(-1, 24*3600 - 1, 999000))
220 eq(abs(a), a)
221 eq(abs(-a), a)
222 eq(td(6, 24*3600), a)
223 eq(td(0, 0, 60*1000000), b)
224 eq(a*10, td(70))
225 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000226 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000227 eq(b*10, td(0, 600))
228 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000229 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000230 eq(c*10, td(0, 0, 10000))
231 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000232 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000233 eq(a*-1, -a)
234 eq(b*-2, -b-b)
235 eq(c*-2, -c+-c)
236 eq(b*(60*24), (b*60)*24)
237 eq(b*(60*24), (60*b)*24)
238 eq(c*1000, td(0, 1))
239 eq(1000*c, td(0, 1))
240 eq(a//7, td(1))
241 eq(b//10, td(0, 6))
242 eq(c//1000, td(0, 0, 1))
243 eq(a//10, td(0, 7*24*360))
244 eq(a//3600000, td(0, 0, 7*24*1000))
245
246 def test_disallowed_computations(self):
247 a = timedelta(42)
248
249 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000250 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000251 self.assertRaises(TypeError, lambda: a+i)
252 self.assertRaises(TypeError, lambda: a-i)
253 self.assertRaises(TypeError, lambda: i+a)
254 self.assertRaises(TypeError, lambda: i-a)
255
256 # Mul/div by float isn't supported.
257 x = 2.3
258 self.assertRaises(TypeError, lambda: a*x)
259 self.assertRaises(TypeError, lambda: x*a)
260 self.assertRaises(TypeError, lambda: a/x)
261 self.assertRaises(TypeError, lambda: x/a)
262 self.assertRaises(TypeError, lambda: a // x)
263 self.assertRaises(TypeError, lambda: x // a)
264
265 # Divison of int by timedelta doesn't make sense.
266 # Division by zero doesn't make sense.
Guido van Rossume2a383d2007-01-15 16:59:06 +0000267 for zero in 0, 0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000268 self.assertRaises(TypeError, lambda: zero // a)
269 self.assertRaises(ZeroDivisionError, lambda: a // zero)
270
271 def test_basic_attributes(self):
272 days, seconds, us = 1, 7, 31
273 td = timedelta(days, seconds, us)
274 self.assertEqual(td.days, days)
275 self.assertEqual(td.seconds, seconds)
276 self.assertEqual(td.microseconds, us)
277
278 def test_carries(self):
279 t1 = timedelta(days=100,
280 weeks=-7,
281 hours=-24*(100-49),
282 minutes=-3,
283 seconds=12,
284 microseconds=(3*60 - 12) * 1e6 + 1)
285 t2 = timedelta(microseconds=1)
286 self.assertEqual(t1, t2)
287
288 def test_hash_equality(self):
289 t1 = timedelta(days=100,
290 weeks=-7,
291 hours=-24*(100-49),
292 minutes=-3,
293 seconds=12,
294 microseconds=(3*60 - 12) * 1000000)
295 t2 = timedelta()
296 self.assertEqual(hash(t1), hash(t2))
297
298 t1 += timedelta(weeks=7)
299 t2 += timedelta(days=7*7)
300 self.assertEqual(t1, t2)
301 self.assertEqual(hash(t1), hash(t2))
302
303 d = {t1: 1}
304 d[t2] = 2
305 self.assertEqual(len(d), 1)
306 self.assertEqual(d[t1], 2)
307
308 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000309 args = 12, 34, 56
310 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000311 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000312 green = pickler.dumps(orig, proto)
313 derived = unpickler.loads(green)
314 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000315
316 def test_compare(self):
317 t1 = timedelta(2, 3, 4)
318 t2 = timedelta(2, 3, 4)
319 self.failUnless(t1 == t2)
320 self.failUnless(t1 <= t2)
321 self.failUnless(t1 >= t2)
322 self.failUnless(not t1 != t2)
323 self.failUnless(not t1 < t2)
324 self.failUnless(not t1 > t2)
325 self.assertEqual(cmp(t1, t2), 0)
326 self.assertEqual(cmp(t2, t1), 0)
327
328 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
329 t2 = timedelta(*args) # this is larger than t1
330 self.failUnless(t1 < t2)
331 self.failUnless(t2 > t1)
332 self.failUnless(t1 <= t2)
333 self.failUnless(t2 >= t1)
334 self.failUnless(t1 != t2)
335 self.failUnless(t2 != t1)
336 self.failUnless(not t1 == t2)
337 self.failUnless(not t2 == t1)
338 self.failUnless(not t1 > t2)
339 self.failUnless(not t2 < t1)
340 self.failUnless(not t1 >= t2)
341 self.failUnless(not t2 <= t1)
342 self.assertEqual(cmp(t1, t2), -1)
343 self.assertEqual(cmp(t2, t1), 1)
344
Tim Peters68124bb2003-02-08 03:46:31 +0000345 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000346 self.assertEqual(t1 == badarg, False)
347 self.assertEqual(t1 != badarg, True)
348 self.assertEqual(badarg == t1, False)
349 self.assertEqual(badarg != t1, True)
350
Tim Peters2a799bf2002-12-16 20:18:38 +0000351 self.assertRaises(TypeError, lambda: t1 <= badarg)
352 self.assertRaises(TypeError, lambda: t1 < badarg)
353 self.assertRaises(TypeError, lambda: t1 > badarg)
354 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000355 self.assertRaises(TypeError, lambda: badarg <= t1)
356 self.assertRaises(TypeError, lambda: badarg < t1)
357 self.assertRaises(TypeError, lambda: badarg > t1)
358 self.assertRaises(TypeError, lambda: badarg >= t1)
359
360 def test_str(self):
361 td = timedelta
362 eq = self.assertEqual
363
364 eq(str(td(1)), "1 day, 0:00:00")
365 eq(str(td(-1)), "-1 day, 0:00:00")
366 eq(str(td(2)), "2 days, 0:00:00")
367 eq(str(td(-2)), "-2 days, 0:00:00")
368
369 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
370 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
371 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
372 "-210 days, 23:12:34")
373
374 eq(str(td(milliseconds=1)), "0:00:00.001000")
375 eq(str(td(microseconds=3)), "0:00:00.000003")
376
377 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
378 microseconds=999999)),
379 "999999999 days, 23:59:59.999999")
380
381 def test_roundtrip(self):
382 for td in (timedelta(days=999999999, hours=23, minutes=59,
383 seconds=59, microseconds=999999),
384 timedelta(days=-999999999),
385 timedelta(days=1, seconds=2, microseconds=3)):
386
387 # Verify td -> string -> td identity.
388 s = repr(td)
389 self.failUnless(s.startswith('datetime.'))
390 s = s[9:]
391 td2 = eval(s)
392 self.assertEqual(td, td2)
393
394 # Verify identity via reconstructing from pieces.
395 td2 = timedelta(td.days, td.seconds, td.microseconds)
396 self.assertEqual(td, td2)
397
398 def test_resolution_info(self):
399 self.assert_(isinstance(timedelta.min, timedelta))
400 self.assert_(isinstance(timedelta.max, timedelta))
401 self.assert_(isinstance(timedelta.resolution, timedelta))
402 self.assert_(timedelta.max > timedelta.min)
403 self.assertEqual(timedelta.min, timedelta(-999999999))
404 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
405 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
406
407 def test_overflow(self):
408 tiny = timedelta.resolution
409
410 td = timedelta.min + tiny
411 td -= tiny # no problem
412 self.assertRaises(OverflowError, td.__sub__, tiny)
413 self.assertRaises(OverflowError, td.__add__, -tiny)
414
415 td = timedelta.max - tiny
416 td += tiny # no problem
417 self.assertRaises(OverflowError, td.__add__, tiny)
418 self.assertRaises(OverflowError, td.__sub__, -tiny)
419
420 self.assertRaises(OverflowError, lambda: -timedelta.max)
421
422 def test_microsecond_rounding(self):
423 td = timedelta
424 eq = self.assertEqual
425
426 # Single-field rounding.
427 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
428 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
429 eq(td(milliseconds=0.6/1000), td(microseconds=1))
430 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
431
432 # Rounding due to contributions from more than one field.
433 us_per_hour = 3600e6
434 us_per_day = us_per_hour * 24
435 eq(td(days=.4/us_per_day), td(0))
436 eq(td(hours=.2/us_per_hour), td(0))
437 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
438
439 eq(td(days=-.4/us_per_day), td(0))
440 eq(td(hours=-.2/us_per_hour), td(0))
441 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
442
443 def test_massive_normalization(self):
444 td = timedelta(microseconds=-1)
445 self.assertEqual((td.days, td.seconds, td.microseconds),
446 (-1, 24*3600-1, 999999))
447
448 def test_bool(self):
449 self.failUnless(timedelta(1))
450 self.failUnless(timedelta(0, 1))
451 self.failUnless(timedelta(0, 0, 1))
452 self.failUnless(timedelta(microseconds=1))
453 self.failUnless(not timedelta(0))
454
Tim Petersb0c854d2003-05-17 15:57:00 +0000455 def test_subclass_timedelta(self):
456
457 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000458 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000459 def from_td(td):
460 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000461
462 def as_hours(self):
463 sum = (self.days * 24 +
464 self.seconds / 3600.0 +
465 self.microseconds / 3600e6)
466 return round(sum)
467
468 t1 = T(days=1)
469 self.assert_(type(t1) is T)
470 self.assertEqual(t1.as_hours(), 24)
471
472 t2 = T(days=-1, seconds=-3600)
473 self.assert_(type(t2) is T)
474 self.assertEqual(t2.as_hours(), -25)
475
476 t3 = t1 + t2
477 self.assert_(type(t3) is timedelta)
478 t4 = T.from_td(t3)
479 self.assert_(type(t4) is T)
480 self.assertEqual(t3.days, t4.days)
481 self.assertEqual(t3.seconds, t4.seconds)
482 self.assertEqual(t3.microseconds, t4.microseconds)
483 self.assertEqual(str(t3), str(t4))
484 self.assertEqual(t4.as_hours(), -1)
485
Tim Peters2a799bf2002-12-16 20:18:38 +0000486#############################################################################
487# date tests
488
489class TestDateOnly(unittest.TestCase):
490 # Tests here won't pass if also run on datetime objects, so don't
491 # subclass this to test datetimes too.
492
493 def test_delta_non_days_ignored(self):
494 dt = date(2000, 1, 2)
495 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
496 microseconds=5)
497 days = timedelta(delta.days)
498 self.assertEqual(days, timedelta(1))
499
500 dt2 = dt + delta
501 self.assertEqual(dt2, dt + days)
502
503 dt2 = delta + dt
504 self.assertEqual(dt2, dt + days)
505
506 dt2 = dt - delta
507 self.assertEqual(dt2, dt - days)
508
509 delta = -delta
510 days = timedelta(delta.days)
511 self.assertEqual(days, timedelta(-2))
512
513 dt2 = dt + delta
514 self.assertEqual(dt2, dt + days)
515
516 dt2 = delta + dt
517 self.assertEqual(dt2, dt + days)
518
519 dt2 = dt - delta
520 self.assertEqual(dt2, dt - days)
521
Tim Peters604c0132004-06-07 23:04:33 +0000522class SubclassDate(date):
523 sub_var = 1
524
Guido van Rossumd8faa362007-04-27 19:54:29 +0000525class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000526 # Tests here should pass for both dates and datetimes, except for a
527 # few tests that TestDateTime overrides.
528
529 theclass = date
530
531 def test_basic_attributes(self):
532 dt = self.theclass(2002, 3, 1)
533 self.assertEqual(dt.year, 2002)
534 self.assertEqual(dt.month, 3)
535 self.assertEqual(dt.day, 1)
536
537 def test_roundtrip(self):
538 for dt in (self.theclass(1, 2, 3),
539 self.theclass.today()):
540 # Verify dt -> string -> date identity.
541 s = repr(dt)
542 self.failUnless(s.startswith('datetime.'))
543 s = s[9:]
544 dt2 = eval(s)
545 self.assertEqual(dt, dt2)
546
547 # Verify identity via reconstructing from pieces.
548 dt2 = self.theclass(dt.year, dt.month, dt.day)
549 self.assertEqual(dt, dt2)
550
551 def test_ordinal_conversions(self):
552 # Check some fixed values.
553 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
554 (1, 12, 31, 365),
555 (2, 1, 1, 366),
556 # first example from "Calendrical Calculations"
557 (1945, 11, 12, 710347)]:
558 d = self.theclass(y, m, d)
559 self.assertEqual(n, d.toordinal())
560 fromord = self.theclass.fromordinal(n)
561 self.assertEqual(d, fromord)
562 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000563 # if we're checking something fancier than a date, verify
564 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000565 self.assertEqual(fromord.hour, 0)
566 self.assertEqual(fromord.minute, 0)
567 self.assertEqual(fromord.second, 0)
568 self.assertEqual(fromord.microsecond, 0)
569
Tim Peters0bf60bd2003-01-08 20:40:01 +0000570 # Check first and last days of year spottily across the whole
571 # range of years supported.
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 """
833 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
834 iso_long_years.sort()
835 L = []
836 for i in range(400):
837 d = self.theclass(2000+i, 12, 31)
838 d1 = self.theclass(1600+i, 12, 31)
839 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
840 if d.isocalendar()[1] == 53:
841 L.append(i)
842 self.assertEqual(L, iso_long_years)
843
844 def test_isoformat(self):
845 t = self.theclass(2, 3, 2)
846 self.assertEqual(t.isoformat(), "0002-03-02")
847
848 def test_ctime(self):
849 t = self.theclass(2002, 3, 2)
850 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
851
852 def test_strftime(self):
853 t = self.theclass(2005, 3, 2)
854 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000855 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000856 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000857
858 self.assertRaises(TypeError, t.strftime) # needs an arg
859 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
860 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
861
862 # A naive object replaces %z and %Z w/ empty strings.
863 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
864
865 def test_resolution_info(self):
866 self.assert_(isinstance(self.theclass.min, self.theclass))
867 self.assert_(isinstance(self.theclass.max, self.theclass))
868 self.assert_(isinstance(self.theclass.resolution, timedelta))
869 self.assert_(self.theclass.max > self.theclass.min)
870
871 def test_extreme_timedelta(self):
872 big = self.theclass.max - self.theclass.min
873 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
874 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
875 # n == 315537897599999999 ~= 2**58.13
876 justasbig = timedelta(0, 0, n)
877 self.assertEqual(big, justasbig)
878 self.assertEqual(self.theclass.min + big, self.theclass.max)
879 self.assertEqual(self.theclass.max - big, self.theclass.min)
880
881 def test_timetuple(self):
882 for i in range(7):
883 # January 2, 1956 is a Monday (0)
884 d = self.theclass(1956, 1, 2+i)
885 t = d.timetuple()
886 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
887 # February 1, 1956 is a Wednesday (2)
888 d = self.theclass(1956, 2, 1+i)
889 t = d.timetuple()
890 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
891 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
892 # of the year.
893 d = self.theclass(1956, 3, 1+i)
894 t = d.timetuple()
895 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
896 self.assertEqual(t.tm_year, 1956)
897 self.assertEqual(t.tm_mon, 3)
898 self.assertEqual(t.tm_mday, 1+i)
899 self.assertEqual(t.tm_hour, 0)
900 self.assertEqual(t.tm_min, 0)
901 self.assertEqual(t.tm_sec, 0)
902 self.assertEqual(t.tm_wday, (3+i)%7)
903 self.assertEqual(t.tm_yday, 61+i)
904 self.assertEqual(t.tm_isdst, -1)
905
906 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000907 args = 6, 7, 23
908 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000909 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000910 green = pickler.dumps(orig, proto)
911 derived = unpickler.loads(green)
912 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000913
914 def test_compare(self):
915 t1 = self.theclass(2, 3, 4)
916 t2 = self.theclass(2, 3, 4)
917 self.failUnless(t1 == t2)
918 self.failUnless(t1 <= t2)
919 self.failUnless(t1 >= t2)
920 self.failUnless(not t1 != t2)
921 self.failUnless(not t1 < t2)
922 self.failUnless(not t1 > t2)
923 self.assertEqual(cmp(t1, t2), 0)
924 self.assertEqual(cmp(t2, t1), 0)
925
926 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
927 t2 = self.theclass(*args) # this is larger than t1
928 self.failUnless(t1 < t2)
929 self.failUnless(t2 > t1)
930 self.failUnless(t1 <= t2)
931 self.failUnless(t2 >= t1)
932 self.failUnless(t1 != t2)
933 self.failUnless(t2 != t1)
934 self.failUnless(not t1 == t2)
935 self.failUnless(not t2 == t1)
936 self.failUnless(not t1 > t2)
937 self.failUnless(not t2 < t1)
938 self.failUnless(not t1 >= t2)
939 self.failUnless(not t2 <= t1)
940 self.assertEqual(cmp(t1, t2), -1)
941 self.assertEqual(cmp(t2, t1), 1)
942
Tim Peters68124bb2003-02-08 03:46:31 +0000943 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000944 self.assertEqual(t1 == badarg, False)
945 self.assertEqual(t1 != badarg, True)
946 self.assertEqual(badarg == t1, False)
947 self.assertEqual(badarg != t1, True)
948
Tim Peters2a799bf2002-12-16 20:18:38 +0000949 self.assertRaises(TypeError, lambda: t1 < badarg)
950 self.assertRaises(TypeError, lambda: t1 > badarg)
951 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000952 self.assertRaises(TypeError, lambda: badarg <= t1)
953 self.assertRaises(TypeError, lambda: badarg < t1)
954 self.assertRaises(TypeError, lambda: badarg > t1)
955 self.assertRaises(TypeError, lambda: badarg >= t1)
956
Tim Peters8d81a012003-01-24 22:36:34 +0000957 def test_mixed_compare(self):
958 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000959
960 # Our class can be compared for equality to other classes
961 self.assertEqual(our == 1, False)
962 self.assertEqual(1 == our, False)
963 self.assertEqual(our != 1, True)
964 self.assertEqual(1 != our, True)
965
966 # But the ordering is undefined
967 self.assertRaises(TypeError, lambda: our < 1)
968 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000969 self.assertRaises(TypeError, cmp, our, 1)
970 self.assertRaises(TypeError, cmp, 1, our)
971
Guido van Rossum19960592006-08-24 17:29:38 +0000972 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +0000973
Guido van Rossum19960592006-08-24 17:29:38 +0000974 class SomeClass:
975 pass
976
977 their = SomeClass()
978 self.assertEqual(our == their, False)
979 self.assertEqual(their == our, False)
980 self.assertEqual(our != their, True)
981 self.assertEqual(their != our, True)
982 self.assertRaises(TypeError, lambda: our < their)
983 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000984 self.assertRaises(TypeError, cmp, our, their)
Guido van Rossum19960592006-08-24 17:29:38 +0000985 self.assertRaises(TypeError, cmp, their, our)
Tim Peters8d81a012003-01-24 22:36:34 +0000986
Guido van Rossum19960592006-08-24 17:29:38 +0000987 # However, if the other class explicitly defines ordering
988 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +0000989
Guido van Rossum19960592006-08-24 17:29:38 +0000990 class LargerThanAnything:
991 def __lt__(self, other):
992 return False
993 def __le__(self, other):
994 return isinstance(other, LargerThanAnything)
995 def __eq__(self, other):
996 return isinstance(other, LargerThanAnything)
997 def __ne__(self, other):
998 return not isinstance(other, LargerThanAnything)
999 def __gt__(self, other):
1000 return not isinstance(other, LargerThanAnything)
1001 def __ge__(self, other):
1002 return True
1003
1004 their = LargerThanAnything()
1005 self.assertEqual(our == their, False)
1006 self.assertEqual(their == our, False)
1007 self.assertEqual(our != their, True)
1008 self.assertEqual(their != our, True)
1009 self.assertEqual(our < their, True)
1010 self.assertEqual(their < our, False)
1011 self.assertEqual(cmp(our, their), -1)
1012 self.assertEqual(cmp(their, our), 1)
Tim Peters8d81a012003-01-24 22:36:34 +00001013
Tim Peters2a799bf2002-12-16 20:18:38 +00001014 def test_bool(self):
1015 # All dates are considered true.
1016 self.failUnless(self.theclass.min)
1017 self.failUnless(self.theclass.max)
1018
Tim Petersd6844152002-12-22 20:58:42 +00001019 def test_srftime_out_of_range(self):
1020 # For nasty technical reasons, we can't handle years before 1900.
1021 cls = self.theclass
1022 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1023 for y in 1, 49, 51, 99, 100, 1000, 1899:
1024 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001025
1026 def test_replace(self):
1027 cls = self.theclass
1028 args = [1, 2, 3]
1029 base = cls(*args)
1030 self.assertEqual(base, base.replace())
1031
1032 i = 0
1033 for name, newval in (("year", 2),
1034 ("month", 3),
1035 ("day", 4)):
1036 newargs = args[:]
1037 newargs[i] = newval
1038 expected = cls(*newargs)
1039 got = base.replace(**{name: newval})
1040 self.assertEqual(expected, got)
1041 i += 1
1042
1043 # Out of bounds.
1044 base = cls(2000, 2, 29)
1045 self.assertRaises(ValueError, base.replace, year=2001)
1046
Tim Petersa98924a2003-05-17 05:55:19 +00001047 def test_subclass_date(self):
1048
1049 class C(self.theclass):
1050 theAnswer = 42
1051
1052 def __new__(cls, *args, **kws):
1053 temp = kws.copy()
1054 extra = temp.pop('extra')
1055 result = self.theclass.__new__(cls, *args, **temp)
1056 result.extra = extra
1057 return result
1058
1059 def newmeth(self, start):
1060 return start + self.year + self.month
1061
1062 args = 2003, 4, 14
1063
1064 dt1 = self.theclass(*args)
1065 dt2 = C(*args, **{'extra': 7})
1066
1067 self.assertEqual(dt2.__class__, C)
1068 self.assertEqual(dt2.theAnswer, 42)
1069 self.assertEqual(dt2.extra, 7)
1070 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1071 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1072
Tim Peters604c0132004-06-07 23:04:33 +00001073 def test_pickling_subclass_date(self):
1074
1075 args = 6, 7, 23
1076 orig = SubclassDate(*args)
1077 for pickler, unpickler, proto in pickle_choices:
1078 green = pickler.dumps(orig, proto)
1079 derived = unpickler.loads(green)
1080 self.assertEqual(orig, derived)
1081
Tim Peters3f606292004-03-21 23:38:41 +00001082 def test_backdoor_resistance(self):
1083 # For fast unpickling, the constructor accepts a pickle string.
1084 # This is a low-overhead backdoor. A user can (by intent or
1085 # mistake) pass a string directly, which (if it's the right length)
1086 # will get treated like a pickle, and bypass the normal sanity
1087 # checks in the constructor. This can create insane objects.
1088 # The constructor doesn't want to burn the time to validate all
1089 # fields, but does check the month field. This stops, e.g.,
1090 # datetime.datetime('1995-03-25') from yielding an insane object.
1091 base = '1995-03-25'
1092 if not issubclass(self.theclass, datetime):
1093 base = base[:4]
1094 for month_byte in '9', chr(0), chr(13), '\xff':
1095 self.assertRaises(TypeError, self.theclass,
1096 base[:2] + month_byte + base[3:])
1097 for ord_byte in range(1, 13):
1098 # This shouldn't blow up because of the month byte alone. If
1099 # the implementation changes to do more-careful checking, it may
1100 # blow up because other fields are insane.
Guido van Rossume3d1d412007-05-23 21:24:35 +00001101 # XXX Maybe this will have to become bytes?
1102 self.theclass(str8(base[:2] + chr(ord_byte) + base[3:]))
Tim Peterseb1a4962003-05-17 02:25:20 +00001103
Tim Peters2a799bf2002-12-16 20:18:38 +00001104#############################################################################
1105# datetime tests
1106
Tim Peters604c0132004-06-07 23:04:33 +00001107class SubclassDatetime(datetime):
1108 sub_var = 1
1109
Tim Peters2a799bf2002-12-16 20:18:38 +00001110class TestDateTime(TestDate):
1111
1112 theclass = datetime
1113
1114 def test_basic_attributes(self):
1115 dt = self.theclass(2002, 3, 1, 12, 0)
1116 self.assertEqual(dt.year, 2002)
1117 self.assertEqual(dt.month, 3)
1118 self.assertEqual(dt.day, 1)
1119 self.assertEqual(dt.hour, 12)
1120 self.assertEqual(dt.minute, 0)
1121 self.assertEqual(dt.second, 0)
1122 self.assertEqual(dt.microsecond, 0)
1123
1124 def test_basic_attributes_nonzero(self):
1125 # Make sure all attributes are non-zero so bugs in
1126 # bit-shifting access show up.
1127 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1128 self.assertEqual(dt.year, 2002)
1129 self.assertEqual(dt.month, 3)
1130 self.assertEqual(dt.day, 1)
1131 self.assertEqual(dt.hour, 12)
1132 self.assertEqual(dt.minute, 59)
1133 self.assertEqual(dt.second, 59)
1134 self.assertEqual(dt.microsecond, 8000)
1135
1136 def test_roundtrip(self):
1137 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1138 self.theclass.now()):
1139 # Verify dt -> string -> datetime identity.
1140 s = repr(dt)
1141 self.failUnless(s.startswith('datetime.'))
1142 s = s[9:]
1143 dt2 = eval(s)
1144 self.assertEqual(dt, dt2)
1145
1146 # Verify identity via reconstructing from pieces.
1147 dt2 = self.theclass(dt.year, dt.month, dt.day,
1148 dt.hour, dt.minute, dt.second,
1149 dt.microsecond)
1150 self.assertEqual(dt, dt2)
1151
1152 def test_isoformat(self):
1153 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1154 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1155 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1156 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1157 # str is ISO format with the separator forced to a blank.
1158 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1159
1160 t = self.theclass(2, 3, 2)
1161 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1162 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1163 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1164 # str is ISO format with the separator forced to a blank.
1165 self.assertEqual(str(t), "0002-03-02 00:00:00")
1166
1167 def test_more_ctime(self):
1168 # Test fields that TestDate doesn't touch.
1169 import time
1170
1171 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1172 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1173 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1174 # out. The difference is that t.ctime() produces " 2" for the day,
1175 # but platform ctime() produces "02" for the day. According to
1176 # C99, t.ctime() is correct here.
1177 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1178
1179 # So test a case where that difference doesn't matter.
1180 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1181 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1182
1183 def test_tz_independent_comparing(self):
1184 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1185 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1186 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1187 self.assertEqual(dt1, dt3)
1188 self.assert_(dt2 > dt3)
1189
1190 # Make sure comparison doesn't forget microseconds, and isn't done
1191 # via comparing a float timestamp (an IEEE double doesn't have enough
1192 # precision to span microsecond resolution across years 1 thru 9999,
1193 # so comparing via timestamp necessarily calls some distinct values
1194 # equal).
1195 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1196 us = timedelta(microseconds=1)
1197 dt2 = dt1 + us
1198 self.assertEqual(dt2 - dt1, us)
1199 self.assert_(dt1 < dt2)
1200
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001201 def test_strftime_with_bad_tzname_replace(self):
1202 # verify ok if tzinfo.tzname().replace() returns a non-string
1203 class MyTzInfo(FixedOffset):
1204 def tzname(self, dt):
1205 class MyStr(str):
1206 def replace(self, *args):
1207 return None
1208 return MyStr('name')
1209 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1210 self.assertRaises(TypeError, t.strftime, '%Z')
1211
Tim Peters2a799bf2002-12-16 20:18:38 +00001212 def test_bad_constructor_arguments(self):
1213 # bad years
1214 self.theclass(MINYEAR, 1, 1) # no exception
1215 self.theclass(MAXYEAR, 1, 1) # no exception
1216 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1217 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1218 # bad months
1219 self.theclass(2000, 1, 1) # no exception
1220 self.theclass(2000, 12, 1) # no exception
1221 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1222 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1223 # bad days
1224 self.theclass(2000, 2, 29) # no exception
1225 self.theclass(2004, 2, 29) # no exception
1226 self.theclass(2400, 2, 29) # no exception
1227 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1228 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1229 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1230 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1231 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1232 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1233 # bad hours
1234 self.theclass(2000, 1, 31, 0) # no exception
1235 self.theclass(2000, 1, 31, 23) # no exception
1236 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1237 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1238 # bad minutes
1239 self.theclass(2000, 1, 31, 23, 0) # no exception
1240 self.theclass(2000, 1, 31, 23, 59) # no exception
1241 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1242 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1243 # bad seconds
1244 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1245 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1246 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1247 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1248 # bad microseconds
1249 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1250 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1251 self.assertRaises(ValueError, self.theclass,
1252 2000, 1, 31, 23, 59, 59, -1)
1253 self.assertRaises(ValueError, self.theclass,
1254 2000, 1, 31, 23, 59, 59,
1255 1000000)
1256
1257 def test_hash_equality(self):
1258 d = self.theclass(2000, 12, 31, 23, 30, 17)
1259 e = self.theclass(2000, 12, 31, 23, 30, 17)
1260 self.assertEqual(d, e)
1261 self.assertEqual(hash(d), hash(e))
1262
1263 dic = {d: 1}
1264 dic[e] = 2
1265 self.assertEqual(len(dic), 1)
1266 self.assertEqual(dic[d], 2)
1267 self.assertEqual(dic[e], 2)
1268
1269 d = self.theclass(2001, 1, 1, 0, 5, 17)
1270 e = self.theclass(2001, 1, 1, 0, 5, 17)
1271 self.assertEqual(d, e)
1272 self.assertEqual(hash(d), hash(e))
1273
1274 dic = {d: 1}
1275 dic[e] = 2
1276 self.assertEqual(len(dic), 1)
1277 self.assertEqual(dic[d], 2)
1278 self.assertEqual(dic[e], 2)
1279
1280 def test_computations(self):
1281 a = self.theclass(2002, 1, 31)
1282 b = self.theclass(1956, 1, 31)
1283 diff = a-b
1284 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1285 self.assertEqual(diff.seconds, 0)
1286 self.assertEqual(diff.microseconds, 0)
1287 a = self.theclass(2002, 3, 2, 17, 6)
1288 millisec = timedelta(0, 0, 1000)
1289 hour = timedelta(0, 3600)
1290 day = timedelta(1)
1291 week = timedelta(7)
1292 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1293 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1294 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1295 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1296 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1297 self.assertEqual(a - hour, a + -hour)
1298 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1299 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1300 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1301 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1302 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1303 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1304 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1305 self.assertEqual((a + week) - a, week)
1306 self.assertEqual((a + day) - a, day)
1307 self.assertEqual((a + hour) - a, hour)
1308 self.assertEqual((a + millisec) - a, millisec)
1309 self.assertEqual((a - week) - a, -week)
1310 self.assertEqual((a - day) - a, -day)
1311 self.assertEqual((a - hour) - a, -hour)
1312 self.assertEqual((a - millisec) - a, -millisec)
1313 self.assertEqual(a - (a + week), -week)
1314 self.assertEqual(a - (a + day), -day)
1315 self.assertEqual(a - (a + hour), -hour)
1316 self.assertEqual(a - (a + millisec), -millisec)
1317 self.assertEqual(a - (a - week), week)
1318 self.assertEqual(a - (a - day), day)
1319 self.assertEqual(a - (a - hour), hour)
1320 self.assertEqual(a - (a - millisec), millisec)
1321 self.assertEqual(a + (week + day + hour + millisec),
1322 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1323 self.assertEqual(a + (week + day + hour + millisec),
1324 (((a + week) + day) + hour) + millisec)
1325 self.assertEqual(a - (week + day + hour + millisec),
1326 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1327 self.assertEqual(a - (week + day + hour + millisec),
1328 (((a - week) - day) - hour) - millisec)
1329 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001330 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001331 self.assertRaises(TypeError, lambda: a+i)
1332 self.assertRaises(TypeError, lambda: a-i)
1333 self.assertRaises(TypeError, lambda: i+a)
1334 self.assertRaises(TypeError, lambda: i-a)
1335
1336 # delta - datetime is senseless.
1337 self.assertRaises(TypeError, lambda: day - a)
1338 # mixing datetime and (delta or datetime) via * or // is senseless
1339 self.assertRaises(TypeError, lambda: day * a)
1340 self.assertRaises(TypeError, lambda: a * day)
1341 self.assertRaises(TypeError, lambda: day // a)
1342 self.assertRaises(TypeError, lambda: a // day)
1343 self.assertRaises(TypeError, lambda: a * a)
1344 self.assertRaises(TypeError, lambda: a // a)
1345 # datetime + datetime is senseless
1346 self.assertRaises(TypeError, lambda: a + a)
1347
1348 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001349 args = 6, 7, 23, 20, 59, 1, 64**2
1350 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001351 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001352 green = pickler.dumps(orig, proto)
1353 derived = unpickler.loads(green)
1354 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001355
Guido van Rossum275666f2003-02-07 21:49:01 +00001356 def test_more_pickling(self):
1357 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1358 s = pickle.dumps(a)
1359 b = pickle.loads(s)
1360 self.assertEqual(b.year, 2003)
1361 self.assertEqual(b.month, 2)
1362 self.assertEqual(b.day, 7)
1363
Tim Peters604c0132004-06-07 23:04:33 +00001364 def test_pickling_subclass_datetime(self):
1365 args = 6, 7, 23, 20, 59, 1, 64**2
1366 orig = SubclassDatetime(*args)
1367 for pickler, unpickler, proto in pickle_choices:
1368 green = pickler.dumps(orig, proto)
1369 derived = unpickler.loads(green)
1370 self.assertEqual(orig, derived)
1371
Tim Peters2a799bf2002-12-16 20:18:38 +00001372 def test_more_compare(self):
1373 # The test_compare() inherited from TestDate covers the error cases.
1374 # We just want to test lexicographic ordering on the members datetime
1375 # has that date lacks.
1376 args = [2000, 11, 29, 20, 58, 16, 999998]
1377 t1 = self.theclass(*args)
1378 t2 = self.theclass(*args)
1379 self.failUnless(t1 == t2)
1380 self.failUnless(t1 <= t2)
1381 self.failUnless(t1 >= t2)
1382 self.failUnless(not t1 != t2)
1383 self.failUnless(not t1 < t2)
1384 self.failUnless(not t1 > t2)
1385 self.assertEqual(cmp(t1, t2), 0)
1386 self.assertEqual(cmp(t2, t1), 0)
1387
1388 for i in range(len(args)):
1389 newargs = args[:]
1390 newargs[i] = args[i] + 1
1391 t2 = self.theclass(*newargs) # this is larger than t1
1392 self.failUnless(t1 < t2)
1393 self.failUnless(t2 > t1)
1394 self.failUnless(t1 <= t2)
1395 self.failUnless(t2 >= t1)
1396 self.failUnless(t1 != t2)
1397 self.failUnless(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.failUnless(not t1 >= t2)
1403 self.failUnless(not t2 <= t1)
1404 self.assertEqual(cmp(t1, t2), -1)
1405 self.assertEqual(cmp(t2, t1), 1)
1406
1407
1408 # A helper for timestamp constructor tests.
1409 def verify_field_equality(self, expected, got):
1410 self.assertEqual(expected.tm_year, got.year)
1411 self.assertEqual(expected.tm_mon, got.month)
1412 self.assertEqual(expected.tm_mday, got.day)
1413 self.assertEqual(expected.tm_hour, got.hour)
1414 self.assertEqual(expected.tm_min, got.minute)
1415 self.assertEqual(expected.tm_sec, got.second)
1416
1417 def test_fromtimestamp(self):
1418 import time
1419
1420 ts = time.time()
1421 expected = time.localtime(ts)
1422 got = self.theclass.fromtimestamp(ts)
1423 self.verify_field_equality(expected, got)
1424
1425 def test_utcfromtimestamp(self):
1426 import time
1427
1428 ts = time.time()
1429 expected = time.gmtime(ts)
1430 got = self.theclass.utcfromtimestamp(ts)
1431 self.verify_field_equality(expected, got)
1432
Thomas Wouters477c8d52006-05-27 19:21:47 +00001433 def test_microsecond_rounding(self):
1434 # Test whether fromtimestamp "rounds up" floats that are less
1435 # than one microsecond smaller than an integer.
1436 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1437 self.theclass.fromtimestamp(1))
1438
Tim Peters1b6f7a92004-06-20 02:50:16 +00001439 def test_insane_fromtimestamp(self):
1440 # It's possible that some platform maps time_t to double,
1441 # and that this test will fail there. This test should
1442 # exempt such platforms (provided they return reasonable
1443 # results!).
1444 for insane in -1e200, 1e200:
1445 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1446 insane)
1447
1448 def test_insane_utcfromtimestamp(self):
1449 # It's possible that some platform maps time_t to double,
1450 # and that this test will fail there. This test should
1451 # exempt such platforms (provided they return reasonable
1452 # results!).
1453 for insane in -1e200, 1e200:
1454 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1455 insane)
1456
Guido van Rossumd8faa362007-04-27 19:54:29 +00001457 def test_negative_float_fromtimestamp(self):
1458 # Windows doesn't accept negative timestamps
1459 if os.name == "nt":
1460 return
1461 # The result is tz-dependent; at least test that this doesn't
1462 # fail (like it did before bug 1646728 was fixed).
1463 self.theclass.fromtimestamp(-1.05)
1464
1465 def test_negative_float_utcfromtimestamp(self):
1466 # Windows doesn't accept negative timestamps
1467 if os.name == "nt":
1468 return
1469 d = self.theclass.utcfromtimestamp(-1.05)
1470 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1471
Tim Peters2a799bf2002-12-16 20:18:38 +00001472 def test_utcnow(self):
1473 import time
1474
1475 # Call it a success if utcnow() and utcfromtimestamp() are within
1476 # a second of each other.
1477 tolerance = timedelta(seconds=1)
1478 for dummy in range(3):
1479 from_now = self.theclass.utcnow()
1480 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1481 if abs(from_timestamp - from_now) <= tolerance:
1482 break
1483 # Else try again a few times.
1484 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1485
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001486 def test_strptime(self):
1487 import time
1488
1489 string = '2004-12-01 13:02:47'
1490 format = '%Y-%m-%d %H:%M:%S'
1491 expected = self.theclass(*(time.strptime(string, format)[0:6]))
1492 got = self.theclass.strptime(string, format)
1493 self.assertEqual(expected, got)
1494
Tim Peters2a799bf2002-12-16 20:18:38 +00001495 def test_more_timetuple(self):
1496 # This tests fields beyond those tested by the TestDate.test_timetuple.
1497 t = self.theclass(2004, 12, 31, 6, 22, 33)
1498 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1499 self.assertEqual(t.timetuple(),
1500 (t.year, t.month, t.day,
1501 t.hour, t.minute, t.second,
1502 t.weekday(),
1503 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1504 -1))
1505 tt = t.timetuple()
1506 self.assertEqual(tt.tm_year, t.year)
1507 self.assertEqual(tt.tm_mon, t.month)
1508 self.assertEqual(tt.tm_mday, t.day)
1509 self.assertEqual(tt.tm_hour, t.hour)
1510 self.assertEqual(tt.tm_min, t.minute)
1511 self.assertEqual(tt.tm_sec, t.second)
1512 self.assertEqual(tt.tm_wday, t.weekday())
1513 self.assertEqual(tt.tm_yday, t.toordinal() -
1514 date(t.year, 1, 1).toordinal() + 1)
1515 self.assertEqual(tt.tm_isdst, -1)
1516
1517 def test_more_strftime(self):
1518 # This tests fields beyond those tested by the TestDate.test_strftime.
1519 t = self.theclass(2004, 12, 31, 6, 22, 33)
1520 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1521 "12 31 04 33 22 06 366")
1522
1523 def test_extract(self):
1524 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1525 self.assertEqual(dt.date(), date(2002, 3, 4))
1526 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1527
1528 def test_combine(self):
1529 d = date(2002, 3, 4)
1530 t = time(18, 45, 3, 1234)
1531 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1532 combine = self.theclass.combine
1533 dt = combine(d, t)
1534 self.assertEqual(dt, expected)
1535
1536 dt = combine(time=t, date=d)
1537 self.assertEqual(dt, expected)
1538
1539 self.assertEqual(d, dt.date())
1540 self.assertEqual(t, dt.time())
1541 self.assertEqual(dt, combine(dt.date(), dt.time()))
1542
1543 self.assertRaises(TypeError, combine) # need an arg
1544 self.assertRaises(TypeError, combine, d) # need two args
1545 self.assertRaises(TypeError, combine, t, d) # args reversed
1546 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1547 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1548
Tim Peters12bf3392002-12-24 05:41:27 +00001549 def test_replace(self):
1550 cls = self.theclass
1551 args = [1, 2, 3, 4, 5, 6, 7]
1552 base = cls(*args)
1553 self.assertEqual(base, base.replace())
1554
1555 i = 0
1556 for name, newval in (("year", 2),
1557 ("month", 3),
1558 ("day", 4),
1559 ("hour", 5),
1560 ("minute", 6),
1561 ("second", 7),
1562 ("microsecond", 8)):
1563 newargs = args[:]
1564 newargs[i] = newval
1565 expected = cls(*newargs)
1566 got = base.replace(**{name: newval})
1567 self.assertEqual(expected, got)
1568 i += 1
1569
1570 # Out of bounds.
1571 base = cls(2000, 2, 29)
1572 self.assertRaises(ValueError, base.replace, year=2001)
1573
Tim Peters80475bb2002-12-25 07:40:55 +00001574 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001575 # Pretty boring! The TZ test is more interesting here. astimezone()
1576 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001577 dt = self.theclass.now()
1578 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001579 self.assertRaises(TypeError, dt.astimezone) # not enough args
1580 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1581 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001582 self.assertRaises(ValueError, dt.astimezone, f) # naive
1583 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001584
Tim Peters52dcce22003-01-23 16:36:11 +00001585 class Bogus(tzinfo):
1586 def utcoffset(self, dt): return None
1587 def dst(self, dt): return timedelta(0)
1588 bog = Bogus()
1589 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1590
1591 class AlsoBogus(tzinfo):
1592 def utcoffset(self, dt): return timedelta(0)
1593 def dst(self, dt): return None
1594 alsobog = AlsoBogus()
1595 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001596
Tim Petersa98924a2003-05-17 05:55:19 +00001597 def test_subclass_datetime(self):
1598
1599 class C(self.theclass):
1600 theAnswer = 42
1601
1602 def __new__(cls, *args, **kws):
1603 temp = kws.copy()
1604 extra = temp.pop('extra')
1605 result = self.theclass.__new__(cls, *args, **temp)
1606 result.extra = extra
1607 return result
1608
1609 def newmeth(self, start):
1610 return start + self.year + self.month + self.second
1611
1612 args = 2003, 4, 14, 12, 13, 41
1613
1614 dt1 = self.theclass(*args)
1615 dt2 = C(*args, **{'extra': 7})
1616
1617 self.assertEqual(dt2.__class__, C)
1618 self.assertEqual(dt2.theAnswer, 42)
1619 self.assertEqual(dt2.extra, 7)
1620 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1621 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1622 dt1.second - 7)
1623
Tim Peters604c0132004-06-07 23:04:33 +00001624class SubclassTime(time):
1625 sub_var = 1
1626
Guido van Rossumd8faa362007-04-27 19:54:29 +00001627class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001628
1629 theclass = time
1630
1631 def test_basic_attributes(self):
1632 t = self.theclass(12, 0)
1633 self.assertEqual(t.hour, 12)
1634 self.assertEqual(t.minute, 0)
1635 self.assertEqual(t.second, 0)
1636 self.assertEqual(t.microsecond, 0)
1637
1638 def test_basic_attributes_nonzero(self):
1639 # Make sure all attributes are non-zero so bugs in
1640 # bit-shifting access show up.
1641 t = self.theclass(12, 59, 59, 8000)
1642 self.assertEqual(t.hour, 12)
1643 self.assertEqual(t.minute, 59)
1644 self.assertEqual(t.second, 59)
1645 self.assertEqual(t.microsecond, 8000)
1646
1647 def test_roundtrip(self):
1648 t = self.theclass(1, 2, 3, 4)
1649
1650 # Verify t -> string -> time identity.
1651 s = repr(t)
1652 self.failUnless(s.startswith('datetime.'))
1653 s = s[9:]
1654 t2 = eval(s)
1655 self.assertEqual(t, t2)
1656
1657 # Verify identity via reconstructing from pieces.
1658 t2 = self.theclass(t.hour, t.minute, t.second,
1659 t.microsecond)
1660 self.assertEqual(t, t2)
1661
1662 def test_comparing(self):
1663 args = [1, 2, 3, 4]
1664 t1 = self.theclass(*args)
1665 t2 = self.theclass(*args)
1666 self.failUnless(t1 == t2)
1667 self.failUnless(t1 <= t2)
1668 self.failUnless(t1 >= t2)
1669 self.failUnless(not t1 != t2)
1670 self.failUnless(not t1 < t2)
1671 self.failUnless(not t1 > t2)
1672 self.assertEqual(cmp(t1, t2), 0)
1673 self.assertEqual(cmp(t2, t1), 0)
1674
1675 for i in range(len(args)):
1676 newargs = args[:]
1677 newargs[i] = args[i] + 1
1678 t2 = self.theclass(*newargs) # this is larger than t1
1679 self.failUnless(t1 < t2)
1680 self.failUnless(t2 > t1)
1681 self.failUnless(t1 <= t2)
1682 self.failUnless(t2 >= t1)
1683 self.failUnless(t1 != t2)
1684 self.failUnless(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.failUnless(not t1 >= t2)
1690 self.failUnless(not t2 <= t1)
1691 self.assertEqual(cmp(t1, t2), -1)
1692 self.assertEqual(cmp(t2, t1), 1)
1693
Tim Peters68124bb2003-02-08 03:46:31 +00001694 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001695 self.assertEqual(t1 == badarg, False)
1696 self.assertEqual(t1 != badarg, True)
1697 self.assertEqual(badarg == t1, False)
1698 self.assertEqual(badarg != t1, True)
1699
Tim Peters2a799bf2002-12-16 20:18:38 +00001700 self.assertRaises(TypeError, lambda: t1 <= badarg)
1701 self.assertRaises(TypeError, lambda: t1 < badarg)
1702 self.assertRaises(TypeError, lambda: t1 > badarg)
1703 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001704 self.assertRaises(TypeError, lambda: badarg <= t1)
1705 self.assertRaises(TypeError, lambda: badarg < t1)
1706 self.assertRaises(TypeError, lambda: badarg > t1)
1707 self.assertRaises(TypeError, lambda: badarg >= t1)
1708
1709 def test_bad_constructor_arguments(self):
1710 # bad hours
1711 self.theclass(0, 0) # no exception
1712 self.theclass(23, 0) # no exception
1713 self.assertRaises(ValueError, self.theclass, -1, 0)
1714 self.assertRaises(ValueError, self.theclass, 24, 0)
1715 # bad minutes
1716 self.theclass(23, 0) # no exception
1717 self.theclass(23, 59) # no exception
1718 self.assertRaises(ValueError, self.theclass, 23, -1)
1719 self.assertRaises(ValueError, self.theclass, 23, 60)
1720 # bad seconds
1721 self.theclass(23, 59, 0) # no exception
1722 self.theclass(23, 59, 59) # no exception
1723 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1724 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1725 # bad microseconds
1726 self.theclass(23, 59, 59, 0) # no exception
1727 self.theclass(23, 59, 59, 999999) # no exception
1728 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1729 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1730
1731 def test_hash_equality(self):
1732 d = self.theclass(23, 30, 17)
1733 e = self.theclass(23, 30, 17)
1734 self.assertEqual(d, e)
1735 self.assertEqual(hash(d), hash(e))
1736
1737 dic = {d: 1}
1738 dic[e] = 2
1739 self.assertEqual(len(dic), 1)
1740 self.assertEqual(dic[d], 2)
1741 self.assertEqual(dic[e], 2)
1742
1743 d = self.theclass(0, 5, 17)
1744 e = self.theclass(0, 5, 17)
1745 self.assertEqual(d, e)
1746 self.assertEqual(hash(d), hash(e))
1747
1748 dic = {d: 1}
1749 dic[e] = 2
1750 self.assertEqual(len(dic), 1)
1751 self.assertEqual(dic[d], 2)
1752 self.assertEqual(dic[e], 2)
1753
1754 def test_isoformat(self):
1755 t = self.theclass(4, 5, 1, 123)
1756 self.assertEqual(t.isoformat(), "04:05:01.000123")
1757 self.assertEqual(t.isoformat(), str(t))
1758
1759 t = self.theclass()
1760 self.assertEqual(t.isoformat(), "00:00:00")
1761 self.assertEqual(t.isoformat(), str(t))
1762
1763 t = self.theclass(microsecond=1)
1764 self.assertEqual(t.isoformat(), "00:00:00.000001")
1765 self.assertEqual(t.isoformat(), str(t))
1766
1767 t = self.theclass(microsecond=10)
1768 self.assertEqual(t.isoformat(), "00:00:00.000010")
1769 self.assertEqual(t.isoformat(), str(t))
1770
1771 t = self.theclass(microsecond=100)
1772 self.assertEqual(t.isoformat(), "00:00:00.000100")
1773 self.assertEqual(t.isoformat(), str(t))
1774
1775 t = self.theclass(microsecond=1000)
1776 self.assertEqual(t.isoformat(), "00:00:00.001000")
1777 self.assertEqual(t.isoformat(), str(t))
1778
1779 t = self.theclass(microsecond=10000)
1780 self.assertEqual(t.isoformat(), "00:00:00.010000")
1781 self.assertEqual(t.isoformat(), str(t))
1782
1783 t = self.theclass(microsecond=100000)
1784 self.assertEqual(t.isoformat(), "00:00:00.100000")
1785 self.assertEqual(t.isoformat(), str(t))
1786
Thomas Wouterscf297e42007-02-23 15:07:44 +00001787 def test_1653736(self):
1788 # verify it doesn't accept extra keyword arguments
1789 t = self.theclass(second=1)
1790 self.assertRaises(TypeError, t.isoformat, foo=3)
1791
Tim Peters2a799bf2002-12-16 20:18:38 +00001792 def test_strftime(self):
1793 t = self.theclass(1, 2, 3, 4)
1794 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1795 # A naive object replaces %z and %Z with empty strings.
1796 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1797
1798 def test_str(self):
1799 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1800 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1801 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1802 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1803 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1804
1805 def test_repr(self):
1806 name = 'datetime.' + self.theclass.__name__
1807 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1808 "%s(1, 2, 3, 4)" % name)
1809 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1810 "%s(10, 2, 3, 4000)" % name)
1811 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1812 "%s(0, 2, 3, 400000)" % name)
1813 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1814 "%s(12, 2, 3)" % name)
1815 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1816 "%s(23, 15)" % name)
1817
1818 def test_resolution_info(self):
1819 self.assert_(isinstance(self.theclass.min, self.theclass))
1820 self.assert_(isinstance(self.theclass.max, self.theclass))
1821 self.assert_(isinstance(self.theclass.resolution, timedelta))
1822 self.assert_(self.theclass.max > self.theclass.min)
1823
1824 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001825 args = 20, 59, 16, 64**2
1826 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001827 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001828 green = pickler.dumps(orig, proto)
1829 derived = unpickler.loads(green)
1830 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001831
Tim Peters604c0132004-06-07 23:04:33 +00001832 def test_pickling_subclass_time(self):
1833 args = 20, 59, 16, 64**2
1834 orig = SubclassTime(*args)
1835 for pickler, unpickler, proto in pickle_choices:
1836 green = pickler.dumps(orig, proto)
1837 derived = unpickler.loads(green)
1838 self.assertEqual(orig, derived)
1839
Tim Peters2a799bf2002-12-16 20:18:38 +00001840 def test_bool(self):
1841 cls = self.theclass
1842 self.failUnless(cls(1))
1843 self.failUnless(cls(0, 1))
1844 self.failUnless(cls(0, 0, 1))
1845 self.failUnless(cls(0, 0, 0, 1))
1846 self.failUnless(not cls(0))
1847 self.failUnless(not cls())
1848
Tim Peters12bf3392002-12-24 05:41:27 +00001849 def test_replace(self):
1850 cls = self.theclass
1851 args = [1, 2, 3, 4]
1852 base = cls(*args)
1853 self.assertEqual(base, base.replace())
1854
1855 i = 0
1856 for name, newval in (("hour", 5),
1857 ("minute", 6),
1858 ("second", 7),
1859 ("microsecond", 8)):
1860 newargs = args[:]
1861 newargs[i] = newval
1862 expected = cls(*newargs)
1863 got = base.replace(**{name: newval})
1864 self.assertEqual(expected, got)
1865 i += 1
1866
1867 # Out of bounds.
1868 base = cls(1)
1869 self.assertRaises(ValueError, base.replace, hour=24)
1870 self.assertRaises(ValueError, base.replace, minute=-1)
1871 self.assertRaises(ValueError, base.replace, second=100)
1872 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1873
Tim Petersa98924a2003-05-17 05:55:19 +00001874 def test_subclass_time(self):
1875
1876 class C(self.theclass):
1877 theAnswer = 42
1878
1879 def __new__(cls, *args, **kws):
1880 temp = kws.copy()
1881 extra = temp.pop('extra')
1882 result = self.theclass.__new__(cls, *args, **temp)
1883 result.extra = extra
1884 return result
1885
1886 def newmeth(self, start):
1887 return start + self.hour + self.second
1888
1889 args = 4, 5, 6
1890
1891 dt1 = self.theclass(*args)
1892 dt2 = C(*args, **{'extra': 7})
1893
1894 self.assertEqual(dt2.__class__, C)
1895 self.assertEqual(dt2.theAnswer, 42)
1896 self.assertEqual(dt2.extra, 7)
1897 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1898 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1899
Armin Rigof4afb212005-11-07 07:15:48 +00001900 def test_backdoor_resistance(self):
1901 # see TestDate.test_backdoor_resistance().
1902 base = '2:59.0'
1903 for hour_byte in ' ', '9', chr(24), '\xff':
1904 self.assertRaises(TypeError, self.theclass,
1905 hour_byte + base[1:])
1906
Tim Peters855fe882002-12-22 03:43:39 +00001907# A mixin for classes with a tzinfo= argument. Subclasses must define
1908# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001909# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001910class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001911
Tim Petersbad8ff02002-12-30 20:52:32 +00001912 def test_argument_passing(self):
1913 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001914 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001915 class introspective(tzinfo):
1916 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001917 def utcoffset(self, dt):
1918 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001919 dst = utcoffset
1920
1921 obj = cls(1, 2, 3, tzinfo=introspective())
1922
Tim Peters0bf60bd2003-01-08 20:40:01 +00001923 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001924 self.assertEqual(obj.tzname(), expected)
1925
Tim Peters0bf60bd2003-01-08 20:40:01 +00001926 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001927 self.assertEqual(obj.utcoffset(), expected)
1928 self.assertEqual(obj.dst(), expected)
1929
Tim Peters855fe882002-12-22 03:43:39 +00001930 def test_bad_tzinfo_classes(self):
1931 cls = self.theclass
1932 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001933
Tim Peters855fe882002-12-22 03:43:39 +00001934 class NiceTry(object):
1935 def __init__(self): pass
1936 def utcoffset(self, dt): pass
1937 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1938
1939 class BetterTry(tzinfo):
1940 def __init__(self): pass
1941 def utcoffset(self, dt): pass
1942 b = BetterTry()
1943 t = cls(1, 1, 1, tzinfo=b)
1944 self.failUnless(t.tzinfo is b)
1945
1946 def test_utc_offset_out_of_bounds(self):
1947 class Edgy(tzinfo):
1948 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00001949 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00001950 def utcoffset(self, dt):
1951 return self.offset
1952
1953 cls = self.theclass
1954 for offset, legit in ((-1440, False),
1955 (-1439, True),
1956 (1439, True),
1957 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001958 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00001959 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001960 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00001961 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001962 else:
1963 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00001964 if legit:
1965 aofs = abs(offset)
1966 h, m = divmod(aofs, 60)
1967 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001968 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00001969 t = t.timetz()
1970 self.assertEqual(str(t), "01:02:03" + tag)
1971 else:
1972 self.assertRaises(ValueError, str, t)
1973
1974 def test_tzinfo_classes(self):
1975 cls = self.theclass
1976 class C1(tzinfo):
1977 def utcoffset(self, dt): return None
1978 def dst(self, dt): return None
1979 def tzname(self, dt): return None
1980 for t in (cls(1, 1, 1),
1981 cls(1, 1, 1, tzinfo=None),
1982 cls(1, 1, 1, tzinfo=C1())):
1983 self.failUnless(t.utcoffset() is None)
1984 self.failUnless(t.dst() is None)
1985 self.failUnless(t.tzname() is None)
1986
Tim Peters855fe882002-12-22 03:43:39 +00001987 class C3(tzinfo):
1988 def utcoffset(self, dt): return timedelta(minutes=-1439)
1989 def dst(self, dt): return timedelta(minutes=1439)
1990 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001991 t = cls(1, 1, 1, tzinfo=C3())
1992 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
1993 self.assertEqual(t.dst(), timedelta(minutes=1439))
1994 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00001995
1996 # Wrong types.
1997 class C4(tzinfo):
1998 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001999 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002000 def tzname(self, dt): return 0
2001 t = cls(1, 1, 1, tzinfo=C4())
2002 self.assertRaises(TypeError, t.utcoffset)
2003 self.assertRaises(TypeError, t.dst)
2004 self.assertRaises(TypeError, t.tzname)
2005
2006 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002007 class C6(tzinfo):
2008 def utcoffset(self, dt): return timedelta(hours=-24)
2009 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002010 t = cls(1, 1, 1, tzinfo=C6())
2011 self.assertRaises(ValueError, t.utcoffset)
2012 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002013
2014 # Not a whole number of minutes.
2015 class C7(tzinfo):
2016 def utcoffset(self, dt): return timedelta(seconds=61)
2017 def dst(self, dt): return timedelta(microseconds=-81)
2018 t = cls(1, 1, 1, tzinfo=C7())
2019 self.assertRaises(ValueError, t.utcoffset)
2020 self.assertRaises(ValueError, t.dst)
2021
Tim Peters4c0db782002-12-26 05:01:19 +00002022 def test_aware_compare(self):
2023 cls = self.theclass
2024
Tim Peters60c76e42002-12-27 00:41:11 +00002025 # Ensure that utcoffset() gets ignored if the comparands have
2026 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002027 class OperandDependentOffset(tzinfo):
2028 def utcoffset(self, t):
2029 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002030 # d0 and d1 equal after adjustment
2031 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002032 else:
Tim Peters397301e2003-01-02 21:28:08 +00002033 # d2 off in the weeds
2034 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002035
2036 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2037 d0 = base.replace(minute=3)
2038 d1 = base.replace(minute=9)
2039 d2 = base.replace(minute=11)
2040 for x in d0, d1, d2:
2041 for y in d0, d1, d2:
2042 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002043 expected = cmp(x.minute, y.minute)
2044 self.assertEqual(got, expected)
2045
2046 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002047 # Note that a time can't actually have an operand-depedent offset,
2048 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2049 # so skip this test for time.
2050 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002051 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2052 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2053 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2054 for x in d0, d1, d2:
2055 for y in d0, d1, d2:
2056 got = cmp(x, y)
2057 if (x is d0 or x is d1) and (y is d0 or y is d1):
2058 expected = 0
2059 elif x is y is d2:
2060 expected = 0
2061 elif x is d2:
2062 expected = -1
2063 else:
2064 assert y is d2
2065 expected = 1
2066 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002067
Tim Peters855fe882002-12-22 03:43:39 +00002068
Tim Peters0bf60bd2003-01-08 20:40:01 +00002069# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002070class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002071 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002072
2073 def test_empty(self):
2074 t = self.theclass()
2075 self.assertEqual(t.hour, 0)
2076 self.assertEqual(t.minute, 0)
2077 self.assertEqual(t.second, 0)
2078 self.assertEqual(t.microsecond, 0)
2079 self.failUnless(t.tzinfo is None)
2080
Tim Peters2a799bf2002-12-16 20:18:38 +00002081 def test_zones(self):
2082 est = FixedOffset(-300, "EST", 1)
2083 utc = FixedOffset(0, "UTC", -2)
2084 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002085 t1 = time( 7, 47, tzinfo=est)
2086 t2 = time(12, 47, tzinfo=utc)
2087 t3 = time(13, 47, tzinfo=met)
2088 t4 = time(microsecond=40)
2089 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002090
2091 self.assertEqual(t1.tzinfo, est)
2092 self.assertEqual(t2.tzinfo, utc)
2093 self.assertEqual(t3.tzinfo, met)
2094 self.failUnless(t4.tzinfo is None)
2095 self.assertEqual(t5.tzinfo, utc)
2096
Tim Peters855fe882002-12-22 03:43:39 +00002097 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2098 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2099 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002100 self.failUnless(t4.utcoffset() is None)
2101 self.assertRaises(TypeError, t1.utcoffset, "no args")
2102
2103 self.assertEqual(t1.tzname(), "EST")
2104 self.assertEqual(t2.tzname(), "UTC")
2105 self.assertEqual(t3.tzname(), "MET")
2106 self.failUnless(t4.tzname() is None)
2107 self.assertRaises(TypeError, t1.tzname, "no args")
2108
Tim Peters855fe882002-12-22 03:43:39 +00002109 self.assertEqual(t1.dst(), timedelta(minutes=1))
2110 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2111 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002112 self.failUnless(t4.dst() is None)
2113 self.assertRaises(TypeError, t1.dst, "no args")
2114
2115 self.assertEqual(hash(t1), hash(t2))
2116 self.assertEqual(hash(t1), hash(t3))
2117 self.assertEqual(hash(t2), hash(t3))
2118
2119 self.assertEqual(t1, t2)
2120 self.assertEqual(t1, t3)
2121 self.assertEqual(t2, t3)
2122 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2123 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2124 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2125
2126 self.assertEqual(str(t1), "07:47:00-05:00")
2127 self.assertEqual(str(t2), "12:47:00+00:00")
2128 self.assertEqual(str(t3), "13:47:00+01:00")
2129 self.assertEqual(str(t4), "00:00:00.000040")
2130 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2131
2132 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2133 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2134 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2135 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2136 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2137
Tim Peters0bf60bd2003-01-08 20:40:01 +00002138 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002139 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2140 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2141 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2142 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2143 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2144
2145 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2146 "07:47:00 %Z=EST %z=-0500")
2147 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2148 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2149
2150 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002151 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002152 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2153 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2154
Tim Petersb92bb712002-12-21 17:44:07 +00002155 # Check that an invalid tzname result raises an exception.
2156 class Badtzname(tzinfo):
2157 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002158 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002159 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2160 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002161
2162 def test_hash_edge_cases(self):
2163 # Offsets that overflow a basic time.
2164 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2165 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2166 self.assertEqual(hash(t1), hash(t2))
2167
2168 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2169 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2170 self.assertEqual(hash(t1), hash(t2))
2171
Tim Peters2a799bf2002-12-16 20:18:38 +00002172 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002173 # Try one without a tzinfo.
2174 args = 20, 59, 16, 64**2
2175 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002176 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002177 green = pickler.dumps(orig, proto)
2178 derived = unpickler.loads(green)
2179 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002180
2181 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002182 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002183 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002184 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002185 green = pickler.dumps(orig, proto)
2186 derived = unpickler.loads(green)
2187 self.assertEqual(orig, derived)
2188 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2189 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2190 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002191
2192 def test_more_bool(self):
2193 # Test cases with non-None tzinfo.
2194 cls = self.theclass
2195
2196 t = cls(0, tzinfo=FixedOffset(-300, ""))
2197 self.failUnless(t)
2198
2199 t = cls(5, tzinfo=FixedOffset(-300, ""))
2200 self.failUnless(t)
2201
2202 t = cls(5, tzinfo=FixedOffset(300, ""))
2203 self.failUnless(not t)
2204
2205 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2206 self.failUnless(not t)
2207
2208 # Mostly ensuring this doesn't overflow internally.
2209 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2210 self.failUnless(t)
2211
2212 # But this should yield a value error -- the utcoffset is bogus.
2213 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2214 self.assertRaises(ValueError, lambda: bool(t))
2215
2216 # Likewise.
2217 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2218 self.assertRaises(ValueError, lambda: bool(t))
2219
Tim Peters12bf3392002-12-24 05:41:27 +00002220 def test_replace(self):
2221 cls = self.theclass
2222 z100 = FixedOffset(100, "+100")
2223 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2224 args = [1, 2, 3, 4, z100]
2225 base = cls(*args)
2226 self.assertEqual(base, base.replace())
2227
2228 i = 0
2229 for name, newval in (("hour", 5),
2230 ("minute", 6),
2231 ("second", 7),
2232 ("microsecond", 8),
2233 ("tzinfo", zm200)):
2234 newargs = args[:]
2235 newargs[i] = newval
2236 expected = cls(*newargs)
2237 got = base.replace(**{name: newval})
2238 self.assertEqual(expected, got)
2239 i += 1
2240
2241 # Ensure we can get rid of a tzinfo.
2242 self.assertEqual(base.tzname(), "+100")
2243 base2 = base.replace(tzinfo=None)
2244 self.failUnless(base2.tzinfo is None)
2245 self.failUnless(base2.tzname() is None)
2246
2247 # Ensure we can add one.
2248 base3 = base2.replace(tzinfo=z100)
2249 self.assertEqual(base, base3)
2250 self.failUnless(base.tzinfo is base3.tzinfo)
2251
2252 # Out of bounds.
2253 base = cls(1)
2254 self.assertRaises(ValueError, base.replace, hour=24)
2255 self.assertRaises(ValueError, base.replace, minute=-1)
2256 self.assertRaises(ValueError, base.replace, second=100)
2257 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2258
Tim Peters60c76e42002-12-27 00:41:11 +00002259 def test_mixed_compare(self):
2260 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002261 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002262 self.assertEqual(t1, t2)
2263 t2 = t2.replace(tzinfo=None)
2264 self.assertEqual(t1, t2)
2265 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2266 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002267 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2268 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002269
Tim Peters0bf60bd2003-01-08 20:40:01 +00002270 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002271 class Varies(tzinfo):
2272 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002273 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002274 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002275 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002276 return self.offset
2277
2278 v = Varies()
2279 t1 = t2.replace(tzinfo=v)
2280 t2 = t2.replace(tzinfo=v)
2281 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2282 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2283 self.assertEqual(t1, t2)
2284
2285 # But if they're not identical, it isn't ignored.
2286 t2 = t2.replace(tzinfo=Varies())
2287 self.failUnless(t1 < t2) # t1's offset counter still going up
2288
Tim Petersa98924a2003-05-17 05:55:19 +00002289 def test_subclass_timetz(self):
2290
2291 class C(self.theclass):
2292 theAnswer = 42
2293
2294 def __new__(cls, *args, **kws):
2295 temp = kws.copy()
2296 extra = temp.pop('extra')
2297 result = self.theclass.__new__(cls, *args, **temp)
2298 result.extra = extra
2299 return result
2300
2301 def newmeth(self, start):
2302 return start + self.hour + self.second
2303
2304 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2305
2306 dt1 = self.theclass(*args)
2307 dt2 = C(*args, **{'extra': 7})
2308
2309 self.assertEqual(dt2.__class__, C)
2310 self.assertEqual(dt2.theAnswer, 42)
2311 self.assertEqual(dt2.extra, 7)
2312 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2313 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2314
Tim Peters4c0db782002-12-26 05:01:19 +00002315
Tim Peters0bf60bd2003-01-08 20:40:01 +00002316# Testing datetime objects with a non-None tzinfo.
2317
Guido van Rossumd8faa362007-04-27 19:54:29 +00002318class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002319 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002320
2321 def test_trivial(self):
2322 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2323 self.assertEqual(dt.year, 1)
2324 self.assertEqual(dt.month, 2)
2325 self.assertEqual(dt.day, 3)
2326 self.assertEqual(dt.hour, 4)
2327 self.assertEqual(dt.minute, 5)
2328 self.assertEqual(dt.second, 6)
2329 self.assertEqual(dt.microsecond, 7)
2330 self.assertEqual(dt.tzinfo, None)
2331
2332 def test_even_more_compare(self):
2333 # The test_compare() and test_more_compare() inherited from TestDate
2334 # and TestDateTime covered non-tzinfo cases.
2335
2336 # Smallest possible after UTC adjustment.
2337 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2338 # Largest possible after UTC adjustment.
2339 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2340 tzinfo=FixedOffset(-1439, ""))
2341
2342 # Make sure those compare correctly, and w/o overflow.
2343 self.failUnless(t1 < t2)
2344 self.failUnless(t1 != t2)
2345 self.failUnless(t2 > t1)
2346
2347 self.failUnless(t1 == t1)
2348 self.failUnless(t2 == t2)
2349
2350 # Equal afer adjustment.
2351 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2352 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2353 self.assertEqual(t1, t2)
2354
2355 # Change t1 not to subtract a minute, and t1 should be larger.
2356 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2357 self.failUnless(t1 > t2)
2358
2359 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2360 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2361 self.failUnless(t1 < t2)
2362
2363 # Back to the original t1, but make seconds resolve it.
2364 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2365 second=1)
2366 self.failUnless(t1 > t2)
2367
2368 # Likewise, but make microseconds resolve it.
2369 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2370 microsecond=1)
2371 self.failUnless(t1 > t2)
2372
2373 # Make t2 naive and it should fail.
2374 t2 = self.theclass.min
2375 self.assertRaises(TypeError, lambda: t1 == t2)
2376 self.assertEqual(t2, t2)
2377
2378 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2379 class Naive(tzinfo):
2380 def utcoffset(self, dt): return None
2381 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2382 self.assertRaises(TypeError, lambda: t1 == t2)
2383 self.assertEqual(t2, t2)
2384
2385 # OTOH, it's OK to compare two of these mixing the two ways of being
2386 # naive.
2387 t1 = self.theclass(5, 6, 7)
2388 self.assertEqual(t1, t2)
2389
2390 # Try a bogus uctoffset.
2391 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002392 def utcoffset(self, dt):
2393 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002394 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2395 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002396 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002397
Tim Peters2a799bf2002-12-16 20:18:38 +00002398 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002399 # Try one without a tzinfo.
2400 args = 6, 7, 23, 20, 59, 1, 64**2
2401 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002402 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002403 green = pickler.dumps(orig, proto)
2404 derived = unpickler.loads(green)
2405 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002406
2407 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002408 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002409 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002410 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002411 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002412 green = pickler.dumps(orig, proto)
2413 derived = unpickler.loads(green)
2414 self.assertEqual(orig, derived)
2415 self.failUnless(isinstance(derived.tzinfo,
2416 PicklableFixedOffset))
2417 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2418 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002419
2420 def test_extreme_hashes(self):
2421 # If an attempt is made to hash these via subtracting the offset
2422 # then hashing a datetime object, OverflowError results. The
2423 # Python implementation used to blow up here.
2424 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2425 hash(t)
2426 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2427 tzinfo=FixedOffset(-1439, ""))
2428 hash(t)
2429
2430 # OTOH, an OOB offset should blow up.
2431 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2432 self.assertRaises(ValueError, hash, t)
2433
2434 def test_zones(self):
2435 est = FixedOffset(-300, "EST")
2436 utc = FixedOffset(0, "UTC")
2437 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002438 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2439 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2440 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002441 self.assertEqual(t1.tzinfo, est)
2442 self.assertEqual(t2.tzinfo, utc)
2443 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002444 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2445 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2446 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002447 self.assertEqual(t1.tzname(), "EST")
2448 self.assertEqual(t2.tzname(), "UTC")
2449 self.assertEqual(t3.tzname(), "MET")
2450 self.assertEqual(hash(t1), hash(t2))
2451 self.assertEqual(hash(t1), hash(t3))
2452 self.assertEqual(hash(t2), hash(t3))
2453 self.assertEqual(t1, t2)
2454 self.assertEqual(t1, t3)
2455 self.assertEqual(t2, t3)
2456 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2457 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2458 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002459 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002460 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2461 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2462 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2463
2464 def test_combine(self):
2465 met = FixedOffset(60, "MET")
2466 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002467 tz = time(18, 45, 3, 1234, tzinfo=met)
2468 dt = datetime.combine(d, tz)
2469 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002470 tzinfo=met))
2471
2472 def test_extract(self):
2473 met = FixedOffset(60, "MET")
2474 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2475 self.assertEqual(dt.date(), date(2002, 3, 4))
2476 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002477 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002478
2479 def test_tz_aware_arithmetic(self):
2480 import random
2481
2482 now = self.theclass.now()
2483 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002484 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002485 nowaware = self.theclass.combine(now.date(), timeaware)
2486 self.failUnless(nowaware.tzinfo is tz55)
2487 self.assertEqual(nowaware.timetz(), timeaware)
2488
2489 # Can't mix aware and non-aware.
2490 self.assertRaises(TypeError, lambda: now - nowaware)
2491 self.assertRaises(TypeError, lambda: nowaware - now)
2492
Tim Peters0bf60bd2003-01-08 20:40:01 +00002493 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002494 self.assertRaises(TypeError, lambda: now + nowaware)
2495 self.assertRaises(TypeError, lambda: nowaware + now)
2496 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2497
2498 # Subtracting should yield 0.
2499 self.assertEqual(now - now, timedelta(0))
2500 self.assertEqual(nowaware - nowaware, timedelta(0))
2501
2502 # Adding a delta should preserve tzinfo.
2503 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2504 nowawareplus = nowaware + delta
2505 self.failUnless(nowaware.tzinfo is tz55)
2506 nowawareplus2 = delta + nowaware
2507 self.failUnless(nowawareplus2.tzinfo is tz55)
2508 self.assertEqual(nowawareplus, nowawareplus2)
2509
2510 # that - delta should be what we started with, and that - what we
2511 # started with should be delta.
2512 diff = nowawareplus - delta
2513 self.failUnless(diff.tzinfo is tz55)
2514 self.assertEqual(nowaware, diff)
2515 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2516 self.assertEqual(nowawareplus - nowaware, delta)
2517
2518 # Make up a random timezone.
2519 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002520 # Attach it to nowawareplus.
2521 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002522 self.failUnless(nowawareplus.tzinfo is tzr)
2523 # Make sure the difference takes the timezone adjustments into account.
2524 got = nowaware - nowawareplus
2525 # Expected: (nowaware base - nowaware offset) -
2526 # (nowawareplus base - nowawareplus offset) =
2527 # (nowaware base - nowawareplus base) +
2528 # (nowawareplus offset - nowaware offset) =
2529 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002530 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002531 self.assertEqual(got, expected)
2532
2533 # Try max possible difference.
2534 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2535 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2536 tzinfo=FixedOffset(-1439, "max"))
2537 maxdiff = max - min
2538 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2539 timedelta(minutes=2*1439))
2540
2541 def test_tzinfo_now(self):
2542 meth = self.theclass.now
2543 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2544 base = meth()
2545 # Try with and without naming the keyword.
2546 off42 = FixedOffset(42, "42")
2547 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002548 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002549 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002550 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002551 # Bad argument with and w/o naming the keyword.
2552 self.assertRaises(TypeError, meth, 16)
2553 self.assertRaises(TypeError, meth, tzinfo=16)
2554 # Bad keyword name.
2555 self.assertRaises(TypeError, meth, tinfo=off42)
2556 # Too many args.
2557 self.assertRaises(TypeError, meth, off42, off42)
2558
Tim Peters10cadce2003-01-23 19:58:02 +00002559 # We don't know which time zone we're in, and don't have a tzinfo
2560 # class to represent it, so seeing whether a tz argument actually
2561 # does a conversion is tricky.
2562 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2563 utc = FixedOffset(0, "utc", 0)
2564 for dummy in range(3):
2565 now = datetime.now(weirdtz)
2566 self.failUnless(now.tzinfo is weirdtz)
2567 utcnow = datetime.utcnow().replace(tzinfo=utc)
2568 now2 = utcnow.astimezone(weirdtz)
2569 if abs(now - now2) < timedelta(seconds=30):
2570 break
2571 # Else the code is broken, or more than 30 seconds passed between
2572 # calls; assuming the latter, just try again.
2573 else:
2574 # Three strikes and we're out.
2575 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2576
Tim Peters2a799bf2002-12-16 20:18:38 +00002577 def test_tzinfo_fromtimestamp(self):
2578 import time
2579 meth = self.theclass.fromtimestamp
2580 ts = time.time()
2581 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2582 base = meth(ts)
2583 # Try with and without naming the keyword.
2584 off42 = FixedOffset(42, "42")
2585 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002586 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002587 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002588 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002589 # Bad argument with and w/o naming the keyword.
2590 self.assertRaises(TypeError, meth, ts, 16)
2591 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2592 # Bad keyword name.
2593 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2594 # Too many args.
2595 self.assertRaises(TypeError, meth, ts, off42, off42)
2596 # Too few args.
2597 self.assertRaises(TypeError, meth)
2598
Tim Peters2a44a8d2003-01-23 20:53:10 +00002599 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002600 timestamp = 1000000000
2601 utcdatetime = datetime.utcfromtimestamp(timestamp)
2602 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2603 # But on some flavor of Mac, it's nowhere near that. So we can't have
2604 # any idea here what time that actually is, we can only test that
2605 # relative changes match.
2606 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2607 tz = FixedOffset(utcoffset, "tz", 0)
2608 expected = utcdatetime + utcoffset
2609 got = datetime.fromtimestamp(timestamp, tz)
2610 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002611
Tim Peters2a799bf2002-12-16 20:18:38 +00002612 def test_tzinfo_utcnow(self):
2613 meth = self.theclass.utcnow
2614 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2615 base = meth()
2616 # Try with and without naming the keyword; for whatever reason,
2617 # utcnow() doesn't accept a tzinfo argument.
2618 off42 = FixedOffset(42, "42")
2619 self.assertRaises(TypeError, meth, off42)
2620 self.assertRaises(TypeError, meth, tzinfo=off42)
2621
2622 def test_tzinfo_utcfromtimestamp(self):
2623 import time
2624 meth = self.theclass.utcfromtimestamp
2625 ts = time.time()
2626 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2627 base = meth(ts)
2628 # Try with and without naming the keyword; for whatever reason,
2629 # utcfromtimestamp() doesn't accept a tzinfo argument.
2630 off42 = FixedOffset(42, "42")
2631 self.assertRaises(TypeError, meth, ts, off42)
2632 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2633
2634 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002635 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002636 # DST flag.
2637 class DST(tzinfo):
2638 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002639 if isinstance(dstvalue, int):
2640 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002641 self.dstvalue = dstvalue
2642 def dst(self, dt):
2643 return self.dstvalue
2644
2645 cls = self.theclass
2646 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2647 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2648 t = d.timetuple()
2649 self.assertEqual(1, t.tm_year)
2650 self.assertEqual(1, t.tm_mon)
2651 self.assertEqual(1, t.tm_mday)
2652 self.assertEqual(10, t.tm_hour)
2653 self.assertEqual(20, t.tm_min)
2654 self.assertEqual(30, t.tm_sec)
2655 self.assertEqual(0, t.tm_wday)
2656 self.assertEqual(1, t.tm_yday)
2657 self.assertEqual(flag, t.tm_isdst)
2658
2659 # dst() returns wrong type.
2660 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2661
2662 # dst() at the edge.
2663 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2664 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2665
2666 # dst() out of range.
2667 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2668 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2669
2670 def test_utctimetuple(self):
2671 class DST(tzinfo):
2672 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002673 if isinstance(dstvalue, int):
2674 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002675 self.dstvalue = dstvalue
2676 def dst(self, dt):
2677 return self.dstvalue
2678
2679 cls = self.theclass
2680 # This can't work: DST didn't implement utcoffset.
2681 self.assertRaises(NotImplementedError,
2682 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2683
2684 class UOFS(DST):
2685 def __init__(self, uofs, dofs=None):
2686 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002687 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002688 def utcoffset(self, dt):
2689 return self.uofs
2690
2691 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2692 # in effect for a UTC time.
2693 for dstvalue in -33, 33, 0, None:
2694 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2695 t = d.utctimetuple()
2696 self.assertEqual(d.year, t.tm_year)
2697 self.assertEqual(d.month, t.tm_mon)
2698 self.assertEqual(d.day, t.tm_mday)
2699 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2700 self.assertEqual(13, t.tm_min)
2701 self.assertEqual(d.second, t.tm_sec)
2702 self.assertEqual(d.weekday(), t.tm_wday)
2703 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2704 t.tm_yday)
2705 self.assertEqual(0, t.tm_isdst)
2706
2707 # At the edges, UTC adjustment can normalize into years out-of-range
2708 # for a datetime object. Ensure that a correct timetuple is
2709 # created anyway.
2710 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2711 # That goes back 1 minute less than a full day.
2712 t = tiny.utctimetuple()
2713 self.assertEqual(t.tm_year, MINYEAR-1)
2714 self.assertEqual(t.tm_mon, 12)
2715 self.assertEqual(t.tm_mday, 31)
2716 self.assertEqual(t.tm_hour, 0)
2717 self.assertEqual(t.tm_min, 1)
2718 self.assertEqual(t.tm_sec, 37)
2719 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2720 self.assertEqual(t.tm_isdst, 0)
2721
2722 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2723 # That goes forward 1 minute less than a full day.
2724 t = huge.utctimetuple()
2725 self.assertEqual(t.tm_year, MAXYEAR+1)
2726 self.assertEqual(t.tm_mon, 1)
2727 self.assertEqual(t.tm_mday, 1)
2728 self.assertEqual(t.tm_hour, 23)
2729 self.assertEqual(t.tm_min, 58)
2730 self.assertEqual(t.tm_sec, 37)
2731 self.assertEqual(t.tm_yday, 1)
2732 self.assertEqual(t.tm_isdst, 0)
2733
2734 def test_tzinfo_isoformat(self):
2735 zero = FixedOffset(0, "+00:00")
2736 plus = FixedOffset(220, "+03:40")
2737 minus = FixedOffset(-231, "-03:51")
2738 unknown = FixedOffset(None, "")
2739
2740 cls = self.theclass
2741 datestr = '0001-02-03'
2742 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002743 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002744 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2745 timestr = '04:05:59' + (us and '.987001' or '')
2746 ofsstr = ofs is not None and d.tzname() or ''
2747 tailstr = timestr + ofsstr
2748 iso = d.isoformat()
2749 self.assertEqual(iso, datestr + 'T' + tailstr)
2750 self.assertEqual(iso, d.isoformat('T'))
2751 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002752 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002753 self.assertEqual(str(d), datestr + ' ' + tailstr)
2754
Tim Peters12bf3392002-12-24 05:41:27 +00002755 def test_replace(self):
2756 cls = self.theclass
2757 z100 = FixedOffset(100, "+100")
2758 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2759 args = [1, 2, 3, 4, 5, 6, 7, z100]
2760 base = cls(*args)
2761 self.assertEqual(base, base.replace())
2762
2763 i = 0
2764 for name, newval in (("year", 2),
2765 ("month", 3),
2766 ("day", 4),
2767 ("hour", 5),
2768 ("minute", 6),
2769 ("second", 7),
2770 ("microsecond", 8),
2771 ("tzinfo", zm200)):
2772 newargs = args[:]
2773 newargs[i] = newval
2774 expected = cls(*newargs)
2775 got = base.replace(**{name: newval})
2776 self.assertEqual(expected, got)
2777 i += 1
2778
2779 # Ensure we can get rid of a tzinfo.
2780 self.assertEqual(base.tzname(), "+100")
2781 base2 = base.replace(tzinfo=None)
2782 self.failUnless(base2.tzinfo is None)
2783 self.failUnless(base2.tzname() is None)
2784
2785 # Ensure we can add one.
2786 base3 = base2.replace(tzinfo=z100)
2787 self.assertEqual(base, base3)
2788 self.failUnless(base.tzinfo is base3.tzinfo)
2789
2790 # Out of bounds.
2791 base = cls(2000, 2, 29)
2792 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002793
Tim Peters80475bb2002-12-25 07:40:55 +00002794 def test_more_astimezone(self):
2795 # The inherited test_astimezone covered some trivial and error cases.
2796 fnone = FixedOffset(None, "None")
2797 f44m = FixedOffset(44, "44")
2798 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2799
Tim Peters10cadce2003-01-23 19:58:02 +00002800 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002801 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002802 # Replacing with degenerate tzinfo raises an exception.
2803 self.assertRaises(ValueError, dt.astimezone, fnone)
2804 # Ditto with None tz.
2805 self.assertRaises(TypeError, dt.astimezone, None)
2806 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002807 x = dt.astimezone(dt.tzinfo)
2808 self.failUnless(x.tzinfo is f44m)
2809 self.assertEqual(x.date(), dt.date())
2810 self.assertEqual(x.time(), dt.time())
2811
2812 # Replacing with different tzinfo does adjust.
2813 got = dt.astimezone(fm5h)
2814 self.failUnless(got.tzinfo is fm5h)
2815 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2816 expected = dt - dt.utcoffset() # in effect, convert to UTC
2817 expected += fm5h.utcoffset(dt) # and from there to local time
2818 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2819 self.assertEqual(got.date(), expected.date())
2820 self.assertEqual(got.time(), expected.time())
2821 self.assertEqual(got.timetz(), expected.timetz())
2822 self.failUnless(got.tzinfo is expected.tzinfo)
2823 self.assertEqual(got, expected)
2824
Tim Peters4c0db782002-12-26 05:01:19 +00002825 def test_aware_subtract(self):
2826 cls = self.theclass
2827
Tim Peters60c76e42002-12-27 00:41:11 +00002828 # Ensure that utcoffset() is ignored when the operands have the
2829 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002830 class OperandDependentOffset(tzinfo):
2831 def utcoffset(self, t):
2832 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002833 # d0 and d1 equal after adjustment
2834 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002835 else:
Tim Peters397301e2003-01-02 21:28:08 +00002836 # d2 off in the weeds
2837 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002838
2839 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2840 d0 = base.replace(minute=3)
2841 d1 = base.replace(minute=9)
2842 d2 = base.replace(minute=11)
2843 for x in d0, d1, d2:
2844 for y in d0, d1, d2:
2845 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002846 expected = timedelta(minutes=x.minute - y.minute)
2847 self.assertEqual(got, expected)
2848
2849 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2850 # ignored.
2851 base = cls(8, 9, 10, 11, 12, 13, 14)
2852 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2853 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2854 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2855 for x in d0, d1, d2:
2856 for y in d0, d1, d2:
2857 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002858 if (x is d0 or x is d1) and (y is d0 or y is d1):
2859 expected = timedelta(0)
2860 elif x is y is d2:
2861 expected = timedelta(0)
2862 elif x is d2:
2863 expected = timedelta(minutes=(11-59)-0)
2864 else:
2865 assert y is d2
2866 expected = timedelta(minutes=0-(11-59))
2867 self.assertEqual(got, expected)
2868
Tim Peters60c76e42002-12-27 00:41:11 +00002869 def test_mixed_compare(self):
2870 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002871 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002872 self.assertEqual(t1, t2)
2873 t2 = t2.replace(tzinfo=None)
2874 self.assertEqual(t1, t2)
2875 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2876 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002877 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2878 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002879
Tim Peters0bf60bd2003-01-08 20:40:01 +00002880 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002881 class Varies(tzinfo):
2882 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002883 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002884 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002885 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002886 return self.offset
2887
2888 v = Varies()
2889 t1 = t2.replace(tzinfo=v)
2890 t2 = t2.replace(tzinfo=v)
2891 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2892 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2893 self.assertEqual(t1, t2)
2894
2895 # But if they're not identical, it isn't ignored.
2896 t2 = t2.replace(tzinfo=Varies())
2897 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002898
Tim Petersa98924a2003-05-17 05:55:19 +00002899 def test_subclass_datetimetz(self):
2900
2901 class C(self.theclass):
2902 theAnswer = 42
2903
2904 def __new__(cls, *args, **kws):
2905 temp = kws.copy()
2906 extra = temp.pop('extra')
2907 result = self.theclass.__new__(cls, *args, **temp)
2908 result.extra = extra
2909 return result
2910
2911 def newmeth(self, start):
2912 return start + self.hour + self.year
2913
2914 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2915
2916 dt1 = self.theclass(*args)
2917 dt2 = C(*args, **{'extra': 7})
2918
2919 self.assertEqual(dt2.__class__, C)
2920 self.assertEqual(dt2.theAnswer, 42)
2921 self.assertEqual(dt2.extra, 7)
2922 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2923 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2924
Tim Peters621818b2002-12-29 23:44:49 +00002925# Pain to set up DST-aware tzinfo classes.
2926
2927def first_sunday_on_or_after(dt):
2928 days_to_go = 6 - dt.weekday()
2929 if days_to_go:
2930 dt += timedelta(days_to_go)
2931 return dt
2932
2933ZERO = timedelta(0)
2934HOUR = timedelta(hours=1)
2935DAY = timedelta(days=1)
2936# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2937DSTSTART = datetime(1, 4, 1, 2)
2938# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002939# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2940# being standard time on that day, there is no spelling in local time of
2941# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2942DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002943
2944class USTimeZone(tzinfo):
2945
2946 def __init__(self, hours, reprname, stdname, dstname):
2947 self.stdoffset = timedelta(hours=hours)
2948 self.reprname = reprname
2949 self.stdname = stdname
2950 self.dstname = dstname
2951
2952 def __repr__(self):
2953 return self.reprname
2954
2955 def tzname(self, dt):
2956 if self.dst(dt):
2957 return self.dstname
2958 else:
2959 return self.stdname
2960
2961 def utcoffset(self, dt):
2962 return self.stdoffset + self.dst(dt)
2963
2964 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00002965 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00002966 # An exception instead may be sensible here, in one or more of
2967 # the cases.
2968 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00002969 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00002970
2971 # Find first Sunday in April.
2972 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
2973 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
2974
2975 # Find last Sunday in October.
2976 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
2977 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
2978
Tim Peters621818b2002-12-29 23:44:49 +00002979 # Can't compare naive to aware objects, so strip the timezone from
2980 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00002981 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00002982 return HOUR
2983 else:
2984 return ZERO
2985
Tim Peters521fc152002-12-31 17:36:56 +00002986Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
2987Central = USTimeZone(-6, "Central", "CST", "CDT")
2988Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
2989Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00002990utc_real = FixedOffset(0, "UTC", 0)
2991# For better test coverage, we want another flavor of UTC that's west of
2992# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00002993utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00002994
2995class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00002996 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002997 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00002998 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002999
Tim Peters0bf60bd2003-01-08 20:40:01 +00003000 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003001
Tim Peters521fc152002-12-31 17:36:56 +00003002 # Check a time that's inside DST.
3003 def checkinside(self, dt, tz, utc, dston, dstoff):
3004 self.assertEqual(dt.dst(), HOUR)
3005
3006 # Conversion to our own timezone is always an identity.
3007 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003008
3009 asutc = dt.astimezone(utc)
3010 there_and_back = asutc.astimezone(tz)
3011
3012 # Conversion to UTC and back isn't always an identity here,
3013 # because there are redundant spellings (in local time) of
3014 # UTC time when DST begins: the clock jumps from 1:59:59
3015 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3016 # make sense then. The classes above treat 2:MM:SS as
3017 # daylight time then (it's "after 2am"), really an alias
3018 # for 1:MM:SS standard time. The latter form is what
3019 # conversion back from UTC produces.
3020 if dt.date() == dston.date() and dt.hour == 2:
3021 # We're in the redundant hour, and coming back from
3022 # UTC gives the 1:MM:SS standard-time spelling.
3023 self.assertEqual(there_and_back + HOUR, dt)
3024 # Although during was considered to be in daylight
3025 # time, there_and_back is not.
3026 self.assertEqual(there_and_back.dst(), ZERO)
3027 # They're the same times in UTC.
3028 self.assertEqual(there_and_back.astimezone(utc),
3029 dt.astimezone(utc))
3030 else:
3031 # We're not in the redundant hour.
3032 self.assertEqual(dt, there_and_back)
3033
Tim Peters327098a2003-01-20 22:54:38 +00003034 # Because we have a redundant spelling when DST begins, there is
3035 # (unforunately) an hour when DST ends that can't be spelled at all in
3036 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3037 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3038 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3039 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3040 # expressed in local time. Nevertheless, we want conversion back
3041 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003042 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003043 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003044 if dt.date() == dstoff.date() and dt.hour == 0:
3045 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003046 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003047 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3048 nexthour_utc += HOUR
3049 nexthour_tz = nexthour_utc.astimezone(tz)
3050 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003051 else:
Tim Peters327098a2003-01-20 22:54:38 +00003052 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003053
3054 # Check a time that's outside DST.
3055 def checkoutside(self, dt, tz, utc):
3056 self.assertEqual(dt.dst(), ZERO)
3057
3058 # Conversion to our own timezone is always an identity.
3059 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003060
3061 # Converting to UTC and back is an identity too.
3062 asutc = dt.astimezone(utc)
3063 there_and_back = asutc.astimezone(tz)
3064 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003065
Tim Peters1024bf82002-12-30 17:09:40 +00003066 def convert_between_tz_and_utc(self, tz, utc):
3067 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003068 # Because 1:MM on the day DST ends is taken as being standard time,
3069 # there is no spelling in tz for the last hour of daylight time.
3070 # For purposes of the test, the last hour of DST is 0:MM, which is
3071 # taken as being daylight time (and 1:MM is taken as being standard
3072 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003073 dstoff = self.dstoff.replace(tzinfo=tz)
3074 for delta in (timedelta(weeks=13),
3075 DAY,
3076 HOUR,
3077 timedelta(minutes=1),
3078 timedelta(microseconds=1)):
3079
Tim Peters521fc152002-12-31 17:36:56 +00003080 self.checkinside(dston, tz, utc, dston, dstoff)
3081 for during in dston + delta, dstoff - delta:
3082 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003083
Tim Peters521fc152002-12-31 17:36:56 +00003084 self.checkoutside(dstoff, tz, utc)
3085 for outside in dston - delta, dstoff + delta:
3086 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003087
Tim Peters621818b2002-12-29 23:44:49 +00003088 def test_easy(self):
3089 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003090 self.convert_between_tz_and_utc(Eastern, utc_real)
3091 self.convert_between_tz_and_utc(Pacific, utc_real)
3092 self.convert_between_tz_and_utc(Eastern, utc_fake)
3093 self.convert_between_tz_and_utc(Pacific, utc_fake)
3094 # The next is really dancing near the edge. It works because
3095 # Pacific and Eastern are far enough apart that their "problem
3096 # hours" don't overlap.
3097 self.convert_between_tz_and_utc(Eastern, Pacific)
3098 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003099 # OTOH, these fail! Don't enable them. The difficulty is that
3100 # the edge case tests assume that every hour is representable in
3101 # the "utc" class. This is always true for a fixed-offset tzinfo
3102 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3103 # For these adjacent DST-aware time zones, the range of time offsets
3104 # tested ends up creating hours in the one that aren't representable
3105 # in the other. For the same reason, we would see failures in the
3106 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3107 # offset deltas in convert_between_tz_and_utc().
3108 #
3109 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3110 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003111
Tim Petersf3615152003-01-01 21:51:37 +00003112 def test_tricky(self):
3113 # 22:00 on day before daylight starts.
3114 fourback = self.dston - timedelta(hours=4)
3115 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003116 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003117 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3118 # 2", we should get the 3 spelling.
3119 # If we plug 22:00 the day before into Eastern, it "looks like std
3120 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3121 # to 22:00 lands on 2:00, which makes no sense in local time (the
3122 # local clock jumps from 1 to 3). The point here is to make sure we
3123 # get the 3 spelling.
3124 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003125 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003126 self.assertEqual(expected, got)
3127
3128 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3129 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003130 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003131 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3132 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3133 # spelling.
3134 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003135 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003136 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003137
Tim Petersadf64202003-01-04 06:03:15 +00003138 # Now on the day DST ends, we want "repeat an hour" behavior.
3139 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3140 # EST 23:MM 0:MM 1:MM 2:MM
3141 # EDT 0:MM 1:MM 2:MM 3:MM
3142 # wall 0:MM 1:MM 1:MM 2:MM against these
3143 for utc in utc_real, utc_fake:
3144 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003145 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003146 # Convert that to UTC.
3147 first_std_hour -= tz.utcoffset(None)
3148 # Adjust for possibly fake UTC.
3149 asutc = first_std_hour + utc.utcoffset(None)
3150 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3151 # tz=Eastern.
3152 asutcbase = asutc.replace(tzinfo=utc)
3153 for tzhour in (0, 1, 1, 2):
3154 expectedbase = self.dstoff.replace(hour=tzhour)
3155 for minute in 0, 30, 59:
3156 expected = expectedbase.replace(minute=minute)
3157 asutc = asutcbase.replace(minute=minute)
3158 astz = asutc.astimezone(tz)
3159 self.assertEqual(astz.replace(tzinfo=None), expected)
3160 asutcbase += HOUR
3161
3162
Tim Peters710fb152003-01-02 19:35:54 +00003163 def test_bogus_dst(self):
3164 class ok(tzinfo):
3165 def utcoffset(self, dt): return HOUR
3166 def dst(self, dt): return HOUR
3167
3168 now = self.theclass.now().replace(tzinfo=utc_real)
3169 # Doesn't blow up.
3170 now.astimezone(ok())
3171
3172 # Does blow up.
3173 class notok(ok):
3174 def dst(self, dt): return None
3175 self.assertRaises(ValueError, now.astimezone, notok())
3176
Tim Peters52dcce22003-01-23 16:36:11 +00003177 def test_fromutc(self):
3178 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3179 now = datetime.utcnow().replace(tzinfo=utc_real)
3180 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3181 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3182 enow = Eastern.fromutc(now) # doesn't blow up
3183 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3184 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3185 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3186
3187 # Always converts UTC to standard time.
3188 class FauxUSTimeZone(USTimeZone):
3189 def fromutc(self, dt):
3190 return dt + self.stdoffset
3191 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3192
3193 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3194 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3195 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3196
3197 # Check around DST start.
3198 start = self.dston.replace(hour=4, tzinfo=Eastern)
3199 fstart = start.replace(tzinfo=FEastern)
3200 for wall in 23, 0, 1, 3, 4, 5:
3201 expected = start.replace(hour=wall)
3202 if wall == 23:
3203 expected -= timedelta(days=1)
3204 got = Eastern.fromutc(start)
3205 self.assertEqual(expected, got)
3206
3207 expected = fstart + FEastern.stdoffset
3208 got = FEastern.fromutc(fstart)
3209 self.assertEqual(expected, got)
3210
3211 # Ensure astimezone() calls fromutc() too.
3212 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3213 self.assertEqual(expected, got)
3214
3215 start += HOUR
3216 fstart += HOUR
3217
3218 # Check around DST end.
3219 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3220 fstart = start.replace(tzinfo=FEastern)
3221 for wall in 0, 1, 1, 2, 3, 4:
3222 expected = start.replace(hour=wall)
3223 got = Eastern.fromutc(start)
3224 self.assertEqual(expected, got)
3225
3226 expected = fstart + FEastern.stdoffset
3227 got = FEastern.fromutc(fstart)
3228 self.assertEqual(expected, got)
3229
3230 # Ensure astimezone() calls fromutc() too.
3231 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3232 self.assertEqual(expected, got)
3233
3234 start += HOUR
3235 fstart += HOUR
3236
Tim Peters710fb152003-01-02 19:35:54 +00003237
Tim Peters528ca532004-09-16 01:30:50 +00003238#############################################################################
3239# oddballs
3240
3241class Oddballs(unittest.TestCase):
3242
3243 def test_bug_1028306(self):
3244 # Trying to compare a date to a datetime should act like a mixed-
3245 # type comparison, despite that datetime is a subclass of date.
3246 as_date = date.today()
3247 as_datetime = datetime.combine(as_date, time())
3248 self.assert_(as_date != as_datetime)
3249 self.assert_(as_datetime != as_date)
3250 self.assert_(not as_date == as_datetime)
3251 self.assert_(not 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 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3259 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3260
3261 # Neverthelss, comparison should work with the base-class (date)
3262 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003263 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003264 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003265 as_different = as_datetime.replace(day= different_day)
3266 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003267
3268 # And date should compare with other subclasses of date. If a
3269 # subclass wants to stop this, it's up to the subclass to do so.
3270 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3271 self.assertEqual(as_date, date_sc)
3272 self.assertEqual(date_sc, as_date)
3273
3274 # Ditto for datetimes.
3275 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3276 as_date.day, 0, 0, 0)
3277 self.assertEqual(as_datetime, datetime_sc)
3278 self.assertEqual(datetime_sc, as_datetime)
3279
Tim Peters2a799bf2002-12-16 20:18:38 +00003280def test_main():
Guido van Rossumd8faa362007-04-27 19:54:29 +00003281 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003282
3283if __name__ == "__main__":
3284 test_main()