blob: 4b8ae82df77fcd101872453862ff90a5ab522504 [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
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00008import unittest
9
Benjamin Petersonee8712c2008-05-20 21:35:26 +000010from test import support
Tim Peters2a799bf2002-12-16 20:18:38 +000011
12from datetime import MINYEAR, MAXYEAR
13from datetime import timedelta
14from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000015from datetime import time
16from datetime import date, datetime
17
Guido van Rossumbe6fe542007-07-19 23:55:34 +000018pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
19assert len(pickle_choices) == 3
Guido van Rossum177e41a2003-01-30 22:06:23 +000020
Tim Peters68124bb2003-02-08 03:46:31 +000021# An arbitrary collection of objects of non-datetime types, for testing
22# mixed-type comparisons.
Guido van Rossume2a383d2007-01-15 16:59:06 +000023OTHERSTUFF = (10, 10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000024
Tim Peters2a799bf2002-12-16 20:18:38 +000025
26#############################################################################
27# module tests
28
29class TestModule(unittest.TestCase):
30
31 def test_constants(self):
32 import datetime
33 self.assertEqual(datetime.MINYEAR, 1)
34 self.assertEqual(datetime.MAXYEAR, 9999)
35
36#############################################################################
37# tzinfo tests
38
39class FixedOffset(tzinfo):
40 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000041 if isinstance(offset, int):
42 offset = timedelta(minutes=offset)
43 if isinstance(dstoffset, int):
44 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000045 self.__offset = offset
46 self.__name = name
47 self.__dstoffset = dstoffset
48 def __repr__(self):
49 return self.__name.lower()
50 def utcoffset(self, dt):
51 return self.__offset
52 def tzname(self, dt):
53 return self.__name
54 def dst(self, dt):
55 return self.__dstoffset
56
Tim Petersfb8472c2002-12-21 05:04:42 +000057class PicklableFixedOffset(FixedOffset):
58 def __init__(self, offset=None, name=None, dstoffset=None):
59 FixedOffset.__init__(self, offset, name, dstoffset)
60
Tim Peters2a799bf2002-12-16 20:18:38 +000061class TestTZInfo(unittest.TestCase):
62
63 def test_non_abstractness(self):
64 # In order to allow subclasses to get pickled, the C implementation
65 # wasn't able to get away with having __init__ raise
66 # NotImplementedError.
67 useless = tzinfo()
68 dt = datetime.max
69 self.assertRaises(NotImplementedError, useless.tzname, dt)
70 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
71 self.assertRaises(NotImplementedError, useless.dst, dt)
72
73 def test_subclass_must_override(self):
74 class NotEnough(tzinfo):
75 def __init__(self, offset, name):
76 self.__offset = offset
77 self.__name = name
78 self.failUnless(issubclass(NotEnough, tzinfo))
79 ne = NotEnough(3, "NotByALongShot")
80 self.failUnless(isinstance(ne, tzinfo))
81
82 dt = datetime.now()
83 self.assertRaises(NotImplementedError, ne.tzname, dt)
84 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
85 self.assertRaises(NotImplementedError, ne.dst, dt)
86
87 def test_normal(self):
88 fo = FixedOffset(3, "Three")
89 self.failUnless(isinstance(fo, tzinfo))
90 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +000091 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +000092 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +000093 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +000094
95 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +000096 # There's no point to pickling tzinfo objects on their own (they
97 # carry no data), but they need to be picklable anyway else
98 # concrete subclasses can't be pickled.
99 orig = tzinfo.__new__(tzinfo)
100 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000101 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000102 green = pickler.dumps(orig, proto)
103 derived = unpickler.loads(green)
104 self.failUnless(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000105
106 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000107 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000108 offset = timedelta(minutes=-300)
109 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000110 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000111 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000112 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000113 self.assertEqual(orig.tzname(None), 'cookie')
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(isinstance(derived, tzinfo))
118 self.failUnless(type(derived) is PicklableFixedOffset)
119 self.assertEqual(derived.utcoffset(None), offset)
120 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000121
122#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000123# Base clase for testing a particular aspect of timedelta, time, date and
124# datetime comparisons.
125
Guido van Rossumd8faa362007-04-27 19:54:29 +0000126class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000127 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
128
129 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
130 # legit constructor.
131
132 def test_harmless_mixed_comparison(self):
133 me = self.theclass(1, 1, 1)
134
135 self.failIf(me == ())
136 self.failUnless(me != ())
137 self.failIf(() == me)
138 self.failUnless(() != me)
139
Guido van Rossume2a383d2007-01-15 16:59:06 +0000140 self.failUnless(me in [1, 20, [], me])
141 self.failIf(me not in [1, 20, [], me])
Tim Peters07534a62003-02-07 22:50:28 +0000142
Guido van Rossume2a383d2007-01-15 16:59:06 +0000143 self.failUnless([] in [me, 1, 20, []])
144 self.failIf([] not in [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000145
146 def test_harmful_mixed_comparison(self):
147 me = self.theclass(1, 1, 1)
148
149 self.assertRaises(TypeError, lambda: me < ())
150 self.assertRaises(TypeError, lambda: me <= ())
151 self.assertRaises(TypeError, lambda: me > ())
152 self.assertRaises(TypeError, lambda: me >= ())
153
154 self.assertRaises(TypeError, lambda: () < me)
155 self.assertRaises(TypeError, lambda: () <= me)
156 self.assertRaises(TypeError, lambda: () > me)
157 self.assertRaises(TypeError, lambda: () >= me)
158
159 self.assertRaises(TypeError, cmp, (), me)
160 self.assertRaises(TypeError, cmp, me, ())
161
162#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000163# timedelta tests
164
Guido van Rossumd8faa362007-04-27 19:54:29 +0000165class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000166
167 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000168
169 def test_constructor(self):
170 eq = self.assertEqual
171 td = timedelta
172
173 # Check keyword args to constructor
174 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
175 milliseconds=0, microseconds=0))
176 eq(td(1), td(days=1))
177 eq(td(0, 1), td(seconds=1))
178 eq(td(0, 0, 1), td(microseconds=1))
179 eq(td(weeks=1), td(days=7))
180 eq(td(days=1), td(hours=24))
181 eq(td(hours=1), td(minutes=60))
182 eq(td(minutes=1), td(seconds=60))
183 eq(td(seconds=1), td(milliseconds=1000))
184 eq(td(milliseconds=1), td(microseconds=1000))
185
186 # Check float args to constructor
187 eq(td(weeks=1.0/7), td(days=1))
188 eq(td(days=1.0/24), td(hours=1))
189 eq(td(hours=1.0/60), td(minutes=1))
190 eq(td(minutes=1.0/60), td(seconds=1))
191 eq(td(seconds=0.001), td(milliseconds=1))
192 eq(td(milliseconds=0.001), td(microseconds=1))
193
194 def test_computations(self):
195 eq = self.assertEqual
196 td = timedelta
197
198 a = td(7) # One week
199 b = td(0, 60) # One minute
200 c = td(0, 0, 1000) # One millisecond
201 eq(a+b+c, td(7, 60, 1000))
202 eq(a-b, td(6, 24*3600 - 60))
203 eq(-a, td(-7))
204 eq(+a, td(7))
205 eq(-b, td(-1, 24*3600 - 60))
206 eq(-c, td(-1, 24*3600 - 1, 999000))
207 eq(abs(a), a)
208 eq(abs(-a), a)
209 eq(td(6, 24*3600), a)
210 eq(td(0, 0, 60*1000000), b)
211 eq(a*10, td(70))
212 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000213 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000214 eq(b*10, td(0, 600))
215 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000216 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000217 eq(c*10, td(0, 0, 10000))
218 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000219 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000220 eq(a*-1, -a)
221 eq(b*-2, -b-b)
222 eq(c*-2, -c+-c)
223 eq(b*(60*24), (b*60)*24)
224 eq(b*(60*24), (60*b)*24)
225 eq(c*1000, td(0, 1))
226 eq(1000*c, td(0, 1))
227 eq(a//7, td(1))
228 eq(b//10, td(0, 6))
229 eq(c//1000, td(0, 0, 1))
230 eq(a//10, td(0, 7*24*360))
231 eq(a//3600000, td(0, 0, 7*24*1000))
232
233 def test_disallowed_computations(self):
234 a = timedelta(42)
235
236 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000237 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000238 self.assertRaises(TypeError, lambda: a+i)
239 self.assertRaises(TypeError, lambda: a-i)
240 self.assertRaises(TypeError, lambda: i+a)
241 self.assertRaises(TypeError, lambda: i-a)
242
243 # Mul/div by float isn't supported.
244 x = 2.3
245 self.assertRaises(TypeError, lambda: a*x)
246 self.assertRaises(TypeError, lambda: x*a)
247 self.assertRaises(TypeError, lambda: a/x)
248 self.assertRaises(TypeError, lambda: x/a)
249 self.assertRaises(TypeError, lambda: a // x)
250 self.assertRaises(TypeError, lambda: x // a)
251
252 # Divison of int by timedelta doesn't make sense.
253 # Division by zero doesn't make sense.
Guido van Rossume2a383d2007-01-15 16:59:06 +0000254 for zero in 0, 0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000255 self.assertRaises(TypeError, lambda: zero // a)
256 self.assertRaises(ZeroDivisionError, lambda: a // zero)
257
258 def test_basic_attributes(self):
259 days, seconds, us = 1, 7, 31
260 td = timedelta(days, seconds, us)
261 self.assertEqual(td.days, days)
262 self.assertEqual(td.seconds, seconds)
263 self.assertEqual(td.microseconds, us)
264
265 def test_carries(self):
266 t1 = timedelta(days=100,
267 weeks=-7,
268 hours=-24*(100-49),
269 minutes=-3,
270 seconds=12,
271 microseconds=(3*60 - 12) * 1e6 + 1)
272 t2 = timedelta(microseconds=1)
273 self.assertEqual(t1, t2)
274
275 def test_hash_equality(self):
276 t1 = timedelta(days=100,
277 weeks=-7,
278 hours=-24*(100-49),
279 minutes=-3,
280 seconds=12,
281 microseconds=(3*60 - 12) * 1000000)
282 t2 = timedelta()
283 self.assertEqual(hash(t1), hash(t2))
284
285 t1 += timedelta(weeks=7)
286 t2 += timedelta(days=7*7)
287 self.assertEqual(t1, t2)
288 self.assertEqual(hash(t1), hash(t2))
289
290 d = {t1: 1}
291 d[t2] = 2
292 self.assertEqual(len(d), 1)
293 self.assertEqual(d[t1], 2)
294
295 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000296 args = 12, 34, 56
297 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000298 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000299 green = pickler.dumps(orig, proto)
300 derived = unpickler.loads(green)
301 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000302
303 def test_compare(self):
304 t1 = timedelta(2, 3, 4)
305 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000306 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000307 self.failUnless(t1 <= t2)
308 self.failUnless(t1 >= t2)
309 self.failUnless(not t1 != t2)
310 self.failUnless(not t1 < t2)
311 self.failUnless(not t1 > t2)
312 self.assertEqual(cmp(t1, t2), 0)
313 self.assertEqual(cmp(t2, t1), 0)
314
315 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
316 t2 = timedelta(*args) # this is larger than t1
317 self.failUnless(t1 < t2)
318 self.failUnless(t2 > t1)
319 self.failUnless(t1 <= t2)
320 self.failUnless(t2 >= t1)
321 self.failUnless(t1 != t2)
322 self.failUnless(t2 != t1)
323 self.failUnless(not t1 == t2)
324 self.failUnless(not t2 == t1)
325 self.failUnless(not t1 > t2)
326 self.failUnless(not t2 < t1)
327 self.failUnless(not t1 >= t2)
328 self.failUnless(not t2 <= t1)
329 self.assertEqual(cmp(t1, t2), -1)
330 self.assertEqual(cmp(t2, t1), 1)
331
Tim Peters68124bb2003-02-08 03:46:31 +0000332 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000333 self.assertEqual(t1 == badarg, False)
334 self.assertEqual(t1 != badarg, True)
335 self.assertEqual(badarg == t1, False)
336 self.assertEqual(badarg != t1, True)
337
Tim Peters2a799bf2002-12-16 20:18:38 +0000338 self.assertRaises(TypeError, lambda: t1 <= badarg)
339 self.assertRaises(TypeError, lambda: t1 < badarg)
340 self.assertRaises(TypeError, lambda: t1 > badarg)
341 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000342 self.assertRaises(TypeError, lambda: badarg <= t1)
343 self.assertRaises(TypeError, lambda: badarg < t1)
344 self.assertRaises(TypeError, lambda: badarg > t1)
345 self.assertRaises(TypeError, lambda: badarg >= t1)
346
347 def test_str(self):
348 td = timedelta
349 eq = self.assertEqual
350
351 eq(str(td(1)), "1 day, 0:00:00")
352 eq(str(td(-1)), "-1 day, 0:00:00")
353 eq(str(td(2)), "2 days, 0:00:00")
354 eq(str(td(-2)), "-2 days, 0:00:00")
355
356 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
357 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
358 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
359 "-210 days, 23:12:34")
360
361 eq(str(td(milliseconds=1)), "0:00:00.001000")
362 eq(str(td(microseconds=3)), "0:00:00.000003")
363
364 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
365 microseconds=999999)),
366 "999999999 days, 23:59:59.999999")
367
368 def test_roundtrip(self):
369 for td in (timedelta(days=999999999, hours=23, minutes=59,
370 seconds=59, microseconds=999999),
371 timedelta(days=-999999999),
372 timedelta(days=1, seconds=2, microseconds=3)):
373
374 # Verify td -> string -> td identity.
375 s = repr(td)
376 self.failUnless(s.startswith('datetime.'))
377 s = s[9:]
378 td2 = eval(s)
379 self.assertEqual(td, td2)
380
381 # Verify identity via reconstructing from pieces.
382 td2 = timedelta(td.days, td.seconds, td.microseconds)
383 self.assertEqual(td, td2)
384
385 def test_resolution_info(self):
386 self.assert_(isinstance(timedelta.min, timedelta))
387 self.assert_(isinstance(timedelta.max, timedelta))
388 self.assert_(isinstance(timedelta.resolution, timedelta))
389 self.assert_(timedelta.max > timedelta.min)
390 self.assertEqual(timedelta.min, timedelta(-999999999))
391 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
392 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
393
394 def test_overflow(self):
395 tiny = timedelta.resolution
396
397 td = timedelta.min + tiny
398 td -= tiny # no problem
399 self.assertRaises(OverflowError, td.__sub__, tiny)
400 self.assertRaises(OverflowError, td.__add__, -tiny)
401
402 td = timedelta.max - tiny
403 td += tiny # no problem
404 self.assertRaises(OverflowError, td.__add__, tiny)
405 self.assertRaises(OverflowError, td.__sub__, -tiny)
406
407 self.assertRaises(OverflowError, lambda: -timedelta.max)
408
409 def test_microsecond_rounding(self):
410 td = timedelta
411 eq = self.assertEqual
412
413 # Single-field rounding.
414 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
415 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
416 eq(td(milliseconds=0.6/1000), td(microseconds=1))
417 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
418
419 # Rounding due to contributions from more than one field.
420 us_per_hour = 3600e6
421 us_per_day = us_per_hour * 24
422 eq(td(days=.4/us_per_day), td(0))
423 eq(td(hours=.2/us_per_hour), td(0))
424 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
425
426 eq(td(days=-.4/us_per_day), td(0))
427 eq(td(hours=-.2/us_per_hour), td(0))
428 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
429
430 def test_massive_normalization(self):
431 td = timedelta(microseconds=-1)
432 self.assertEqual((td.days, td.seconds, td.microseconds),
433 (-1, 24*3600-1, 999999))
434
435 def test_bool(self):
436 self.failUnless(timedelta(1))
437 self.failUnless(timedelta(0, 1))
438 self.failUnless(timedelta(0, 0, 1))
439 self.failUnless(timedelta(microseconds=1))
440 self.failUnless(not timedelta(0))
441
Tim Petersb0c854d2003-05-17 15:57:00 +0000442 def test_subclass_timedelta(self):
443
444 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000445 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000446 def from_td(td):
447 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000448
449 def as_hours(self):
450 sum = (self.days * 24 +
451 self.seconds / 3600.0 +
452 self.microseconds / 3600e6)
453 return round(sum)
454
455 t1 = T(days=1)
456 self.assert_(type(t1) is T)
457 self.assertEqual(t1.as_hours(), 24)
458
459 t2 = T(days=-1, seconds=-3600)
460 self.assert_(type(t2) is T)
461 self.assertEqual(t2.as_hours(), -25)
462
463 t3 = t1 + t2
464 self.assert_(type(t3) is timedelta)
465 t4 = T.from_td(t3)
466 self.assert_(type(t4) is T)
467 self.assertEqual(t3.days, t4.days)
468 self.assertEqual(t3.seconds, t4.seconds)
469 self.assertEqual(t3.microseconds, t4.microseconds)
470 self.assertEqual(str(t3), str(t4))
471 self.assertEqual(t4.as_hours(), -1)
472
Tim Peters2a799bf2002-12-16 20:18:38 +0000473#############################################################################
474# date tests
475
476class TestDateOnly(unittest.TestCase):
477 # Tests here won't pass if also run on datetime objects, so don't
478 # subclass this to test datetimes too.
479
480 def test_delta_non_days_ignored(self):
481 dt = date(2000, 1, 2)
482 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
483 microseconds=5)
484 days = timedelta(delta.days)
485 self.assertEqual(days, timedelta(1))
486
487 dt2 = dt + delta
488 self.assertEqual(dt2, dt + days)
489
490 dt2 = delta + dt
491 self.assertEqual(dt2, dt + days)
492
493 dt2 = dt - delta
494 self.assertEqual(dt2, dt - days)
495
496 delta = -delta
497 days = timedelta(delta.days)
498 self.assertEqual(days, timedelta(-2))
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
Tim Peters604c0132004-06-07 23:04:33 +0000509class SubclassDate(date):
510 sub_var = 1
511
Guido van Rossumd8faa362007-04-27 19:54:29 +0000512class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000513 # Tests here should pass for both dates and datetimes, except for a
514 # few tests that TestDateTime overrides.
515
516 theclass = date
517
518 def test_basic_attributes(self):
519 dt = self.theclass(2002, 3, 1)
520 self.assertEqual(dt.year, 2002)
521 self.assertEqual(dt.month, 3)
522 self.assertEqual(dt.day, 1)
523
524 def test_roundtrip(self):
525 for dt in (self.theclass(1, 2, 3),
526 self.theclass.today()):
527 # Verify dt -> string -> date identity.
528 s = repr(dt)
529 self.failUnless(s.startswith('datetime.'))
530 s = s[9:]
531 dt2 = eval(s)
532 self.assertEqual(dt, dt2)
533
534 # Verify identity via reconstructing from pieces.
535 dt2 = self.theclass(dt.year, dt.month, dt.day)
536 self.assertEqual(dt, dt2)
537
538 def test_ordinal_conversions(self):
539 # Check some fixed values.
540 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
541 (1, 12, 31, 365),
542 (2, 1, 1, 366),
543 # first example from "Calendrical Calculations"
544 (1945, 11, 12, 710347)]:
545 d = self.theclass(y, m, d)
546 self.assertEqual(n, d.toordinal())
547 fromord = self.theclass.fromordinal(n)
548 self.assertEqual(d, fromord)
549 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000550 # if we're checking something fancier than a date, verify
551 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000552 self.assertEqual(fromord.hour, 0)
553 self.assertEqual(fromord.minute, 0)
554 self.assertEqual(fromord.second, 0)
555 self.assertEqual(fromord.microsecond, 0)
556
Tim Peters0bf60bd2003-01-08 20:40:01 +0000557 # Check first and last days of year spottily across the whole
558 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000559 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000560 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
561 d = self.theclass(year, 1, 1)
562 n = d.toordinal()
563 d2 = self.theclass.fromordinal(n)
564 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000565 # Verify that moving back a day gets to the end of year-1.
566 if year > 1:
567 d = self.theclass.fromordinal(n-1)
568 d2 = self.theclass(year-1, 12, 31)
569 self.assertEqual(d, d2)
570 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000571
572 # Test every day in a leap-year and a non-leap year.
573 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
574 for year, isleap in (2000, True), (2002, False):
575 n = self.theclass(year, 1, 1).toordinal()
576 for month, maxday in zip(range(1, 13), dim):
577 if month == 2 and isleap:
578 maxday += 1
579 for day in range(1, maxday+1):
580 d = self.theclass(year, month, day)
581 self.assertEqual(d.toordinal(), n)
582 self.assertEqual(d, self.theclass.fromordinal(n))
583 n += 1
584
585 def test_extreme_ordinals(self):
586 a = self.theclass.min
587 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
588 aord = a.toordinal()
589 b = a.fromordinal(aord)
590 self.assertEqual(a, b)
591
592 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
593
594 b = a + timedelta(days=1)
595 self.assertEqual(b.toordinal(), aord + 1)
596 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
597
598 a = self.theclass.max
599 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
600 aord = a.toordinal()
601 b = a.fromordinal(aord)
602 self.assertEqual(a, b)
603
604 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
605
606 b = a - timedelta(days=1)
607 self.assertEqual(b.toordinal(), aord - 1)
608 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
609
610 def test_bad_constructor_arguments(self):
611 # bad years
612 self.theclass(MINYEAR, 1, 1) # no exception
613 self.theclass(MAXYEAR, 1, 1) # no exception
614 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
615 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
616 # bad months
617 self.theclass(2000, 1, 1) # no exception
618 self.theclass(2000, 12, 1) # no exception
619 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
620 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
621 # bad days
622 self.theclass(2000, 2, 29) # no exception
623 self.theclass(2004, 2, 29) # no exception
624 self.theclass(2400, 2, 29) # no exception
625 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
626 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
627 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
628 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
629 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
630 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
631
632 def test_hash_equality(self):
633 d = self.theclass(2000, 12, 31)
634 # same thing
635 e = self.theclass(2000, 12, 31)
636 self.assertEqual(d, e)
637 self.assertEqual(hash(d), hash(e))
638
639 dic = {d: 1}
640 dic[e] = 2
641 self.assertEqual(len(dic), 1)
642 self.assertEqual(dic[d], 2)
643 self.assertEqual(dic[e], 2)
644
645 d = self.theclass(2001, 1, 1)
646 # same thing
647 e = self.theclass(2001, 1, 1)
648 self.assertEqual(d, e)
649 self.assertEqual(hash(d), hash(e))
650
651 dic = {d: 1}
652 dic[e] = 2
653 self.assertEqual(len(dic), 1)
654 self.assertEqual(dic[d], 2)
655 self.assertEqual(dic[e], 2)
656
657 def test_computations(self):
658 a = self.theclass(2002, 1, 31)
659 b = self.theclass(1956, 1, 31)
660
661 diff = a-b
662 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
663 self.assertEqual(diff.seconds, 0)
664 self.assertEqual(diff.microseconds, 0)
665
666 day = timedelta(1)
667 week = timedelta(7)
668 a = self.theclass(2002, 3, 2)
669 self.assertEqual(a + day, self.theclass(2002, 3, 3))
670 self.assertEqual(day + a, self.theclass(2002, 3, 3))
671 self.assertEqual(a - day, self.theclass(2002, 3, 1))
672 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
673 self.assertEqual(a + week, self.theclass(2002, 3, 9))
674 self.assertEqual(a - week, self.theclass(2002, 2, 23))
675 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
676 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
677 self.assertEqual((a + week) - a, week)
678 self.assertEqual((a + day) - a, day)
679 self.assertEqual((a - week) - a, -week)
680 self.assertEqual((a - day) - a, -day)
681 self.assertEqual(a - (a + week), -week)
682 self.assertEqual(a - (a + day), -day)
683 self.assertEqual(a - (a - week), week)
684 self.assertEqual(a - (a - day), day)
685
686 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000687 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000688 self.assertRaises(TypeError, lambda: a+i)
689 self.assertRaises(TypeError, lambda: a-i)
690 self.assertRaises(TypeError, lambda: i+a)
691 self.assertRaises(TypeError, lambda: i-a)
692
693 # delta - date is senseless.
694 self.assertRaises(TypeError, lambda: day - a)
695 # mixing date and (delta or date) via * or // is senseless
696 self.assertRaises(TypeError, lambda: day * a)
697 self.assertRaises(TypeError, lambda: a * day)
698 self.assertRaises(TypeError, lambda: day // a)
699 self.assertRaises(TypeError, lambda: a // day)
700 self.assertRaises(TypeError, lambda: a * a)
701 self.assertRaises(TypeError, lambda: a // a)
702 # date + date is senseless
703 self.assertRaises(TypeError, lambda: a + a)
704
705 def test_overflow(self):
706 tiny = self.theclass.resolution
707
708 dt = self.theclass.min + tiny
709 dt -= tiny # no problem
710 self.assertRaises(OverflowError, dt.__sub__, tiny)
711 self.assertRaises(OverflowError, dt.__add__, -tiny)
712
713 dt = self.theclass.max - tiny
714 dt += tiny # no problem
715 self.assertRaises(OverflowError, dt.__add__, tiny)
716 self.assertRaises(OverflowError, dt.__sub__, -tiny)
717
718 def test_fromtimestamp(self):
719 import time
720
721 # Try an arbitrary fixed value.
722 year, month, day = 1999, 9, 19
723 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
724 d = self.theclass.fromtimestamp(ts)
725 self.assertEqual(d.year, year)
726 self.assertEqual(d.month, month)
727 self.assertEqual(d.day, day)
728
Tim Peters1b6f7a92004-06-20 02:50:16 +0000729 def test_insane_fromtimestamp(self):
730 # It's possible that some platform maps time_t to double,
731 # and that this test will fail there. This test should
732 # exempt such platforms (provided they return reasonable
733 # results!).
734 for insane in -1e200, 1e200:
735 self.assertRaises(ValueError, self.theclass.fromtimestamp,
736 insane)
737
Tim Peters2a799bf2002-12-16 20:18:38 +0000738 def test_today(self):
739 import time
740
741 # We claim that today() is like fromtimestamp(time.time()), so
742 # prove it.
743 for dummy in range(3):
744 today = self.theclass.today()
745 ts = time.time()
746 todayagain = self.theclass.fromtimestamp(ts)
747 if today == todayagain:
748 break
749 # There are several legit reasons that could fail:
750 # 1. It recently became midnight, between the today() and the
751 # time() calls.
752 # 2. The platform time() has such fine resolution that we'll
753 # never get the same value twice.
754 # 3. The platform time() has poor resolution, and we just
755 # happened to call today() right before a resolution quantum
756 # boundary.
757 # 4. The system clock got fiddled between calls.
758 # In any case, wait a little while and try again.
759 time.sleep(0.1)
760
761 # It worked or it didn't. If it didn't, assume it's reason #2, and
762 # let the test pass if they're within half a second of each other.
763 self.failUnless(today == todayagain or
764 abs(todayagain - today) < timedelta(seconds=0.5))
765
766 def test_weekday(self):
767 for i in range(7):
768 # March 4, 2002 is a Monday
769 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
770 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
771 # January 2, 1956 is a Monday
772 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
773 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
774
775 def test_isocalendar(self):
776 # Check examples from
777 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
778 for i in range(7):
779 d = self.theclass(2003, 12, 22+i)
780 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
781 d = self.theclass(2003, 12, 29) + timedelta(i)
782 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
783 d = self.theclass(2004, 1, 5+i)
784 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
785 d = self.theclass(2009, 12, 21+i)
786 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
787 d = self.theclass(2009, 12, 28) + timedelta(i)
788 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
789 d = self.theclass(2010, 1, 4+i)
790 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
791
792 def test_iso_long_years(self):
793 # Calculate long ISO years and compare to table from
794 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
795 ISO_LONG_YEARS_TABLE = """
796 4 32 60 88
797 9 37 65 93
798 15 43 71 99
799 20 48 76
800 26 54 82
801
802 105 133 161 189
803 111 139 167 195
804 116 144 172
805 122 150 178
806 128 156 184
807
808 201 229 257 285
809 207 235 263 291
810 212 240 268 296
811 218 246 274
812 224 252 280
813
814 303 331 359 387
815 308 336 364 392
816 314 342 370 398
817 320 348 376
818 325 353 381
819 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000820 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000821 L = []
822 for i in range(400):
823 d = self.theclass(2000+i, 12, 31)
824 d1 = self.theclass(1600+i, 12, 31)
825 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
826 if d.isocalendar()[1] == 53:
827 L.append(i)
828 self.assertEqual(L, iso_long_years)
829
830 def test_isoformat(self):
831 t = self.theclass(2, 3, 2)
832 self.assertEqual(t.isoformat(), "0002-03-02")
833
834 def test_ctime(self):
835 t = self.theclass(2002, 3, 2)
836 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
837
838 def test_strftime(self):
839 t = self.theclass(2005, 3, 2)
840 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000841 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000842 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000843
844 self.assertRaises(TypeError, t.strftime) # needs an arg
845 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
846 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
847
Georg Brandlf78e02b2008-06-10 17:40:04 +0000848 # test that unicode input is allowed (issue 2782)
849 self.assertEqual(t.strftime("%m"), "03")
850
Tim Peters2a799bf2002-12-16 20:18:38 +0000851 # A naive object replaces %z and %Z w/ empty strings.
852 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
853
Georg Brandlf78e02b2008-06-10 17:40:04 +0000854
Eric Smith1ba31142007-09-11 18:06:02 +0000855 def test_format(self):
856 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000857 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000858
859 # check that a derived class's __str__() gets called
860 class A(self.theclass):
861 def __str__(self):
862 return 'A'
863 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000864 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000865
866 # check that a derived class's strftime gets called
867 class B(self.theclass):
868 def strftime(self, format_spec):
869 return 'B'
870 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000871 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000872
873 for fmt in ["m:%m d:%d y:%y",
874 "m:%m d:%d y:%y H:%H M:%M S:%S",
875 "%z %Z",
876 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +0000877 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
878 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
879 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +0000880
Tim Peters2a799bf2002-12-16 20:18:38 +0000881 def test_resolution_info(self):
882 self.assert_(isinstance(self.theclass.min, self.theclass))
883 self.assert_(isinstance(self.theclass.max, self.theclass))
884 self.assert_(isinstance(self.theclass.resolution, timedelta))
885 self.assert_(self.theclass.max > self.theclass.min)
886
887 def test_extreme_timedelta(self):
888 big = self.theclass.max - self.theclass.min
889 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
890 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
891 # n == 315537897599999999 ~= 2**58.13
892 justasbig = timedelta(0, 0, n)
893 self.assertEqual(big, justasbig)
894 self.assertEqual(self.theclass.min + big, self.theclass.max)
895 self.assertEqual(self.theclass.max - big, self.theclass.min)
896
897 def test_timetuple(self):
898 for i in range(7):
899 # January 2, 1956 is a Monday (0)
900 d = self.theclass(1956, 1, 2+i)
901 t = d.timetuple()
902 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
903 # February 1, 1956 is a Wednesday (2)
904 d = self.theclass(1956, 2, 1+i)
905 t = d.timetuple()
906 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
907 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
908 # of the year.
909 d = self.theclass(1956, 3, 1+i)
910 t = d.timetuple()
911 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
912 self.assertEqual(t.tm_year, 1956)
913 self.assertEqual(t.tm_mon, 3)
914 self.assertEqual(t.tm_mday, 1+i)
915 self.assertEqual(t.tm_hour, 0)
916 self.assertEqual(t.tm_min, 0)
917 self.assertEqual(t.tm_sec, 0)
918 self.assertEqual(t.tm_wday, (3+i)%7)
919 self.assertEqual(t.tm_yday, 61+i)
920 self.assertEqual(t.tm_isdst, -1)
921
922 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000923 args = 6, 7, 23
924 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000925 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000926 green = pickler.dumps(orig, proto)
927 derived = unpickler.loads(green)
928 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000929
930 def test_compare(self):
931 t1 = self.theclass(2, 3, 4)
932 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000933 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000934 self.failUnless(t1 <= t2)
935 self.failUnless(t1 >= t2)
936 self.failUnless(not t1 != t2)
937 self.failUnless(not t1 < t2)
938 self.failUnless(not t1 > t2)
939 self.assertEqual(cmp(t1, t2), 0)
940 self.assertEqual(cmp(t2, t1), 0)
941
942 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
943 t2 = self.theclass(*args) # this is larger than t1
944 self.failUnless(t1 < t2)
945 self.failUnless(t2 > t1)
946 self.failUnless(t1 <= t2)
947 self.failUnless(t2 >= t1)
948 self.failUnless(t1 != t2)
949 self.failUnless(t2 != t1)
950 self.failUnless(not t1 == t2)
951 self.failUnless(not t2 == t1)
952 self.failUnless(not t1 > t2)
953 self.failUnless(not t2 < t1)
954 self.failUnless(not t1 >= t2)
955 self.failUnless(not t2 <= t1)
956 self.assertEqual(cmp(t1, t2), -1)
957 self.assertEqual(cmp(t2, t1), 1)
958
Tim Peters68124bb2003-02-08 03:46:31 +0000959 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000960 self.assertEqual(t1 == badarg, False)
961 self.assertEqual(t1 != badarg, True)
962 self.assertEqual(badarg == t1, False)
963 self.assertEqual(badarg != t1, True)
964
Tim Peters2a799bf2002-12-16 20:18:38 +0000965 self.assertRaises(TypeError, lambda: t1 < badarg)
966 self.assertRaises(TypeError, lambda: t1 > badarg)
967 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000968 self.assertRaises(TypeError, lambda: badarg <= t1)
969 self.assertRaises(TypeError, lambda: badarg < t1)
970 self.assertRaises(TypeError, lambda: badarg > t1)
971 self.assertRaises(TypeError, lambda: badarg >= t1)
972
Tim Peters8d81a012003-01-24 22:36:34 +0000973 def test_mixed_compare(self):
974 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000975
976 # Our class can be compared for equality to other classes
977 self.assertEqual(our == 1, False)
978 self.assertEqual(1 == our, False)
979 self.assertEqual(our != 1, True)
980 self.assertEqual(1 != our, True)
981
982 # But the ordering is undefined
983 self.assertRaises(TypeError, lambda: our < 1)
984 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000985 self.assertRaises(TypeError, cmp, our, 1)
986 self.assertRaises(TypeError, cmp, 1, our)
987
Guido van Rossum19960592006-08-24 17:29:38 +0000988 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +0000989
Guido van Rossum19960592006-08-24 17:29:38 +0000990 class SomeClass:
991 pass
992
993 their = SomeClass()
994 self.assertEqual(our == their, False)
995 self.assertEqual(their == our, False)
996 self.assertEqual(our != their, True)
997 self.assertEqual(their != our, True)
998 self.assertRaises(TypeError, lambda: our < their)
999 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001000 self.assertRaises(TypeError, cmp, our, their)
Guido van Rossum19960592006-08-24 17:29:38 +00001001 self.assertRaises(TypeError, cmp, their, our)
Tim Peters8d81a012003-01-24 22:36:34 +00001002
Guido van Rossum19960592006-08-24 17:29:38 +00001003 # However, if the other class explicitly defines ordering
1004 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001005
Guido van Rossum19960592006-08-24 17:29:38 +00001006 class LargerThanAnything:
1007 def __lt__(self, other):
1008 return False
1009 def __le__(self, other):
1010 return isinstance(other, LargerThanAnything)
1011 def __eq__(self, other):
1012 return isinstance(other, LargerThanAnything)
1013 def __ne__(self, other):
1014 return not isinstance(other, LargerThanAnything)
1015 def __gt__(self, other):
1016 return not isinstance(other, LargerThanAnything)
1017 def __ge__(self, other):
1018 return True
1019
1020 their = LargerThanAnything()
1021 self.assertEqual(our == their, False)
1022 self.assertEqual(their == our, False)
1023 self.assertEqual(our != their, True)
1024 self.assertEqual(their != our, True)
1025 self.assertEqual(our < their, True)
1026 self.assertEqual(their < our, False)
1027 self.assertEqual(cmp(our, their), -1)
1028 self.assertEqual(cmp(their, our), 1)
Tim Peters8d81a012003-01-24 22:36:34 +00001029
Tim Peters2a799bf2002-12-16 20:18:38 +00001030 def test_bool(self):
1031 # All dates are considered true.
1032 self.failUnless(self.theclass.min)
1033 self.failUnless(self.theclass.max)
1034
Guido van Rossum04110fb2007-08-24 16:32:05 +00001035 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001036 # For nasty technical reasons, we can't handle years before 1900.
1037 cls = self.theclass
1038 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1039 for y in 1, 49, 51, 99, 100, 1000, 1899:
1040 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001041
1042 def test_replace(self):
1043 cls = self.theclass
1044 args = [1, 2, 3]
1045 base = cls(*args)
1046 self.assertEqual(base, base.replace())
1047
1048 i = 0
1049 for name, newval in (("year", 2),
1050 ("month", 3),
1051 ("day", 4)):
1052 newargs = args[:]
1053 newargs[i] = newval
1054 expected = cls(*newargs)
1055 got = base.replace(**{name: newval})
1056 self.assertEqual(expected, got)
1057 i += 1
1058
1059 # Out of bounds.
1060 base = cls(2000, 2, 29)
1061 self.assertRaises(ValueError, base.replace, year=2001)
1062
Tim Petersa98924a2003-05-17 05:55:19 +00001063 def test_subclass_date(self):
1064
1065 class C(self.theclass):
1066 theAnswer = 42
1067
1068 def __new__(cls, *args, **kws):
1069 temp = kws.copy()
1070 extra = temp.pop('extra')
1071 result = self.theclass.__new__(cls, *args, **temp)
1072 result.extra = extra
1073 return result
1074
1075 def newmeth(self, start):
1076 return start + self.year + self.month
1077
1078 args = 2003, 4, 14
1079
1080 dt1 = self.theclass(*args)
1081 dt2 = C(*args, **{'extra': 7})
1082
1083 self.assertEqual(dt2.__class__, C)
1084 self.assertEqual(dt2.theAnswer, 42)
1085 self.assertEqual(dt2.extra, 7)
1086 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1087 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1088
Tim Peters604c0132004-06-07 23:04:33 +00001089 def test_pickling_subclass_date(self):
1090
1091 args = 6, 7, 23
1092 orig = SubclassDate(*args)
1093 for pickler, unpickler, proto in pickle_choices:
1094 green = pickler.dumps(orig, proto)
1095 derived = unpickler.loads(green)
1096 self.assertEqual(orig, derived)
1097
Tim Peters3f606292004-03-21 23:38:41 +00001098 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001099 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001100 # This is a low-overhead backdoor. A user can (by intent or
1101 # mistake) pass a string directly, which (if it's the right length)
1102 # will get treated like a pickle, and bypass the normal sanity
1103 # checks in the constructor. This can create insane objects.
1104 # The constructor doesn't want to burn the time to validate all
1105 # fields, but does check the month field. This stops, e.g.,
1106 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001107 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001108 if not issubclass(self.theclass, datetime):
1109 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001110 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001111 self.assertRaises(TypeError, self.theclass,
1112 base[:2] + month_byte + base[3:])
1113 for ord_byte in range(1, 13):
1114 # This shouldn't blow up because of the month byte alone. If
1115 # the implementation changes to do more-careful checking, it may
1116 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001117 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001118
Tim Peters2a799bf2002-12-16 20:18:38 +00001119#############################################################################
1120# datetime tests
1121
Tim Peters604c0132004-06-07 23:04:33 +00001122class SubclassDatetime(datetime):
1123 sub_var = 1
1124
Tim Peters2a799bf2002-12-16 20:18:38 +00001125class TestDateTime(TestDate):
1126
1127 theclass = datetime
1128
1129 def test_basic_attributes(self):
1130 dt = self.theclass(2002, 3, 1, 12, 0)
1131 self.assertEqual(dt.year, 2002)
1132 self.assertEqual(dt.month, 3)
1133 self.assertEqual(dt.day, 1)
1134 self.assertEqual(dt.hour, 12)
1135 self.assertEqual(dt.minute, 0)
1136 self.assertEqual(dt.second, 0)
1137 self.assertEqual(dt.microsecond, 0)
1138
1139 def test_basic_attributes_nonzero(self):
1140 # Make sure all attributes are non-zero so bugs in
1141 # bit-shifting access show up.
1142 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1143 self.assertEqual(dt.year, 2002)
1144 self.assertEqual(dt.month, 3)
1145 self.assertEqual(dt.day, 1)
1146 self.assertEqual(dt.hour, 12)
1147 self.assertEqual(dt.minute, 59)
1148 self.assertEqual(dt.second, 59)
1149 self.assertEqual(dt.microsecond, 8000)
1150
1151 def test_roundtrip(self):
1152 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1153 self.theclass.now()):
1154 # Verify dt -> string -> datetime identity.
1155 s = repr(dt)
1156 self.failUnless(s.startswith('datetime.'))
1157 s = s[9:]
1158 dt2 = eval(s)
1159 self.assertEqual(dt, dt2)
1160
1161 # Verify identity via reconstructing from pieces.
1162 dt2 = self.theclass(dt.year, dt.month, dt.day,
1163 dt.hour, dt.minute, dt.second,
1164 dt.microsecond)
1165 self.assertEqual(dt, dt2)
1166
1167 def test_isoformat(self):
1168 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1169 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1170 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1171 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1172 # str is ISO format with the separator forced to a blank.
1173 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1174
1175 t = self.theclass(2, 3, 2)
1176 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1177 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1178 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1179 # str is ISO format with the separator forced to a blank.
1180 self.assertEqual(str(t), "0002-03-02 00:00:00")
1181
Eric Smith1ba31142007-09-11 18:06:02 +00001182 def test_format(self):
1183 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001184 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001185
1186 # check that a derived class's __str__() gets called
1187 class A(self.theclass):
1188 def __str__(self):
1189 return 'A'
1190 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001191 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001192
1193 # check that a derived class's strftime gets called
1194 class B(self.theclass):
1195 def strftime(self, format_spec):
1196 return 'B'
1197 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001198 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001199
1200 for fmt in ["m:%m d:%d y:%y",
1201 "m:%m d:%d y:%y H:%H M:%M S:%S",
1202 "%z %Z",
1203 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001204 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1205 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1206 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001207
Tim Peters2a799bf2002-12-16 20:18:38 +00001208 def test_more_ctime(self):
1209 # Test fields that TestDate doesn't touch.
1210 import time
1211
1212 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1213 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1214 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1215 # out. The difference is that t.ctime() produces " 2" for the day,
1216 # but platform ctime() produces "02" for the day. According to
1217 # C99, t.ctime() is correct here.
1218 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1219
1220 # So test a case where that difference doesn't matter.
1221 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1222 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1223
1224 def test_tz_independent_comparing(self):
1225 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1226 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1227 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1228 self.assertEqual(dt1, dt3)
1229 self.assert_(dt2 > dt3)
1230
1231 # Make sure comparison doesn't forget microseconds, and isn't done
1232 # via comparing a float timestamp (an IEEE double doesn't have enough
1233 # precision to span microsecond resolution across years 1 thru 9999,
1234 # so comparing via timestamp necessarily calls some distinct values
1235 # equal).
1236 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1237 us = timedelta(microseconds=1)
1238 dt2 = dt1 + us
1239 self.assertEqual(dt2 - dt1, us)
1240 self.assert_(dt1 < dt2)
1241
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001242 def test_strftime_with_bad_tzname_replace(self):
1243 # verify ok if tzinfo.tzname().replace() returns a non-string
1244 class MyTzInfo(FixedOffset):
1245 def tzname(self, dt):
1246 class MyStr(str):
1247 def replace(self, *args):
1248 return None
1249 return MyStr('name')
1250 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1251 self.assertRaises(TypeError, t.strftime, '%Z')
1252
Tim Peters2a799bf2002-12-16 20:18:38 +00001253 def test_bad_constructor_arguments(self):
1254 # bad years
1255 self.theclass(MINYEAR, 1, 1) # no exception
1256 self.theclass(MAXYEAR, 1, 1) # no exception
1257 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1258 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1259 # bad months
1260 self.theclass(2000, 1, 1) # no exception
1261 self.theclass(2000, 12, 1) # no exception
1262 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1263 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1264 # bad days
1265 self.theclass(2000, 2, 29) # no exception
1266 self.theclass(2004, 2, 29) # no exception
1267 self.theclass(2400, 2, 29) # no exception
1268 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1269 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1270 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1271 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1272 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1273 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1274 # bad hours
1275 self.theclass(2000, 1, 31, 0) # no exception
1276 self.theclass(2000, 1, 31, 23) # no exception
1277 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1278 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1279 # bad minutes
1280 self.theclass(2000, 1, 31, 23, 0) # no exception
1281 self.theclass(2000, 1, 31, 23, 59) # no exception
1282 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1283 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1284 # bad seconds
1285 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1286 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1287 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1288 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1289 # bad microseconds
1290 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1291 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1292 self.assertRaises(ValueError, self.theclass,
1293 2000, 1, 31, 23, 59, 59, -1)
1294 self.assertRaises(ValueError, self.theclass,
1295 2000, 1, 31, 23, 59, 59,
1296 1000000)
1297
1298 def test_hash_equality(self):
1299 d = self.theclass(2000, 12, 31, 23, 30, 17)
1300 e = self.theclass(2000, 12, 31, 23, 30, 17)
1301 self.assertEqual(d, e)
1302 self.assertEqual(hash(d), hash(e))
1303
1304 dic = {d: 1}
1305 dic[e] = 2
1306 self.assertEqual(len(dic), 1)
1307 self.assertEqual(dic[d], 2)
1308 self.assertEqual(dic[e], 2)
1309
1310 d = self.theclass(2001, 1, 1, 0, 5, 17)
1311 e = self.theclass(2001, 1, 1, 0, 5, 17)
1312 self.assertEqual(d, e)
1313 self.assertEqual(hash(d), hash(e))
1314
1315 dic = {d: 1}
1316 dic[e] = 2
1317 self.assertEqual(len(dic), 1)
1318 self.assertEqual(dic[d], 2)
1319 self.assertEqual(dic[e], 2)
1320
1321 def test_computations(self):
1322 a = self.theclass(2002, 1, 31)
1323 b = self.theclass(1956, 1, 31)
1324 diff = a-b
1325 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1326 self.assertEqual(diff.seconds, 0)
1327 self.assertEqual(diff.microseconds, 0)
1328 a = self.theclass(2002, 3, 2, 17, 6)
1329 millisec = timedelta(0, 0, 1000)
1330 hour = timedelta(0, 3600)
1331 day = timedelta(1)
1332 week = timedelta(7)
1333 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1334 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1335 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1336 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1337 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1338 self.assertEqual(a - hour, a + -hour)
1339 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1340 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1341 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1342 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1343 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1344 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1345 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1346 self.assertEqual((a + week) - a, week)
1347 self.assertEqual((a + day) - a, day)
1348 self.assertEqual((a + hour) - a, hour)
1349 self.assertEqual((a + millisec) - a, millisec)
1350 self.assertEqual((a - week) - a, -week)
1351 self.assertEqual((a - day) - a, -day)
1352 self.assertEqual((a - hour) - a, -hour)
1353 self.assertEqual((a - millisec) - a, -millisec)
1354 self.assertEqual(a - (a + week), -week)
1355 self.assertEqual(a - (a + day), -day)
1356 self.assertEqual(a - (a + hour), -hour)
1357 self.assertEqual(a - (a + millisec), -millisec)
1358 self.assertEqual(a - (a - week), week)
1359 self.assertEqual(a - (a - day), day)
1360 self.assertEqual(a - (a - hour), hour)
1361 self.assertEqual(a - (a - millisec), millisec)
1362 self.assertEqual(a + (week + day + hour + millisec),
1363 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1364 self.assertEqual(a + (week + day + hour + millisec),
1365 (((a + week) + day) + hour) + millisec)
1366 self.assertEqual(a - (week + day + hour + millisec),
1367 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1368 self.assertEqual(a - (week + day + hour + millisec),
1369 (((a - week) - day) - hour) - millisec)
1370 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001371 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001372 self.assertRaises(TypeError, lambda: a+i)
1373 self.assertRaises(TypeError, lambda: a-i)
1374 self.assertRaises(TypeError, lambda: i+a)
1375 self.assertRaises(TypeError, lambda: i-a)
1376
1377 # delta - datetime is senseless.
1378 self.assertRaises(TypeError, lambda: day - a)
1379 # mixing datetime and (delta or datetime) via * or // is senseless
1380 self.assertRaises(TypeError, lambda: day * a)
1381 self.assertRaises(TypeError, lambda: a * day)
1382 self.assertRaises(TypeError, lambda: day // a)
1383 self.assertRaises(TypeError, lambda: a // day)
1384 self.assertRaises(TypeError, lambda: a * a)
1385 self.assertRaises(TypeError, lambda: a // a)
1386 # datetime + datetime is senseless
1387 self.assertRaises(TypeError, lambda: a + a)
1388
1389 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001390 args = 6, 7, 23, 20, 59, 1, 64**2
1391 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001392 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001393 green = pickler.dumps(orig, proto)
1394 derived = unpickler.loads(green)
1395 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001396
Guido van Rossum275666f2003-02-07 21:49:01 +00001397 def test_more_pickling(self):
1398 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1399 s = pickle.dumps(a)
1400 b = pickle.loads(s)
1401 self.assertEqual(b.year, 2003)
1402 self.assertEqual(b.month, 2)
1403 self.assertEqual(b.day, 7)
1404
Tim Peters604c0132004-06-07 23:04:33 +00001405 def test_pickling_subclass_datetime(self):
1406 args = 6, 7, 23, 20, 59, 1, 64**2
1407 orig = SubclassDatetime(*args)
1408 for pickler, unpickler, proto in pickle_choices:
1409 green = pickler.dumps(orig, proto)
1410 derived = unpickler.loads(green)
1411 self.assertEqual(orig, derived)
1412
Tim Peters2a799bf2002-12-16 20:18:38 +00001413 def test_more_compare(self):
1414 # The test_compare() inherited from TestDate covers the error cases.
1415 # We just want to test lexicographic ordering on the members datetime
1416 # has that date lacks.
1417 args = [2000, 11, 29, 20, 58, 16, 999998]
1418 t1 = self.theclass(*args)
1419 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001420 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001421 self.failUnless(t1 <= t2)
1422 self.failUnless(t1 >= t2)
1423 self.failUnless(not t1 != t2)
1424 self.failUnless(not t1 < t2)
1425 self.failUnless(not t1 > t2)
1426 self.assertEqual(cmp(t1, t2), 0)
1427 self.assertEqual(cmp(t2, t1), 0)
1428
1429 for i in range(len(args)):
1430 newargs = args[:]
1431 newargs[i] = args[i] + 1
1432 t2 = self.theclass(*newargs) # this is larger than t1
1433 self.failUnless(t1 < t2)
1434 self.failUnless(t2 > t1)
1435 self.failUnless(t1 <= t2)
1436 self.failUnless(t2 >= t1)
1437 self.failUnless(t1 != t2)
1438 self.failUnless(t2 != t1)
1439 self.failUnless(not t1 == t2)
1440 self.failUnless(not t2 == t1)
1441 self.failUnless(not t1 > t2)
1442 self.failUnless(not t2 < t1)
1443 self.failUnless(not t1 >= t2)
1444 self.failUnless(not t2 <= t1)
1445 self.assertEqual(cmp(t1, t2), -1)
1446 self.assertEqual(cmp(t2, t1), 1)
1447
1448
1449 # A helper for timestamp constructor tests.
1450 def verify_field_equality(self, expected, got):
1451 self.assertEqual(expected.tm_year, got.year)
1452 self.assertEqual(expected.tm_mon, got.month)
1453 self.assertEqual(expected.tm_mday, got.day)
1454 self.assertEqual(expected.tm_hour, got.hour)
1455 self.assertEqual(expected.tm_min, got.minute)
1456 self.assertEqual(expected.tm_sec, got.second)
1457
1458 def test_fromtimestamp(self):
1459 import time
1460
1461 ts = time.time()
1462 expected = time.localtime(ts)
1463 got = self.theclass.fromtimestamp(ts)
1464 self.verify_field_equality(expected, got)
1465
1466 def test_utcfromtimestamp(self):
1467 import time
1468
1469 ts = time.time()
1470 expected = time.gmtime(ts)
1471 got = self.theclass.utcfromtimestamp(ts)
1472 self.verify_field_equality(expected, got)
1473
Thomas Wouters477c8d52006-05-27 19:21:47 +00001474 def test_microsecond_rounding(self):
1475 # Test whether fromtimestamp "rounds up" floats that are less
1476 # than one microsecond smaller than an integer.
1477 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1478 self.theclass.fromtimestamp(1))
1479
Tim Peters1b6f7a92004-06-20 02:50:16 +00001480 def test_insane_fromtimestamp(self):
1481 # It's possible that some platform maps time_t to double,
1482 # and that this test will fail there. This test should
1483 # exempt such platforms (provided they return reasonable
1484 # results!).
1485 for insane in -1e200, 1e200:
1486 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1487 insane)
1488
1489 def test_insane_utcfromtimestamp(self):
1490 # It's possible that some platform maps time_t to double,
1491 # and that this test will fail there. This test should
1492 # exempt such platforms (provided they return reasonable
1493 # results!).
1494 for insane in -1e200, 1e200:
1495 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1496 insane)
1497
Guido van Rossumd8faa362007-04-27 19:54:29 +00001498 def test_negative_float_fromtimestamp(self):
1499 # Windows doesn't accept negative timestamps
1500 if os.name == "nt":
1501 return
1502 # The result is tz-dependent; at least test that this doesn't
1503 # fail (like it did before bug 1646728 was fixed).
1504 self.theclass.fromtimestamp(-1.05)
1505
1506 def test_negative_float_utcfromtimestamp(self):
1507 # Windows doesn't accept negative timestamps
1508 if os.name == "nt":
1509 return
1510 d = self.theclass.utcfromtimestamp(-1.05)
1511 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1512
Tim Peters2a799bf2002-12-16 20:18:38 +00001513 def test_utcnow(self):
1514 import time
1515
1516 # Call it a success if utcnow() and utcfromtimestamp() are within
1517 # a second of each other.
1518 tolerance = timedelta(seconds=1)
1519 for dummy in range(3):
1520 from_now = self.theclass.utcnow()
1521 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1522 if abs(from_timestamp - from_now) <= tolerance:
1523 break
1524 # Else try again a few times.
1525 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1526
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001527 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001528 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001529
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001530 string = '2004-12-01 13:02:47.197'
1531 format = '%Y-%m-%d %H:%M:%S.%f'
1532 result, frac = _strptime._strptime(string, format)
1533 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001534 got = self.theclass.strptime(string, format)
1535 self.assertEqual(expected, got)
1536
Tim Peters2a799bf2002-12-16 20:18:38 +00001537 def test_more_timetuple(self):
1538 # This tests fields beyond those tested by the TestDate.test_timetuple.
1539 t = self.theclass(2004, 12, 31, 6, 22, 33)
1540 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1541 self.assertEqual(t.timetuple(),
1542 (t.year, t.month, t.day,
1543 t.hour, t.minute, t.second,
1544 t.weekday(),
1545 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1546 -1))
1547 tt = t.timetuple()
1548 self.assertEqual(tt.tm_year, t.year)
1549 self.assertEqual(tt.tm_mon, t.month)
1550 self.assertEqual(tt.tm_mday, t.day)
1551 self.assertEqual(tt.tm_hour, t.hour)
1552 self.assertEqual(tt.tm_min, t.minute)
1553 self.assertEqual(tt.tm_sec, t.second)
1554 self.assertEqual(tt.tm_wday, t.weekday())
1555 self.assertEqual(tt.tm_yday, t.toordinal() -
1556 date(t.year, 1, 1).toordinal() + 1)
1557 self.assertEqual(tt.tm_isdst, -1)
1558
1559 def test_more_strftime(self):
1560 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001561 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1562 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1563 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001564
1565 def test_extract(self):
1566 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1567 self.assertEqual(dt.date(), date(2002, 3, 4))
1568 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1569
1570 def test_combine(self):
1571 d = date(2002, 3, 4)
1572 t = time(18, 45, 3, 1234)
1573 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1574 combine = self.theclass.combine
1575 dt = combine(d, t)
1576 self.assertEqual(dt, expected)
1577
1578 dt = combine(time=t, date=d)
1579 self.assertEqual(dt, expected)
1580
1581 self.assertEqual(d, dt.date())
1582 self.assertEqual(t, dt.time())
1583 self.assertEqual(dt, combine(dt.date(), dt.time()))
1584
1585 self.assertRaises(TypeError, combine) # need an arg
1586 self.assertRaises(TypeError, combine, d) # need two args
1587 self.assertRaises(TypeError, combine, t, d) # args reversed
1588 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1589 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1590
Tim Peters12bf3392002-12-24 05:41:27 +00001591 def test_replace(self):
1592 cls = self.theclass
1593 args = [1, 2, 3, 4, 5, 6, 7]
1594 base = cls(*args)
1595 self.assertEqual(base, base.replace())
1596
1597 i = 0
1598 for name, newval in (("year", 2),
1599 ("month", 3),
1600 ("day", 4),
1601 ("hour", 5),
1602 ("minute", 6),
1603 ("second", 7),
1604 ("microsecond", 8)):
1605 newargs = args[:]
1606 newargs[i] = newval
1607 expected = cls(*newargs)
1608 got = base.replace(**{name: newval})
1609 self.assertEqual(expected, got)
1610 i += 1
1611
1612 # Out of bounds.
1613 base = cls(2000, 2, 29)
1614 self.assertRaises(ValueError, base.replace, year=2001)
1615
Tim Peters80475bb2002-12-25 07:40:55 +00001616 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001617 # Pretty boring! The TZ test is more interesting here. astimezone()
1618 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001619 dt = self.theclass.now()
1620 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001621 self.assertRaises(TypeError, dt.astimezone) # not enough args
1622 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1623 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001624 self.assertRaises(ValueError, dt.astimezone, f) # naive
1625 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001626
Tim Peters52dcce22003-01-23 16:36:11 +00001627 class Bogus(tzinfo):
1628 def utcoffset(self, dt): return None
1629 def dst(self, dt): return timedelta(0)
1630 bog = Bogus()
1631 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1632
1633 class AlsoBogus(tzinfo):
1634 def utcoffset(self, dt): return timedelta(0)
1635 def dst(self, dt): return None
1636 alsobog = AlsoBogus()
1637 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001638
Tim Petersa98924a2003-05-17 05:55:19 +00001639 def test_subclass_datetime(self):
1640
1641 class C(self.theclass):
1642 theAnswer = 42
1643
1644 def __new__(cls, *args, **kws):
1645 temp = kws.copy()
1646 extra = temp.pop('extra')
1647 result = self.theclass.__new__(cls, *args, **temp)
1648 result.extra = extra
1649 return result
1650
1651 def newmeth(self, start):
1652 return start + self.year + self.month + self.second
1653
1654 args = 2003, 4, 14, 12, 13, 41
1655
1656 dt1 = self.theclass(*args)
1657 dt2 = C(*args, **{'extra': 7})
1658
1659 self.assertEqual(dt2.__class__, C)
1660 self.assertEqual(dt2.theAnswer, 42)
1661 self.assertEqual(dt2.extra, 7)
1662 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1663 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1664 dt1.second - 7)
1665
Tim Peters604c0132004-06-07 23:04:33 +00001666class SubclassTime(time):
1667 sub_var = 1
1668
Guido van Rossumd8faa362007-04-27 19:54:29 +00001669class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001670
1671 theclass = time
1672
1673 def test_basic_attributes(self):
1674 t = self.theclass(12, 0)
1675 self.assertEqual(t.hour, 12)
1676 self.assertEqual(t.minute, 0)
1677 self.assertEqual(t.second, 0)
1678 self.assertEqual(t.microsecond, 0)
1679
1680 def test_basic_attributes_nonzero(self):
1681 # Make sure all attributes are non-zero so bugs in
1682 # bit-shifting access show up.
1683 t = self.theclass(12, 59, 59, 8000)
1684 self.assertEqual(t.hour, 12)
1685 self.assertEqual(t.minute, 59)
1686 self.assertEqual(t.second, 59)
1687 self.assertEqual(t.microsecond, 8000)
1688
1689 def test_roundtrip(self):
1690 t = self.theclass(1, 2, 3, 4)
1691
1692 # Verify t -> string -> time identity.
1693 s = repr(t)
1694 self.failUnless(s.startswith('datetime.'))
1695 s = s[9:]
1696 t2 = eval(s)
1697 self.assertEqual(t, t2)
1698
1699 # Verify identity via reconstructing from pieces.
1700 t2 = self.theclass(t.hour, t.minute, t.second,
1701 t.microsecond)
1702 self.assertEqual(t, t2)
1703
1704 def test_comparing(self):
1705 args = [1, 2, 3, 4]
1706 t1 = self.theclass(*args)
1707 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001708 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001709 self.failUnless(t1 <= t2)
1710 self.failUnless(t1 >= t2)
1711 self.failUnless(not t1 != t2)
1712 self.failUnless(not t1 < t2)
1713 self.failUnless(not t1 > t2)
1714 self.assertEqual(cmp(t1, t2), 0)
1715 self.assertEqual(cmp(t2, t1), 0)
1716
1717 for i in range(len(args)):
1718 newargs = args[:]
1719 newargs[i] = args[i] + 1
1720 t2 = self.theclass(*newargs) # this is larger than t1
1721 self.failUnless(t1 < t2)
1722 self.failUnless(t2 > t1)
1723 self.failUnless(t1 <= t2)
1724 self.failUnless(t2 >= t1)
1725 self.failUnless(t1 != t2)
1726 self.failUnless(t2 != t1)
1727 self.failUnless(not t1 == t2)
1728 self.failUnless(not t2 == t1)
1729 self.failUnless(not t1 > t2)
1730 self.failUnless(not t2 < t1)
1731 self.failUnless(not t1 >= t2)
1732 self.failUnless(not t2 <= t1)
1733 self.assertEqual(cmp(t1, t2), -1)
1734 self.assertEqual(cmp(t2, t1), 1)
1735
Tim Peters68124bb2003-02-08 03:46:31 +00001736 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001737 self.assertEqual(t1 == badarg, False)
1738 self.assertEqual(t1 != badarg, True)
1739 self.assertEqual(badarg == t1, False)
1740 self.assertEqual(badarg != t1, True)
1741
Tim Peters2a799bf2002-12-16 20:18:38 +00001742 self.assertRaises(TypeError, lambda: t1 <= badarg)
1743 self.assertRaises(TypeError, lambda: t1 < badarg)
1744 self.assertRaises(TypeError, lambda: t1 > badarg)
1745 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001746 self.assertRaises(TypeError, lambda: badarg <= t1)
1747 self.assertRaises(TypeError, lambda: badarg < t1)
1748 self.assertRaises(TypeError, lambda: badarg > t1)
1749 self.assertRaises(TypeError, lambda: badarg >= t1)
1750
1751 def test_bad_constructor_arguments(self):
1752 # bad hours
1753 self.theclass(0, 0) # no exception
1754 self.theclass(23, 0) # no exception
1755 self.assertRaises(ValueError, self.theclass, -1, 0)
1756 self.assertRaises(ValueError, self.theclass, 24, 0)
1757 # bad minutes
1758 self.theclass(23, 0) # no exception
1759 self.theclass(23, 59) # no exception
1760 self.assertRaises(ValueError, self.theclass, 23, -1)
1761 self.assertRaises(ValueError, self.theclass, 23, 60)
1762 # bad seconds
1763 self.theclass(23, 59, 0) # no exception
1764 self.theclass(23, 59, 59) # no exception
1765 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1766 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1767 # bad microseconds
1768 self.theclass(23, 59, 59, 0) # no exception
1769 self.theclass(23, 59, 59, 999999) # no exception
1770 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1771 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1772
1773 def test_hash_equality(self):
1774 d = self.theclass(23, 30, 17)
1775 e = self.theclass(23, 30, 17)
1776 self.assertEqual(d, e)
1777 self.assertEqual(hash(d), hash(e))
1778
1779 dic = {d: 1}
1780 dic[e] = 2
1781 self.assertEqual(len(dic), 1)
1782 self.assertEqual(dic[d], 2)
1783 self.assertEqual(dic[e], 2)
1784
1785 d = self.theclass(0, 5, 17)
1786 e = self.theclass(0, 5, 17)
1787 self.assertEqual(d, e)
1788 self.assertEqual(hash(d), hash(e))
1789
1790 dic = {d: 1}
1791 dic[e] = 2
1792 self.assertEqual(len(dic), 1)
1793 self.assertEqual(dic[d], 2)
1794 self.assertEqual(dic[e], 2)
1795
1796 def test_isoformat(self):
1797 t = self.theclass(4, 5, 1, 123)
1798 self.assertEqual(t.isoformat(), "04:05:01.000123")
1799 self.assertEqual(t.isoformat(), str(t))
1800
1801 t = self.theclass()
1802 self.assertEqual(t.isoformat(), "00:00:00")
1803 self.assertEqual(t.isoformat(), str(t))
1804
1805 t = self.theclass(microsecond=1)
1806 self.assertEqual(t.isoformat(), "00:00:00.000001")
1807 self.assertEqual(t.isoformat(), str(t))
1808
1809 t = self.theclass(microsecond=10)
1810 self.assertEqual(t.isoformat(), "00:00:00.000010")
1811 self.assertEqual(t.isoformat(), str(t))
1812
1813 t = self.theclass(microsecond=100)
1814 self.assertEqual(t.isoformat(), "00:00:00.000100")
1815 self.assertEqual(t.isoformat(), str(t))
1816
1817 t = self.theclass(microsecond=1000)
1818 self.assertEqual(t.isoformat(), "00:00:00.001000")
1819 self.assertEqual(t.isoformat(), str(t))
1820
1821 t = self.theclass(microsecond=10000)
1822 self.assertEqual(t.isoformat(), "00:00:00.010000")
1823 self.assertEqual(t.isoformat(), str(t))
1824
1825 t = self.theclass(microsecond=100000)
1826 self.assertEqual(t.isoformat(), "00:00:00.100000")
1827 self.assertEqual(t.isoformat(), str(t))
1828
Thomas Wouterscf297e42007-02-23 15:07:44 +00001829 def test_1653736(self):
1830 # verify it doesn't accept extra keyword arguments
1831 t = self.theclass(second=1)
1832 self.assertRaises(TypeError, t.isoformat, foo=3)
1833
Tim Peters2a799bf2002-12-16 20:18:38 +00001834 def test_strftime(self):
1835 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001836 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001837 # A naive object replaces %z and %Z with empty strings.
1838 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1839
Eric Smith1ba31142007-09-11 18:06:02 +00001840 def test_format(self):
1841 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001842 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001843
1844 # check that a derived class's __str__() gets called
1845 class A(self.theclass):
1846 def __str__(self):
1847 return 'A'
1848 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001849 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001850
1851 # check that a derived class's strftime gets called
1852 class B(self.theclass):
1853 def strftime(self, format_spec):
1854 return 'B'
1855 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001856 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001857
1858 for fmt in ['%H %M %S',
1859 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001860 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1861 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1862 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001863
Tim Peters2a799bf2002-12-16 20:18:38 +00001864 def test_str(self):
1865 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1866 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1867 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1868 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1869 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1870
1871 def test_repr(self):
1872 name = 'datetime.' + self.theclass.__name__
1873 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1874 "%s(1, 2, 3, 4)" % name)
1875 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1876 "%s(10, 2, 3, 4000)" % name)
1877 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1878 "%s(0, 2, 3, 400000)" % name)
1879 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1880 "%s(12, 2, 3)" % name)
1881 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1882 "%s(23, 15)" % name)
1883
1884 def test_resolution_info(self):
1885 self.assert_(isinstance(self.theclass.min, self.theclass))
1886 self.assert_(isinstance(self.theclass.max, self.theclass))
1887 self.assert_(isinstance(self.theclass.resolution, timedelta))
1888 self.assert_(self.theclass.max > self.theclass.min)
1889
1890 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001891 args = 20, 59, 16, 64**2
1892 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001893 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001894 green = pickler.dumps(orig, proto)
1895 derived = unpickler.loads(green)
1896 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001897
Tim Peters604c0132004-06-07 23:04:33 +00001898 def test_pickling_subclass_time(self):
1899 args = 20, 59, 16, 64**2
1900 orig = SubclassTime(*args)
1901 for pickler, unpickler, proto in pickle_choices:
1902 green = pickler.dumps(orig, proto)
1903 derived = unpickler.loads(green)
1904 self.assertEqual(orig, derived)
1905
Tim Peters2a799bf2002-12-16 20:18:38 +00001906 def test_bool(self):
1907 cls = self.theclass
1908 self.failUnless(cls(1))
1909 self.failUnless(cls(0, 1))
1910 self.failUnless(cls(0, 0, 1))
1911 self.failUnless(cls(0, 0, 0, 1))
1912 self.failUnless(not cls(0))
1913 self.failUnless(not cls())
1914
Tim Peters12bf3392002-12-24 05:41:27 +00001915 def test_replace(self):
1916 cls = self.theclass
1917 args = [1, 2, 3, 4]
1918 base = cls(*args)
1919 self.assertEqual(base, base.replace())
1920
1921 i = 0
1922 for name, newval in (("hour", 5),
1923 ("minute", 6),
1924 ("second", 7),
1925 ("microsecond", 8)):
1926 newargs = args[:]
1927 newargs[i] = newval
1928 expected = cls(*newargs)
1929 got = base.replace(**{name: newval})
1930 self.assertEqual(expected, got)
1931 i += 1
1932
1933 # Out of bounds.
1934 base = cls(1)
1935 self.assertRaises(ValueError, base.replace, hour=24)
1936 self.assertRaises(ValueError, base.replace, minute=-1)
1937 self.assertRaises(ValueError, base.replace, second=100)
1938 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1939
Tim Petersa98924a2003-05-17 05:55:19 +00001940 def test_subclass_time(self):
1941
1942 class C(self.theclass):
1943 theAnswer = 42
1944
1945 def __new__(cls, *args, **kws):
1946 temp = kws.copy()
1947 extra = temp.pop('extra')
1948 result = self.theclass.__new__(cls, *args, **temp)
1949 result.extra = extra
1950 return result
1951
1952 def newmeth(self, start):
1953 return start + self.hour + self.second
1954
1955 args = 4, 5, 6
1956
1957 dt1 = self.theclass(*args)
1958 dt2 = C(*args, **{'extra': 7})
1959
1960 self.assertEqual(dt2.__class__, C)
1961 self.assertEqual(dt2.theAnswer, 42)
1962 self.assertEqual(dt2.extra, 7)
1963 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1964 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1965
Armin Rigof4afb212005-11-07 07:15:48 +00001966 def test_backdoor_resistance(self):
1967 # see TestDate.test_backdoor_resistance().
1968 base = '2:59.0'
1969 for hour_byte in ' ', '9', chr(24), '\xff':
1970 self.assertRaises(TypeError, self.theclass,
1971 hour_byte + base[1:])
1972
Tim Peters855fe882002-12-22 03:43:39 +00001973# A mixin for classes with a tzinfo= argument. Subclasses must define
1974# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001975# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001976class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001977
Tim Petersbad8ff02002-12-30 20:52:32 +00001978 def test_argument_passing(self):
1979 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001980 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001981 class introspective(tzinfo):
1982 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001983 def utcoffset(self, dt):
1984 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001985 dst = utcoffset
1986
1987 obj = cls(1, 2, 3, tzinfo=introspective())
1988
Tim Peters0bf60bd2003-01-08 20:40:01 +00001989 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001990 self.assertEqual(obj.tzname(), expected)
1991
Tim Peters0bf60bd2003-01-08 20:40:01 +00001992 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001993 self.assertEqual(obj.utcoffset(), expected)
1994 self.assertEqual(obj.dst(), expected)
1995
Tim Peters855fe882002-12-22 03:43:39 +00001996 def test_bad_tzinfo_classes(self):
1997 cls = self.theclass
1998 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001999
Tim Peters855fe882002-12-22 03:43:39 +00002000 class NiceTry(object):
2001 def __init__(self): pass
2002 def utcoffset(self, dt): pass
2003 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2004
2005 class BetterTry(tzinfo):
2006 def __init__(self): pass
2007 def utcoffset(self, dt): pass
2008 b = BetterTry()
2009 t = cls(1, 1, 1, tzinfo=b)
2010 self.failUnless(t.tzinfo is b)
2011
2012 def test_utc_offset_out_of_bounds(self):
2013 class Edgy(tzinfo):
2014 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002015 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002016 def utcoffset(self, dt):
2017 return self.offset
2018
2019 cls = self.theclass
2020 for offset, legit in ((-1440, False),
2021 (-1439, True),
2022 (1439, True),
2023 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002024 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002025 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002026 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002027 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002028 else:
2029 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002030 if legit:
2031 aofs = abs(offset)
2032 h, m = divmod(aofs, 60)
2033 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002034 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002035 t = t.timetz()
2036 self.assertEqual(str(t), "01:02:03" + tag)
2037 else:
2038 self.assertRaises(ValueError, str, t)
2039
2040 def test_tzinfo_classes(self):
2041 cls = self.theclass
2042 class C1(tzinfo):
2043 def utcoffset(self, dt): return None
2044 def dst(self, dt): return None
2045 def tzname(self, dt): return None
2046 for t in (cls(1, 1, 1),
2047 cls(1, 1, 1, tzinfo=None),
2048 cls(1, 1, 1, tzinfo=C1())):
2049 self.failUnless(t.utcoffset() is None)
2050 self.failUnless(t.dst() is None)
2051 self.failUnless(t.tzname() is None)
2052
Tim Peters855fe882002-12-22 03:43:39 +00002053 class C3(tzinfo):
2054 def utcoffset(self, dt): return timedelta(minutes=-1439)
2055 def dst(self, dt): return timedelta(minutes=1439)
2056 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002057 t = cls(1, 1, 1, tzinfo=C3())
2058 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2059 self.assertEqual(t.dst(), timedelta(minutes=1439))
2060 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002061
2062 # Wrong types.
2063 class C4(tzinfo):
2064 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002065 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002066 def tzname(self, dt): return 0
2067 t = cls(1, 1, 1, tzinfo=C4())
2068 self.assertRaises(TypeError, t.utcoffset)
2069 self.assertRaises(TypeError, t.dst)
2070 self.assertRaises(TypeError, t.tzname)
2071
2072 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002073 class C6(tzinfo):
2074 def utcoffset(self, dt): return timedelta(hours=-24)
2075 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002076 t = cls(1, 1, 1, tzinfo=C6())
2077 self.assertRaises(ValueError, t.utcoffset)
2078 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002079
2080 # Not a whole number of minutes.
2081 class C7(tzinfo):
2082 def utcoffset(self, dt): return timedelta(seconds=61)
2083 def dst(self, dt): return timedelta(microseconds=-81)
2084 t = cls(1, 1, 1, tzinfo=C7())
2085 self.assertRaises(ValueError, t.utcoffset)
2086 self.assertRaises(ValueError, t.dst)
2087
Tim Peters4c0db782002-12-26 05:01:19 +00002088 def test_aware_compare(self):
2089 cls = self.theclass
2090
Tim Peters60c76e42002-12-27 00:41:11 +00002091 # Ensure that utcoffset() gets ignored if the comparands have
2092 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002093 class OperandDependentOffset(tzinfo):
2094 def utcoffset(self, t):
2095 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002096 # d0 and d1 equal after adjustment
2097 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002098 else:
Tim Peters397301e2003-01-02 21:28:08 +00002099 # d2 off in the weeds
2100 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002101
2102 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2103 d0 = base.replace(minute=3)
2104 d1 = base.replace(minute=9)
2105 d2 = base.replace(minute=11)
2106 for x in d0, d1, d2:
2107 for y in d0, d1, d2:
2108 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002109 expected = cmp(x.minute, y.minute)
2110 self.assertEqual(got, expected)
2111
2112 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002113 # Note that a time can't actually have an operand-depedent offset,
2114 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2115 # so skip this test for time.
2116 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002117 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2118 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2119 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2120 for x in d0, d1, d2:
2121 for y in d0, d1, d2:
2122 got = cmp(x, y)
2123 if (x is d0 or x is d1) and (y is d0 or y is d1):
2124 expected = 0
2125 elif x is y is d2:
2126 expected = 0
2127 elif x is d2:
2128 expected = -1
2129 else:
2130 assert y is d2
2131 expected = 1
2132 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002133
Tim Peters855fe882002-12-22 03:43:39 +00002134
Tim Peters0bf60bd2003-01-08 20:40:01 +00002135# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002136class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002137 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002138
2139 def test_empty(self):
2140 t = self.theclass()
2141 self.assertEqual(t.hour, 0)
2142 self.assertEqual(t.minute, 0)
2143 self.assertEqual(t.second, 0)
2144 self.assertEqual(t.microsecond, 0)
2145 self.failUnless(t.tzinfo is None)
2146
Tim Peters2a799bf2002-12-16 20:18:38 +00002147 def test_zones(self):
2148 est = FixedOffset(-300, "EST", 1)
2149 utc = FixedOffset(0, "UTC", -2)
2150 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002151 t1 = time( 7, 47, tzinfo=est)
2152 t2 = time(12, 47, tzinfo=utc)
2153 t3 = time(13, 47, tzinfo=met)
2154 t4 = time(microsecond=40)
2155 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002156
2157 self.assertEqual(t1.tzinfo, est)
2158 self.assertEqual(t2.tzinfo, utc)
2159 self.assertEqual(t3.tzinfo, met)
2160 self.failUnless(t4.tzinfo is None)
2161 self.assertEqual(t5.tzinfo, utc)
2162
Tim Peters855fe882002-12-22 03:43:39 +00002163 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2164 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2165 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002166 self.failUnless(t4.utcoffset() is None)
2167 self.assertRaises(TypeError, t1.utcoffset, "no args")
2168
2169 self.assertEqual(t1.tzname(), "EST")
2170 self.assertEqual(t2.tzname(), "UTC")
2171 self.assertEqual(t3.tzname(), "MET")
2172 self.failUnless(t4.tzname() is None)
2173 self.assertRaises(TypeError, t1.tzname, "no args")
2174
Tim Peters855fe882002-12-22 03:43:39 +00002175 self.assertEqual(t1.dst(), timedelta(minutes=1))
2176 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2177 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002178 self.failUnless(t4.dst() is None)
2179 self.assertRaises(TypeError, t1.dst, "no args")
2180
2181 self.assertEqual(hash(t1), hash(t2))
2182 self.assertEqual(hash(t1), hash(t3))
2183 self.assertEqual(hash(t2), hash(t3))
2184
2185 self.assertEqual(t1, t2)
2186 self.assertEqual(t1, t3)
2187 self.assertEqual(t2, t3)
2188 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2189 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2190 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2191
2192 self.assertEqual(str(t1), "07:47:00-05:00")
2193 self.assertEqual(str(t2), "12:47:00+00:00")
2194 self.assertEqual(str(t3), "13:47:00+01:00")
2195 self.assertEqual(str(t4), "00:00:00.000040")
2196 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2197
2198 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2199 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2200 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2201 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2202 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2203
Tim Peters0bf60bd2003-01-08 20:40:01 +00002204 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002205 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2206 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2207 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2208 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2209 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2210
2211 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2212 "07:47:00 %Z=EST %z=-0500")
2213 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2214 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2215
2216 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002217 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002218 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2219 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2220
Tim Petersb92bb712002-12-21 17:44:07 +00002221 # Check that an invalid tzname result raises an exception.
2222 class Badtzname(tzinfo):
2223 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002224 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002225 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2226 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002227
2228 def test_hash_edge_cases(self):
2229 # Offsets that overflow a basic time.
2230 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2231 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2232 self.assertEqual(hash(t1), hash(t2))
2233
2234 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2235 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2236 self.assertEqual(hash(t1), hash(t2))
2237
Tim Peters2a799bf2002-12-16 20:18:38 +00002238 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002239 # Try one without a tzinfo.
2240 args = 20, 59, 16, 64**2
2241 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002242 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002243 green = pickler.dumps(orig, proto)
2244 derived = unpickler.loads(green)
2245 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002246
2247 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002248 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002249 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002250 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002251 green = pickler.dumps(orig, proto)
2252 derived = unpickler.loads(green)
2253 self.assertEqual(orig, derived)
2254 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2255 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2256 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002257
2258 def test_more_bool(self):
2259 # Test cases with non-None tzinfo.
2260 cls = self.theclass
2261
2262 t = cls(0, tzinfo=FixedOffset(-300, ""))
2263 self.failUnless(t)
2264
2265 t = cls(5, tzinfo=FixedOffset(-300, ""))
2266 self.failUnless(t)
2267
2268 t = cls(5, tzinfo=FixedOffset(300, ""))
2269 self.failUnless(not t)
2270
2271 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2272 self.failUnless(not t)
2273
2274 # Mostly ensuring this doesn't overflow internally.
2275 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2276 self.failUnless(t)
2277
2278 # But this should yield a value error -- the utcoffset is bogus.
2279 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2280 self.assertRaises(ValueError, lambda: bool(t))
2281
2282 # Likewise.
2283 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2284 self.assertRaises(ValueError, lambda: bool(t))
2285
Tim Peters12bf3392002-12-24 05:41:27 +00002286 def test_replace(self):
2287 cls = self.theclass
2288 z100 = FixedOffset(100, "+100")
2289 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2290 args = [1, 2, 3, 4, z100]
2291 base = cls(*args)
2292 self.assertEqual(base, base.replace())
2293
2294 i = 0
2295 for name, newval in (("hour", 5),
2296 ("minute", 6),
2297 ("second", 7),
2298 ("microsecond", 8),
2299 ("tzinfo", zm200)):
2300 newargs = args[:]
2301 newargs[i] = newval
2302 expected = cls(*newargs)
2303 got = base.replace(**{name: newval})
2304 self.assertEqual(expected, got)
2305 i += 1
2306
2307 # Ensure we can get rid of a tzinfo.
2308 self.assertEqual(base.tzname(), "+100")
2309 base2 = base.replace(tzinfo=None)
2310 self.failUnless(base2.tzinfo is None)
2311 self.failUnless(base2.tzname() is None)
2312
2313 # Ensure we can add one.
2314 base3 = base2.replace(tzinfo=z100)
2315 self.assertEqual(base, base3)
2316 self.failUnless(base.tzinfo is base3.tzinfo)
2317
2318 # Out of bounds.
2319 base = cls(1)
2320 self.assertRaises(ValueError, base.replace, hour=24)
2321 self.assertRaises(ValueError, base.replace, minute=-1)
2322 self.assertRaises(ValueError, base.replace, second=100)
2323 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2324
Tim Peters60c76e42002-12-27 00:41:11 +00002325 def test_mixed_compare(self):
2326 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002327 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002328 self.assertEqual(t1, t2)
2329 t2 = t2.replace(tzinfo=None)
2330 self.assertEqual(t1, t2)
2331 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2332 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002333 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2334 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002335
Tim Peters0bf60bd2003-01-08 20:40:01 +00002336 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002337 class Varies(tzinfo):
2338 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002339 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002340 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002341 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002342 return self.offset
2343
2344 v = Varies()
2345 t1 = t2.replace(tzinfo=v)
2346 t2 = t2.replace(tzinfo=v)
2347 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2348 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2349 self.assertEqual(t1, t2)
2350
2351 # But if they're not identical, it isn't ignored.
2352 t2 = t2.replace(tzinfo=Varies())
2353 self.failUnless(t1 < t2) # t1's offset counter still going up
2354
Tim Petersa98924a2003-05-17 05:55:19 +00002355 def test_subclass_timetz(self):
2356
2357 class C(self.theclass):
2358 theAnswer = 42
2359
2360 def __new__(cls, *args, **kws):
2361 temp = kws.copy()
2362 extra = temp.pop('extra')
2363 result = self.theclass.__new__(cls, *args, **temp)
2364 result.extra = extra
2365 return result
2366
2367 def newmeth(self, start):
2368 return start + self.hour + self.second
2369
2370 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2371
2372 dt1 = self.theclass(*args)
2373 dt2 = C(*args, **{'extra': 7})
2374
2375 self.assertEqual(dt2.__class__, C)
2376 self.assertEqual(dt2.theAnswer, 42)
2377 self.assertEqual(dt2.extra, 7)
2378 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2379 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2380
Tim Peters4c0db782002-12-26 05:01:19 +00002381
Tim Peters0bf60bd2003-01-08 20:40:01 +00002382# Testing datetime objects with a non-None tzinfo.
2383
Guido van Rossumd8faa362007-04-27 19:54:29 +00002384class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002385 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002386
2387 def test_trivial(self):
2388 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2389 self.assertEqual(dt.year, 1)
2390 self.assertEqual(dt.month, 2)
2391 self.assertEqual(dt.day, 3)
2392 self.assertEqual(dt.hour, 4)
2393 self.assertEqual(dt.minute, 5)
2394 self.assertEqual(dt.second, 6)
2395 self.assertEqual(dt.microsecond, 7)
2396 self.assertEqual(dt.tzinfo, None)
2397
2398 def test_even_more_compare(self):
2399 # The test_compare() and test_more_compare() inherited from TestDate
2400 # and TestDateTime covered non-tzinfo cases.
2401
2402 # Smallest possible after UTC adjustment.
2403 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2404 # Largest possible after UTC adjustment.
2405 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2406 tzinfo=FixedOffset(-1439, ""))
2407
2408 # Make sure those compare correctly, and w/o overflow.
2409 self.failUnless(t1 < t2)
2410 self.failUnless(t1 != t2)
2411 self.failUnless(t2 > t1)
2412
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002413 self.assertEqual(t1, t1)
2414 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002415
2416 # Equal afer adjustment.
2417 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2418 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2419 self.assertEqual(t1, t2)
2420
2421 # Change t1 not to subtract a minute, and t1 should be larger.
2422 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2423 self.failUnless(t1 > t2)
2424
2425 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2426 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2427 self.failUnless(t1 < t2)
2428
2429 # Back to the original t1, but make seconds resolve it.
2430 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2431 second=1)
2432 self.failUnless(t1 > t2)
2433
2434 # Likewise, but make microseconds resolve it.
2435 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2436 microsecond=1)
2437 self.failUnless(t1 > t2)
2438
2439 # Make t2 naive and it should fail.
2440 t2 = self.theclass.min
2441 self.assertRaises(TypeError, lambda: t1 == t2)
2442 self.assertEqual(t2, t2)
2443
2444 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2445 class Naive(tzinfo):
2446 def utcoffset(self, dt): return None
2447 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2448 self.assertRaises(TypeError, lambda: t1 == t2)
2449 self.assertEqual(t2, t2)
2450
2451 # OTOH, it's OK to compare two of these mixing the two ways of being
2452 # naive.
2453 t1 = self.theclass(5, 6, 7)
2454 self.assertEqual(t1, t2)
2455
2456 # Try a bogus uctoffset.
2457 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002458 def utcoffset(self, dt):
2459 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002460 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2461 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002462 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002463
Tim Peters2a799bf2002-12-16 20:18:38 +00002464 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002465 # Try one without a tzinfo.
2466 args = 6, 7, 23, 20, 59, 1, 64**2
2467 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002468 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002469 green = pickler.dumps(orig, proto)
2470 derived = unpickler.loads(green)
2471 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002472
2473 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002474 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002475 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002476 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002477 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002478 green = pickler.dumps(orig, proto)
2479 derived = unpickler.loads(green)
2480 self.assertEqual(orig, derived)
2481 self.failUnless(isinstance(derived.tzinfo,
2482 PicklableFixedOffset))
2483 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2484 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002485
2486 def test_extreme_hashes(self):
2487 # If an attempt is made to hash these via subtracting the offset
2488 # then hashing a datetime object, OverflowError results. The
2489 # Python implementation used to blow up here.
2490 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2491 hash(t)
2492 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2493 tzinfo=FixedOffset(-1439, ""))
2494 hash(t)
2495
2496 # OTOH, an OOB offset should blow up.
2497 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2498 self.assertRaises(ValueError, hash, t)
2499
2500 def test_zones(self):
2501 est = FixedOffset(-300, "EST")
2502 utc = FixedOffset(0, "UTC")
2503 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002504 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2505 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2506 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002507 self.assertEqual(t1.tzinfo, est)
2508 self.assertEqual(t2.tzinfo, utc)
2509 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002510 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2511 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2512 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002513 self.assertEqual(t1.tzname(), "EST")
2514 self.assertEqual(t2.tzname(), "UTC")
2515 self.assertEqual(t3.tzname(), "MET")
2516 self.assertEqual(hash(t1), hash(t2))
2517 self.assertEqual(hash(t1), hash(t3))
2518 self.assertEqual(hash(t2), hash(t3))
2519 self.assertEqual(t1, t2)
2520 self.assertEqual(t1, t3)
2521 self.assertEqual(t2, t3)
2522 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2523 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2524 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002525 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002526 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2527 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2528 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2529
2530 def test_combine(self):
2531 met = FixedOffset(60, "MET")
2532 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002533 tz = time(18, 45, 3, 1234, tzinfo=met)
2534 dt = datetime.combine(d, tz)
2535 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002536 tzinfo=met))
2537
2538 def test_extract(self):
2539 met = FixedOffset(60, "MET")
2540 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2541 self.assertEqual(dt.date(), date(2002, 3, 4))
2542 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002543 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002544
2545 def test_tz_aware_arithmetic(self):
2546 import random
2547
2548 now = self.theclass.now()
2549 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002550 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002551 nowaware = self.theclass.combine(now.date(), timeaware)
2552 self.failUnless(nowaware.tzinfo is tz55)
2553 self.assertEqual(nowaware.timetz(), timeaware)
2554
2555 # Can't mix aware and non-aware.
2556 self.assertRaises(TypeError, lambda: now - nowaware)
2557 self.assertRaises(TypeError, lambda: nowaware - now)
2558
Tim Peters0bf60bd2003-01-08 20:40:01 +00002559 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002560 self.assertRaises(TypeError, lambda: now + nowaware)
2561 self.assertRaises(TypeError, lambda: nowaware + now)
2562 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2563
2564 # Subtracting should yield 0.
2565 self.assertEqual(now - now, timedelta(0))
2566 self.assertEqual(nowaware - nowaware, timedelta(0))
2567
2568 # Adding a delta should preserve tzinfo.
2569 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2570 nowawareplus = nowaware + delta
2571 self.failUnless(nowaware.tzinfo is tz55)
2572 nowawareplus2 = delta + nowaware
2573 self.failUnless(nowawareplus2.tzinfo is tz55)
2574 self.assertEqual(nowawareplus, nowawareplus2)
2575
2576 # that - delta should be what we started with, and that - what we
2577 # started with should be delta.
2578 diff = nowawareplus - delta
2579 self.failUnless(diff.tzinfo is tz55)
2580 self.assertEqual(nowaware, diff)
2581 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2582 self.assertEqual(nowawareplus - nowaware, delta)
2583
2584 # Make up a random timezone.
2585 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002586 # Attach it to nowawareplus.
2587 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002588 self.failUnless(nowawareplus.tzinfo is tzr)
2589 # Make sure the difference takes the timezone adjustments into account.
2590 got = nowaware - nowawareplus
2591 # Expected: (nowaware base - nowaware offset) -
2592 # (nowawareplus base - nowawareplus offset) =
2593 # (nowaware base - nowawareplus base) +
2594 # (nowawareplus offset - nowaware offset) =
2595 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002596 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002597 self.assertEqual(got, expected)
2598
2599 # Try max possible difference.
2600 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2601 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2602 tzinfo=FixedOffset(-1439, "max"))
2603 maxdiff = max - min
2604 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2605 timedelta(minutes=2*1439))
2606
2607 def test_tzinfo_now(self):
2608 meth = self.theclass.now
2609 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2610 base = meth()
2611 # Try with and without naming the keyword.
2612 off42 = FixedOffset(42, "42")
2613 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002614 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002615 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002616 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002617 # Bad argument with and w/o naming the keyword.
2618 self.assertRaises(TypeError, meth, 16)
2619 self.assertRaises(TypeError, meth, tzinfo=16)
2620 # Bad keyword name.
2621 self.assertRaises(TypeError, meth, tinfo=off42)
2622 # Too many args.
2623 self.assertRaises(TypeError, meth, off42, off42)
2624
Tim Peters10cadce2003-01-23 19:58:02 +00002625 # We don't know which time zone we're in, and don't have a tzinfo
2626 # class to represent it, so seeing whether a tz argument actually
2627 # does a conversion is tricky.
2628 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2629 utc = FixedOffset(0, "utc", 0)
2630 for dummy in range(3):
2631 now = datetime.now(weirdtz)
2632 self.failUnless(now.tzinfo is weirdtz)
2633 utcnow = datetime.utcnow().replace(tzinfo=utc)
2634 now2 = utcnow.astimezone(weirdtz)
2635 if abs(now - now2) < timedelta(seconds=30):
2636 break
2637 # Else the code is broken, or more than 30 seconds passed between
2638 # calls; assuming the latter, just try again.
2639 else:
2640 # Three strikes and we're out.
2641 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2642
Tim Peters2a799bf2002-12-16 20:18:38 +00002643 def test_tzinfo_fromtimestamp(self):
2644 import time
2645 meth = self.theclass.fromtimestamp
2646 ts = time.time()
2647 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2648 base = meth(ts)
2649 # Try with and without naming the keyword.
2650 off42 = FixedOffset(42, "42")
2651 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002652 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002653 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002654 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002655 # Bad argument with and w/o naming the keyword.
2656 self.assertRaises(TypeError, meth, ts, 16)
2657 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2658 # Bad keyword name.
2659 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2660 # Too many args.
2661 self.assertRaises(TypeError, meth, ts, off42, off42)
2662 # Too few args.
2663 self.assertRaises(TypeError, meth)
2664
Tim Peters2a44a8d2003-01-23 20:53:10 +00002665 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002666 timestamp = 1000000000
2667 utcdatetime = datetime.utcfromtimestamp(timestamp)
2668 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2669 # But on some flavor of Mac, it's nowhere near that. So we can't have
2670 # any idea here what time that actually is, we can only test that
2671 # relative changes match.
2672 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2673 tz = FixedOffset(utcoffset, "tz", 0)
2674 expected = utcdatetime + utcoffset
2675 got = datetime.fromtimestamp(timestamp, tz)
2676 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002677
Tim Peters2a799bf2002-12-16 20:18:38 +00002678 def test_tzinfo_utcnow(self):
2679 meth = self.theclass.utcnow
2680 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2681 base = meth()
2682 # Try with and without naming the keyword; for whatever reason,
2683 # utcnow() doesn't accept a tzinfo argument.
2684 off42 = FixedOffset(42, "42")
2685 self.assertRaises(TypeError, meth, off42)
2686 self.assertRaises(TypeError, meth, tzinfo=off42)
2687
2688 def test_tzinfo_utcfromtimestamp(self):
2689 import time
2690 meth = self.theclass.utcfromtimestamp
2691 ts = time.time()
2692 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2693 base = meth(ts)
2694 # Try with and without naming the keyword; for whatever reason,
2695 # utcfromtimestamp() doesn't accept a tzinfo argument.
2696 off42 = FixedOffset(42, "42")
2697 self.assertRaises(TypeError, meth, ts, off42)
2698 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2699
2700 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002701 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002702 # DST flag.
2703 class DST(tzinfo):
2704 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002705 if isinstance(dstvalue, int):
2706 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002707 self.dstvalue = dstvalue
2708 def dst(self, dt):
2709 return self.dstvalue
2710
2711 cls = self.theclass
2712 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2713 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2714 t = d.timetuple()
2715 self.assertEqual(1, t.tm_year)
2716 self.assertEqual(1, t.tm_mon)
2717 self.assertEqual(1, t.tm_mday)
2718 self.assertEqual(10, t.tm_hour)
2719 self.assertEqual(20, t.tm_min)
2720 self.assertEqual(30, t.tm_sec)
2721 self.assertEqual(0, t.tm_wday)
2722 self.assertEqual(1, t.tm_yday)
2723 self.assertEqual(flag, t.tm_isdst)
2724
2725 # dst() returns wrong type.
2726 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2727
2728 # dst() at the edge.
2729 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2730 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2731
2732 # dst() out of range.
2733 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2734 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2735
2736 def test_utctimetuple(self):
2737 class DST(tzinfo):
2738 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002739 if isinstance(dstvalue, int):
2740 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002741 self.dstvalue = dstvalue
2742 def dst(self, dt):
2743 return self.dstvalue
2744
2745 cls = self.theclass
2746 # This can't work: DST didn't implement utcoffset.
2747 self.assertRaises(NotImplementedError,
2748 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2749
2750 class UOFS(DST):
2751 def __init__(self, uofs, dofs=None):
2752 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002753 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002754 def utcoffset(self, dt):
2755 return self.uofs
2756
2757 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2758 # in effect for a UTC time.
2759 for dstvalue in -33, 33, 0, None:
2760 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2761 t = d.utctimetuple()
2762 self.assertEqual(d.year, t.tm_year)
2763 self.assertEqual(d.month, t.tm_mon)
2764 self.assertEqual(d.day, t.tm_mday)
2765 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2766 self.assertEqual(13, t.tm_min)
2767 self.assertEqual(d.second, t.tm_sec)
2768 self.assertEqual(d.weekday(), t.tm_wday)
2769 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2770 t.tm_yday)
2771 self.assertEqual(0, t.tm_isdst)
2772
2773 # At the edges, UTC adjustment can normalize into years out-of-range
2774 # for a datetime object. Ensure that a correct timetuple is
2775 # created anyway.
2776 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2777 # That goes back 1 minute less than a full day.
2778 t = tiny.utctimetuple()
2779 self.assertEqual(t.tm_year, MINYEAR-1)
2780 self.assertEqual(t.tm_mon, 12)
2781 self.assertEqual(t.tm_mday, 31)
2782 self.assertEqual(t.tm_hour, 0)
2783 self.assertEqual(t.tm_min, 1)
2784 self.assertEqual(t.tm_sec, 37)
2785 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2786 self.assertEqual(t.tm_isdst, 0)
2787
2788 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2789 # That goes forward 1 minute less than a full day.
2790 t = huge.utctimetuple()
2791 self.assertEqual(t.tm_year, MAXYEAR+1)
2792 self.assertEqual(t.tm_mon, 1)
2793 self.assertEqual(t.tm_mday, 1)
2794 self.assertEqual(t.tm_hour, 23)
2795 self.assertEqual(t.tm_min, 58)
2796 self.assertEqual(t.tm_sec, 37)
2797 self.assertEqual(t.tm_yday, 1)
2798 self.assertEqual(t.tm_isdst, 0)
2799
2800 def test_tzinfo_isoformat(self):
2801 zero = FixedOffset(0, "+00:00")
2802 plus = FixedOffset(220, "+03:40")
2803 minus = FixedOffset(-231, "-03:51")
2804 unknown = FixedOffset(None, "")
2805
2806 cls = self.theclass
2807 datestr = '0001-02-03'
2808 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002809 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002810 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2811 timestr = '04:05:59' + (us and '.987001' or '')
2812 ofsstr = ofs is not None and d.tzname() or ''
2813 tailstr = timestr + ofsstr
2814 iso = d.isoformat()
2815 self.assertEqual(iso, datestr + 'T' + tailstr)
2816 self.assertEqual(iso, d.isoformat('T'))
2817 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002818 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002819 self.assertEqual(str(d), datestr + ' ' + tailstr)
2820
Tim Peters12bf3392002-12-24 05:41:27 +00002821 def test_replace(self):
2822 cls = self.theclass
2823 z100 = FixedOffset(100, "+100")
2824 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2825 args = [1, 2, 3, 4, 5, 6, 7, z100]
2826 base = cls(*args)
2827 self.assertEqual(base, base.replace())
2828
2829 i = 0
2830 for name, newval in (("year", 2),
2831 ("month", 3),
2832 ("day", 4),
2833 ("hour", 5),
2834 ("minute", 6),
2835 ("second", 7),
2836 ("microsecond", 8),
2837 ("tzinfo", zm200)):
2838 newargs = args[:]
2839 newargs[i] = newval
2840 expected = cls(*newargs)
2841 got = base.replace(**{name: newval})
2842 self.assertEqual(expected, got)
2843 i += 1
2844
2845 # Ensure we can get rid of a tzinfo.
2846 self.assertEqual(base.tzname(), "+100")
2847 base2 = base.replace(tzinfo=None)
2848 self.failUnless(base2.tzinfo is None)
2849 self.failUnless(base2.tzname() is None)
2850
2851 # Ensure we can add one.
2852 base3 = base2.replace(tzinfo=z100)
2853 self.assertEqual(base, base3)
2854 self.failUnless(base.tzinfo is base3.tzinfo)
2855
2856 # Out of bounds.
2857 base = cls(2000, 2, 29)
2858 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002859
Tim Peters80475bb2002-12-25 07:40:55 +00002860 def test_more_astimezone(self):
2861 # The inherited test_astimezone covered some trivial and error cases.
2862 fnone = FixedOffset(None, "None")
2863 f44m = FixedOffset(44, "44")
2864 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2865
Tim Peters10cadce2003-01-23 19:58:02 +00002866 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002867 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002868 # Replacing with degenerate tzinfo raises an exception.
2869 self.assertRaises(ValueError, dt.astimezone, fnone)
2870 # Ditto with None tz.
2871 self.assertRaises(TypeError, dt.astimezone, None)
2872 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002873 x = dt.astimezone(dt.tzinfo)
2874 self.failUnless(x.tzinfo is f44m)
2875 self.assertEqual(x.date(), dt.date())
2876 self.assertEqual(x.time(), dt.time())
2877
2878 # Replacing with different tzinfo does adjust.
2879 got = dt.astimezone(fm5h)
2880 self.failUnless(got.tzinfo is fm5h)
2881 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2882 expected = dt - dt.utcoffset() # in effect, convert to UTC
2883 expected += fm5h.utcoffset(dt) # and from there to local time
2884 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2885 self.assertEqual(got.date(), expected.date())
2886 self.assertEqual(got.time(), expected.time())
2887 self.assertEqual(got.timetz(), expected.timetz())
2888 self.failUnless(got.tzinfo is expected.tzinfo)
2889 self.assertEqual(got, expected)
2890
Tim Peters4c0db782002-12-26 05:01:19 +00002891 def test_aware_subtract(self):
2892 cls = self.theclass
2893
Tim Peters60c76e42002-12-27 00:41:11 +00002894 # Ensure that utcoffset() is ignored when the operands have the
2895 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002896 class OperandDependentOffset(tzinfo):
2897 def utcoffset(self, t):
2898 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002899 # d0 and d1 equal after adjustment
2900 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002901 else:
Tim Peters397301e2003-01-02 21:28:08 +00002902 # d2 off in the weeds
2903 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002904
2905 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2906 d0 = base.replace(minute=3)
2907 d1 = base.replace(minute=9)
2908 d2 = base.replace(minute=11)
2909 for x in d0, d1, d2:
2910 for y in d0, d1, d2:
2911 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002912 expected = timedelta(minutes=x.minute - y.minute)
2913 self.assertEqual(got, expected)
2914
2915 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2916 # ignored.
2917 base = cls(8, 9, 10, 11, 12, 13, 14)
2918 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2919 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2920 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2921 for x in d0, d1, d2:
2922 for y in d0, d1, d2:
2923 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002924 if (x is d0 or x is d1) and (y is d0 or y is d1):
2925 expected = timedelta(0)
2926 elif x is y is d2:
2927 expected = timedelta(0)
2928 elif x is d2:
2929 expected = timedelta(minutes=(11-59)-0)
2930 else:
2931 assert y is d2
2932 expected = timedelta(minutes=0-(11-59))
2933 self.assertEqual(got, expected)
2934
Tim Peters60c76e42002-12-27 00:41:11 +00002935 def test_mixed_compare(self):
2936 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002937 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002938 self.assertEqual(t1, t2)
2939 t2 = t2.replace(tzinfo=None)
2940 self.assertEqual(t1, t2)
2941 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2942 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002943 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2944 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002945
Tim Peters0bf60bd2003-01-08 20:40:01 +00002946 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002947 class Varies(tzinfo):
2948 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002949 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002950 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002951 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002952 return self.offset
2953
2954 v = Varies()
2955 t1 = t2.replace(tzinfo=v)
2956 t2 = t2.replace(tzinfo=v)
2957 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2958 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2959 self.assertEqual(t1, t2)
2960
2961 # But if they're not identical, it isn't ignored.
2962 t2 = t2.replace(tzinfo=Varies())
2963 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002964
Tim Petersa98924a2003-05-17 05:55:19 +00002965 def test_subclass_datetimetz(self):
2966
2967 class C(self.theclass):
2968 theAnswer = 42
2969
2970 def __new__(cls, *args, **kws):
2971 temp = kws.copy()
2972 extra = temp.pop('extra')
2973 result = self.theclass.__new__(cls, *args, **temp)
2974 result.extra = extra
2975 return result
2976
2977 def newmeth(self, start):
2978 return start + self.hour + self.year
2979
2980 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2981
2982 dt1 = self.theclass(*args)
2983 dt2 = C(*args, **{'extra': 7})
2984
2985 self.assertEqual(dt2.__class__, C)
2986 self.assertEqual(dt2.theAnswer, 42)
2987 self.assertEqual(dt2.extra, 7)
2988 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2989 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2990
Tim Peters621818b2002-12-29 23:44:49 +00002991# Pain to set up DST-aware tzinfo classes.
2992
2993def first_sunday_on_or_after(dt):
2994 days_to_go = 6 - dt.weekday()
2995 if days_to_go:
2996 dt += timedelta(days_to_go)
2997 return dt
2998
2999ZERO = timedelta(0)
3000HOUR = timedelta(hours=1)
3001DAY = timedelta(days=1)
3002# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3003DSTSTART = datetime(1, 4, 1, 2)
3004# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003005# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3006# being standard time on that day, there is no spelling in local time of
3007# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3008DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003009
3010class USTimeZone(tzinfo):
3011
3012 def __init__(self, hours, reprname, stdname, dstname):
3013 self.stdoffset = timedelta(hours=hours)
3014 self.reprname = reprname
3015 self.stdname = stdname
3016 self.dstname = dstname
3017
3018 def __repr__(self):
3019 return self.reprname
3020
3021 def tzname(self, dt):
3022 if self.dst(dt):
3023 return self.dstname
3024 else:
3025 return self.stdname
3026
3027 def utcoffset(self, dt):
3028 return self.stdoffset + self.dst(dt)
3029
3030 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003031 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003032 # An exception instead may be sensible here, in one or more of
3033 # the cases.
3034 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003035 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003036
3037 # Find first Sunday in April.
3038 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3039 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3040
3041 # Find last Sunday in October.
3042 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3043 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3044
Tim Peters621818b2002-12-29 23:44:49 +00003045 # Can't compare naive to aware objects, so strip the timezone from
3046 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003047 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003048 return HOUR
3049 else:
3050 return ZERO
3051
Tim Peters521fc152002-12-31 17:36:56 +00003052Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3053Central = USTimeZone(-6, "Central", "CST", "CDT")
3054Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3055Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003056utc_real = FixedOffset(0, "UTC", 0)
3057# For better test coverage, we want another flavor of UTC that's west of
3058# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003059utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003060
3061class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003062 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003063 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003064 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003065
Tim Peters0bf60bd2003-01-08 20:40:01 +00003066 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003067
Tim Peters521fc152002-12-31 17:36:56 +00003068 # Check a time that's inside DST.
3069 def checkinside(self, dt, tz, utc, dston, dstoff):
3070 self.assertEqual(dt.dst(), HOUR)
3071
3072 # Conversion to our own timezone is always an identity.
3073 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003074
3075 asutc = dt.astimezone(utc)
3076 there_and_back = asutc.astimezone(tz)
3077
3078 # Conversion to UTC and back isn't always an identity here,
3079 # because there are redundant spellings (in local time) of
3080 # UTC time when DST begins: the clock jumps from 1:59:59
3081 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3082 # make sense then. The classes above treat 2:MM:SS as
3083 # daylight time then (it's "after 2am"), really an alias
3084 # for 1:MM:SS standard time. The latter form is what
3085 # conversion back from UTC produces.
3086 if dt.date() == dston.date() and dt.hour == 2:
3087 # We're in the redundant hour, and coming back from
3088 # UTC gives the 1:MM:SS standard-time spelling.
3089 self.assertEqual(there_and_back + HOUR, dt)
3090 # Although during was considered to be in daylight
3091 # time, there_and_back is not.
3092 self.assertEqual(there_and_back.dst(), ZERO)
3093 # They're the same times in UTC.
3094 self.assertEqual(there_and_back.astimezone(utc),
3095 dt.astimezone(utc))
3096 else:
3097 # We're not in the redundant hour.
3098 self.assertEqual(dt, there_and_back)
3099
Tim Peters327098a2003-01-20 22:54:38 +00003100 # Because we have a redundant spelling when DST begins, there is
3101 # (unforunately) an hour when DST ends that can't be spelled at all in
3102 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3103 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3104 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3105 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3106 # expressed in local time. Nevertheless, we want conversion back
3107 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003108 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003109 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003110 if dt.date() == dstoff.date() and dt.hour == 0:
3111 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003112 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003113 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3114 nexthour_utc += HOUR
3115 nexthour_tz = nexthour_utc.astimezone(tz)
3116 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003117 else:
Tim Peters327098a2003-01-20 22:54:38 +00003118 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003119
3120 # Check a time that's outside DST.
3121 def checkoutside(self, dt, tz, utc):
3122 self.assertEqual(dt.dst(), ZERO)
3123
3124 # Conversion to our own timezone is always an identity.
3125 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003126
3127 # Converting to UTC and back is an identity too.
3128 asutc = dt.astimezone(utc)
3129 there_and_back = asutc.astimezone(tz)
3130 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003131
Tim Peters1024bf82002-12-30 17:09:40 +00003132 def convert_between_tz_and_utc(self, tz, utc):
3133 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003134 # Because 1:MM on the day DST ends is taken as being standard time,
3135 # there is no spelling in tz for the last hour of daylight time.
3136 # For purposes of the test, the last hour of DST is 0:MM, which is
3137 # taken as being daylight time (and 1:MM is taken as being standard
3138 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003139 dstoff = self.dstoff.replace(tzinfo=tz)
3140 for delta in (timedelta(weeks=13),
3141 DAY,
3142 HOUR,
3143 timedelta(minutes=1),
3144 timedelta(microseconds=1)):
3145
Tim Peters521fc152002-12-31 17:36:56 +00003146 self.checkinside(dston, tz, utc, dston, dstoff)
3147 for during in dston + delta, dstoff - delta:
3148 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003149
Tim Peters521fc152002-12-31 17:36:56 +00003150 self.checkoutside(dstoff, tz, utc)
3151 for outside in dston - delta, dstoff + delta:
3152 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003153
Tim Peters621818b2002-12-29 23:44:49 +00003154 def test_easy(self):
3155 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003156 self.convert_between_tz_and_utc(Eastern, utc_real)
3157 self.convert_between_tz_and_utc(Pacific, utc_real)
3158 self.convert_between_tz_and_utc(Eastern, utc_fake)
3159 self.convert_between_tz_and_utc(Pacific, utc_fake)
3160 # The next is really dancing near the edge. It works because
3161 # Pacific and Eastern are far enough apart that their "problem
3162 # hours" don't overlap.
3163 self.convert_between_tz_and_utc(Eastern, Pacific)
3164 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003165 # OTOH, these fail! Don't enable them. The difficulty is that
3166 # the edge case tests assume that every hour is representable in
3167 # the "utc" class. This is always true for a fixed-offset tzinfo
3168 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3169 # For these adjacent DST-aware time zones, the range of time offsets
3170 # tested ends up creating hours in the one that aren't representable
3171 # in the other. For the same reason, we would see failures in the
3172 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3173 # offset deltas in convert_between_tz_and_utc().
3174 #
3175 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3176 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003177
Tim Petersf3615152003-01-01 21:51:37 +00003178 def test_tricky(self):
3179 # 22:00 on day before daylight starts.
3180 fourback = self.dston - timedelta(hours=4)
3181 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003182 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003183 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3184 # 2", we should get the 3 spelling.
3185 # If we plug 22:00 the day before into Eastern, it "looks like std
3186 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3187 # to 22:00 lands on 2:00, which makes no sense in local time (the
3188 # local clock jumps from 1 to 3). The point here is to make sure we
3189 # get the 3 spelling.
3190 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003191 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003192 self.assertEqual(expected, got)
3193
3194 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3195 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003196 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003197 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3198 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3199 # spelling.
3200 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003201 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003202 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003203
Tim Petersadf64202003-01-04 06:03:15 +00003204 # Now on the day DST ends, we want "repeat an hour" behavior.
3205 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3206 # EST 23:MM 0:MM 1:MM 2:MM
3207 # EDT 0:MM 1:MM 2:MM 3:MM
3208 # wall 0:MM 1:MM 1:MM 2:MM against these
3209 for utc in utc_real, utc_fake:
3210 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003211 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003212 # Convert that to UTC.
3213 first_std_hour -= tz.utcoffset(None)
3214 # Adjust for possibly fake UTC.
3215 asutc = first_std_hour + utc.utcoffset(None)
3216 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3217 # tz=Eastern.
3218 asutcbase = asutc.replace(tzinfo=utc)
3219 for tzhour in (0, 1, 1, 2):
3220 expectedbase = self.dstoff.replace(hour=tzhour)
3221 for minute in 0, 30, 59:
3222 expected = expectedbase.replace(minute=minute)
3223 asutc = asutcbase.replace(minute=minute)
3224 astz = asutc.astimezone(tz)
3225 self.assertEqual(astz.replace(tzinfo=None), expected)
3226 asutcbase += HOUR
3227
3228
Tim Peters710fb152003-01-02 19:35:54 +00003229 def test_bogus_dst(self):
3230 class ok(tzinfo):
3231 def utcoffset(self, dt): return HOUR
3232 def dst(self, dt): return HOUR
3233
3234 now = self.theclass.now().replace(tzinfo=utc_real)
3235 # Doesn't blow up.
3236 now.astimezone(ok())
3237
3238 # Does blow up.
3239 class notok(ok):
3240 def dst(self, dt): return None
3241 self.assertRaises(ValueError, now.astimezone, notok())
3242
Tim Peters52dcce22003-01-23 16:36:11 +00003243 def test_fromutc(self):
3244 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3245 now = datetime.utcnow().replace(tzinfo=utc_real)
3246 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3247 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3248 enow = Eastern.fromutc(now) # doesn't blow up
3249 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3250 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3251 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3252
3253 # Always converts UTC to standard time.
3254 class FauxUSTimeZone(USTimeZone):
3255 def fromutc(self, dt):
3256 return dt + self.stdoffset
3257 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3258
3259 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3260 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3261 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3262
3263 # Check around DST start.
3264 start = self.dston.replace(hour=4, tzinfo=Eastern)
3265 fstart = start.replace(tzinfo=FEastern)
3266 for wall in 23, 0, 1, 3, 4, 5:
3267 expected = start.replace(hour=wall)
3268 if wall == 23:
3269 expected -= timedelta(days=1)
3270 got = Eastern.fromutc(start)
3271 self.assertEqual(expected, got)
3272
3273 expected = fstart + FEastern.stdoffset
3274 got = FEastern.fromutc(fstart)
3275 self.assertEqual(expected, got)
3276
3277 # Ensure astimezone() calls fromutc() too.
3278 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3279 self.assertEqual(expected, got)
3280
3281 start += HOUR
3282 fstart += HOUR
3283
3284 # Check around DST end.
3285 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3286 fstart = start.replace(tzinfo=FEastern)
3287 for wall in 0, 1, 1, 2, 3, 4:
3288 expected = start.replace(hour=wall)
3289 got = Eastern.fromutc(start)
3290 self.assertEqual(expected, got)
3291
3292 expected = fstart + FEastern.stdoffset
3293 got = FEastern.fromutc(fstart)
3294 self.assertEqual(expected, got)
3295
3296 # Ensure astimezone() calls fromutc() too.
3297 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3298 self.assertEqual(expected, got)
3299
3300 start += HOUR
3301 fstart += HOUR
3302
Tim Peters710fb152003-01-02 19:35:54 +00003303
Tim Peters528ca532004-09-16 01:30:50 +00003304#############################################################################
3305# oddballs
3306
3307class Oddballs(unittest.TestCase):
3308
3309 def test_bug_1028306(self):
3310 # Trying to compare a date to a datetime should act like a mixed-
3311 # type comparison, despite that datetime is a subclass of date.
3312 as_date = date.today()
3313 as_datetime = datetime.combine(as_date, time())
3314 self.assert_(as_date != as_datetime)
3315 self.assert_(as_datetime != as_date)
3316 self.assert_(not as_date == as_datetime)
3317 self.assert_(not as_datetime == as_date)
3318 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3319 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3320 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3321 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3322 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3323 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3324 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3325 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3326
3327 # Neverthelss, comparison should work with the base-class (date)
3328 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003329 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003330 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003331 as_different = as_datetime.replace(day= different_day)
3332 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003333
3334 # And date should compare with other subclasses of date. If a
3335 # subclass wants to stop this, it's up to the subclass to do so.
3336 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3337 self.assertEqual(as_date, date_sc)
3338 self.assertEqual(date_sc, as_date)
3339
3340 # Ditto for datetimes.
3341 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3342 as_date.day, 0, 0, 0)
3343 self.assertEqual(as_datetime, datetime_sc)
3344 self.assertEqual(datetime_sc, as_datetime)
3345
Tim Peters2a799bf2002-12-16 20:18:38 +00003346def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003347 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003348
3349if __name__ == "__main__":
3350 test_main()