blob: 797800f0960e3cfa5ffb732896d3af28657ba512 [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
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000252 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000253 # 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
Benjamin Petersone1cdfd72009-01-18 21:02:37 +0000854 #make sure that invalid format specifiers are handled correctly
855 #self.assertRaises(ValueError, t.strftime, "%e")
856 #self.assertRaises(ValueError, t.strftime, "%")
857 #self.assertRaises(ValueError, t.strftime, "%#")
858
859 #oh well, some systems just ignore those invalid ones.
860 #at least, excercise them to make sure that no crashes
861 #are generated
862 for f in ["%e", "%", "%#"]:
863 try:
864 t.strftime(f)
865 except ValueError:
866 pass
867
868 #check that this standard extension works
869 t.strftime("%f")
870
Georg Brandlf78e02b2008-06-10 17:40:04 +0000871
Eric Smith1ba31142007-09-11 18:06:02 +0000872 def test_format(self):
873 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000874 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000875
876 # check that a derived class's __str__() gets called
877 class A(self.theclass):
878 def __str__(self):
879 return 'A'
880 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000881 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000882
883 # check that a derived class's strftime gets called
884 class B(self.theclass):
885 def strftime(self, format_spec):
886 return 'B'
887 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000888 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000889
890 for fmt in ["m:%m d:%d y:%y",
891 "m:%m d:%d y:%y H:%H M:%M S:%S",
892 "%z %Z",
893 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +0000894 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
895 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
896 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +0000897
Tim Peters2a799bf2002-12-16 20:18:38 +0000898 def test_resolution_info(self):
899 self.assert_(isinstance(self.theclass.min, self.theclass))
900 self.assert_(isinstance(self.theclass.max, self.theclass))
901 self.assert_(isinstance(self.theclass.resolution, timedelta))
902 self.assert_(self.theclass.max > self.theclass.min)
903
904 def test_extreme_timedelta(self):
905 big = self.theclass.max - self.theclass.min
906 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
907 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
908 # n == 315537897599999999 ~= 2**58.13
909 justasbig = timedelta(0, 0, n)
910 self.assertEqual(big, justasbig)
911 self.assertEqual(self.theclass.min + big, self.theclass.max)
912 self.assertEqual(self.theclass.max - big, self.theclass.min)
913
914 def test_timetuple(self):
915 for i in range(7):
916 # January 2, 1956 is a Monday (0)
917 d = self.theclass(1956, 1, 2+i)
918 t = d.timetuple()
919 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
920 # February 1, 1956 is a Wednesday (2)
921 d = self.theclass(1956, 2, 1+i)
922 t = d.timetuple()
923 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
924 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
925 # of the year.
926 d = self.theclass(1956, 3, 1+i)
927 t = d.timetuple()
928 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
929 self.assertEqual(t.tm_year, 1956)
930 self.assertEqual(t.tm_mon, 3)
931 self.assertEqual(t.tm_mday, 1+i)
932 self.assertEqual(t.tm_hour, 0)
933 self.assertEqual(t.tm_min, 0)
934 self.assertEqual(t.tm_sec, 0)
935 self.assertEqual(t.tm_wday, (3+i)%7)
936 self.assertEqual(t.tm_yday, 61+i)
937 self.assertEqual(t.tm_isdst, -1)
938
939 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000940 args = 6, 7, 23
941 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000942 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000943 green = pickler.dumps(orig, proto)
944 derived = unpickler.loads(green)
945 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000946
947 def test_compare(self):
948 t1 = self.theclass(2, 3, 4)
949 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000950 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000951 self.failUnless(t1 <= t2)
952 self.failUnless(t1 >= t2)
953 self.failUnless(not t1 != t2)
954 self.failUnless(not t1 < t2)
955 self.failUnless(not t1 > t2)
956 self.assertEqual(cmp(t1, t2), 0)
957 self.assertEqual(cmp(t2, t1), 0)
958
959 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
960 t2 = self.theclass(*args) # this is larger than t1
961 self.failUnless(t1 < t2)
962 self.failUnless(t2 > t1)
963 self.failUnless(t1 <= t2)
964 self.failUnless(t2 >= t1)
965 self.failUnless(t1 != t2)
966 self.failUnless(t2 != t1)
967 self.failUnless(not t1 == t2)
968 self.failUnless(not t2 == t1)
969 self.failUnless(not t1 > t2)
970 self.failUnless(not t2 < t1)
971 self.failUnless(not t1 >= t2)
972 self.failUnless(not t2 <= t1)
973 self.assertEqual(cmp(t1, t2), -1)
974 self.assertEqual(cmp(t2, t1), 1)
975
Tim Peters68124bb2003-02-08 03:46:31 +0000976 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000977 self.assertEqual(t1 == badarg, False)
978 self.assertEqual(t1 != badarg, True)
979 self.assertEqual(badarg == t1, False)
980 self.assertEqual(badarg != t1, True)
981
Tim Peters2a799bf2002-12-16 20:18:38 +0000982 self.assertRaises(TypeError, lambda: t1 < badarg)
983 self.assertRaises(TypeError, lambda: t1 > badarg)
984 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000985 self.assertRaises(TypeError, lambda: badarg <= t1)
986 self.assertRaises(TypeError, lambda: badarg < t1)
987 self.assertRaises(TypeError, lambda: badarg > t1)
988 self.assertRaises(TypeError, lambda: badarg >= t1)
989
Tim Peters8d81a012003-01-24 22:36:34 +0000990 def test_mixed_compare(self):
991 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000992
993 # Our class can be compared for equality to other classes
994 self.assertEqual(our == 1, False)
995 self.assertEqual(1 == our, False)
996 self.assertEqual(our != 1, True)
997 self.assertEqual(1 != our, True)
998
999 # But the ordering is undefined
1000 self.assertRaises(TypeError, lambda: our < 1)
1001 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001002 self.assertRaises(TypeError, cmp, our, 1)
1003 self.assertRaises(TypeError, cmp, 1, our)
1004
Guido van Rossum19960592006-08-24 17:29:38 +00001005 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001006
Guido van Rossum19960592006-08-24 17:29:38 +00001007 class SomeClass:
1008 pass
1009
1010 their = SomeClass()
1011 self.assertEqual(our == their, False)
1012 self.assertEqual(their == our, False)
1013 self.assertEqual(our != their, True)
1014 self.assertEqual(their != our, True)
1015 self.assertRaises(TypeError, lambda: our < their)
1016 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001017 self.assertRaises(TypeError, cmp, our, their)
Guido van Rossum19960592006-08-24 17:29:38 +00001018 self.assertRaises(TypeError, cmp, their, our)
Tim Peters8d81a012003-01-24 22:36:34 +00001019
Guido van Rossum19960592006-08-24 17:29:38 +00001020 # However, if the other class explicitly defines ordering
1021 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001022
Guido van Rossum19960592006-08-24 17:29:38 +00001023 class LargerThanAnything:
1024 def __lt__(self, other):
1025 return False
1026 def __le__(self, other):
1027 return isinstance(other, LargerThanAnything)
1028 def __eq__(self, other):
1029 return isinstance(other, LargerThanAnything)
1030 def __ne__(self, other):
1031 return not isinstance(other, LargerThanAnything)
1032 def __gt__(self, other):
1033 return not isinstance(other, LargerThanAnything)
1034 def __ge__(self, other):
1035 return True
1036
1037 their = LargerThanAnything()
1038 self.assertEqual(our == their, False)
1039 self.assertEqual(their == our, False)
1040 self.assertEqual(our != their, True)
1041 self.assertEqual(their != our, True)
1042 self.assertEqual(our < their, True)
1043 self.assertEqual(their < our, False)
1044 self.assertEqual(cmp(our, their), -1)
1045 self.assertEqual(cmp(their, our), 1)
Tim Peters8d81a012003-01-24 22:36:34 +00001046
Tim Peters2a799bf2002-12-16 20:18:38 +00001047 def test_bool(self):
1048 # All dates are considered true.
1049 self.failUnless(self.theclass.min)
1050 self.failUnless(self.theclass.max)
1051
Guido van Rossum04110fb2007-08-24 16:32:05 +00001052 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001053 # For nasty technical reasons, we can't handle years before 1900.
1054 cls = self.theclass
1055 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1056 for y in 1, 49, 51, 99, 100, 1000, 1899:
1057 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001058
1059 def test_replace(self):
1060 cls = self.theclass
1061 args = [1, 2, 3]
1062 base = cls(*args)
1063 self.assertEqual(base, base.replace())
1064
1065 i = 0
1066 for name, newval in (("year", 2),
1067 ("month", 3),
1068 ("day", 4)):
1069 newargs = args[:]
1070 newargs[i] = newval
1071 expected = cls(*newargs)
1072 got = base.replace(**{name: newval})
1073 self.assertEqual(expected, got)
1074 i += 1
1075
1076 # Out of bounds.
1077 base = cls(2000, 2, 29)
1078 self.assertRaises(ValueError, base.replace, year=2001)
1079
Tim Petersa98924a2003-05-17 05:55:19 +00001080 def test_subclass_date(self):
1081
1082 class C(self.theclass):
1083 theAnswer = 42
1084
1085 def __new__(cls, *args, **kws):
1086 temp = kws.copy()
1087 extra = temp.pop('extra')
1088 result = self.theclass.__new__(cls, *args, **temp)
1089 result.extra = extra
1090 return result
1091
1092 def newmeth(self, start):
1093 return start + self.year + self.month
1094
1095 args = 2003, 4, 14
1096
1097 dt1 = self.theclass(*args)
1098 dt2 = C(*args, **{'extra': 7})
1099
1100 self.assertEqual(dt2.__class__, C)
1101 self.assertEqual(dt2.theAnswer, 42)
1102 self.assertEqual(dt2.extra, 7)
1103 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1104 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1105
Tim Peters604c0132004-06-07 23:04:33 +00001106 def test_pickling_subclass_date(self):
1107
1108 args = 6, 7, 23
1109 orig = SubclassDate(*args)
1110 for pickler, unpickler, proto in pickle_choices:
1111 green = pickler.dumps(orig, proto)
1112 derived = unpickler.loads(green)
1113 self.assertEqual(orig, derived)
1114
Tim Peters3f606292004-03-21 23:38:41 +00001115 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001116 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001117 # This is a low-overhead backdoor. A user can (by intent or
1118 # mistake) pass a string directly, which (if it's the right length)
1119 # will get treated like a pickle, and bypass the normal sanity
1120 # checks in the constructor. This can create insane objects.
1121 # The constructor doesn't want to burn the time to validate all
1122 # fields, but does check the month field. This stops, e.g.,
1123 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001124 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001125 if not issubclass(self.theclass, datetime):
1126 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001127 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001128 self.assertRaises(TypeError, self.theclass,
1129 base[:2] + month_byte + base[3:])
1130 for ord_byte in range(1, 13):
1131 # This shouldn't blow up because of the month byte alone. If
1132 # the implementation changes to do more-careful checking, it may
1133 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001134 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001135
Tim Peters2a799bf2002-12-16 20:18:38 +00001136#############################################################################
1137# datetime tests
1138
Tim Peters604c0132004-06-07 23:04:33 +00001139class SubclassDatetime(datetime):
1140 sub_var = 1
1141
Tim Peters2a799bf2002-12-16 20:18:38 +00001142class TestDateTime(TestDate):
1143
1144 theclass = datetime
1145
1146 def test_basic_attributes(self):
1147 dt = self.theclass(2002, 3, 1, 12, 0)
1148 self.assertEqual(dt.year, 2002)
1149 self.assertEqual(dt.month, 3)
1150 self.assertEqual(dt.day, 1)
1151 self.assertEqual(dt.hour, 12)
1152 self.assertEqual(dt.minute, 0)
1153 self.assertEqual(dt.second, 0)
1154 self.assertEqual(dt.microsecond, 0)
1155
1156 def test_basic_attributes_nonzero(self):
1157 # Make sure all attributes are non-zero so bugs in
1158 # bit-shifting access show up.
1159 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1160 self.assertEqual(dt.year, 2002)
1161 self.assertEqual(dt.month, 3)
1162 self.assertEqual(dt.day, 1)
1163 self.assertEqual(dt.hour, 12)
1164 self.assertEqual(dt.minute, 59)
1165 self.assertEqual(dt.second, 59)
1166 self.assertEqual(dt.microsecond, 8000)
1167
1168 def test_roundtrip(self):
1169 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1170 self.theclass.now()):
1171 # Verify dt -> string -> datetime identity.
1172 s = repr(dt)
1173 self.failUnless(s.startswith('datetime.'))
1174 s = s[9:]
1175 dt2 = eval(s)
1176 self.assertEqual(dt, dt2)
1177
1178 # Verify identity via reconstructing from pieces.
1179 dt2 = self.theclass(dt.year, dt.month, dt.day,
1180 dt.hour, dt.minute, dt.second,
1181 dt.microsecond)
1182 self.assertEqual(dt, dt2)
1183
1184 def test_isoformat(self):
1185 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1186 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1187 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1188 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1189 # str is ISO format with the separator forced to a blank.
1190 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1191
1192 t = self.theclass(2, 3, 2)
1193 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1194 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1195 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1196 # str is ISO format with the separator forced to a blank.
1197 self.assertEqual(str(t), "0002-03-02 00:00:00")
1198
Eric Smith1ba31142007-09-11 18:06:02 +00001199 def test_format(self):
1200 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001201 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001202
1203 # check that a derived class's __str__() gets called
1204 class A(self.theclass):
1205 def __str__(self):
1206 return 'A'
1207 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001208 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001209
1210 # check that a derived class's strftime gets called
1211 class B(self.theclass):
1212 def strftime(self, format_spec):
1213 return 'B'
1214 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001215 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001216
1217 for fmt in ["m:%m d:%d y:%y",
1218 "m:%m d:%d y:%y H:%H M:%M S:%S",
1219 "%z %Z",
1220 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001221 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1222 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1223 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001224
Tim Peters2a799bf2002-12-16 20:18:38 +00001225 def test_more_ctime(self):
1226 # Test fields that TestDate doesn't touch.
1227 import time
1228
1229 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1230 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1231 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1232 # out. The difference is that t.ctime() produces " 2" for the day,
1233 # but platform ctime() produces "02" for the day. According to
1234 # C99, t.ctime() is correct here.
1235 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1236
1237 # So test a case where that difference doesn't matter.
1238 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1239 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1240
1241 def test_tz_independent_comparing(self):
1242 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1243 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1244 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1245 self.assertEqual(dt1, dt3)
1246 self.assert_(dt2 > dt3)
1247
1248 # Make sure comparison doesn't forget microseconds, and isn't done
1249 # via comparing a float timestamp (an IEEE double doesn't have enough
1250 # precision to span microsecond resolution across years 1 thru 9999,
1251 # so comparing via timestamp necessarily calls some distinct values
1252 # equal).
1253 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1254 us = timedelta(microseconds=1)
1255 dt2 = dt1 + us
1256 self.assertEqual(dt2 - dt1, us)
1257 self.assert_(dt1 < dt2)
1258
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001259 def test_strftime_with_bad_tzname_replace(self):
1260 # verify ok if tzinfo.tzname().replace() returns a non-string
1261 class MyTzInfo(FixedOffset):
1262 def tzname(self, dt):
1263 class MyStr(str):
1264 def replace(self, *args):
1265 return None
1266 return MyStr('name')
1267 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1268 self.assertRaises(TypeError, t.strftime, '%Z')
1269
Tim Peters2a799bf2002-12-16 20:18:38 +00001270 def test_bad_constructor_arguments(self):
1271 # bad years
1272 self.theclass(MINYEAR, 1, 1) # no exception
1273 self.theclass(MAXYEAR, 1, 1) # no exception
1274 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1275 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1276 # bad months
1277 self.theclass(2000, 1, 1) # no exception
1278 self.theclass(2000, 12, 1) # no exception
1279 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1280 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1281 # bad days
1282 self.theclass(2000, 2, 29) # no exception
1283 self.theclass(2004, 2, 29) # no exception
1284 self.theclass(2400, 2, 29) # no exception
1285 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1286 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1287 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1288 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1289 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1290 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1291 # bad hours
1292 self.theclass(2000, 1, 31, 0) # no exception
1293 self.theclass(2000, 1, 31, 23) # no exception
1294 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1295 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1296 # bad minutes
1297 self.theclass(2000, 1, 31, 23, 0) # no exception
1298 self.theclass(2000, 1, 31, 23, 59) # no exception
1299 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1300 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1301 # bad seconds
1302 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1303 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1304 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1305 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1306 # bad microseconds
1307 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1308 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1309 self.assertRaises(ValueError, self.theclass,
1310 2000, 1, 31, 23, 59, 59, -1)
1311 self.assertRaises(ValueError, self.theclass,
1312 2000, 1, 31, 23, 59, 59,
1313 1000000)
1314
1315 def test_hash_equality(self):
1316 d = self.theclass(2000, 12, 31, 23, 30, 17)
1317 e = self.theclass(2000, 12, 31, 23, 30, 17)
1318 self.assertEqual(d, e)
1319 self.assertEqual(hash(d), hash(e))
1320
1321 dic = {d: 1}
1322 dic[e] = 2
1323 self.assertEqual(len(dic), 1)
1324 self.assertEqual(dic[d], 2)
1325 self.assertEqual(dic[e], 2)
1326
1327 d = self.theclass(2001, 1, 1, 0, 5, 17)
1328 e = self.theclass(2001, 1, 1, 0, 5, 17)
1329 self.assertEqual(d, e)
1330 self.assertEqual(hash(d), hash(e))
1331
1332 dic = {d: 1}
1333 dic[e] = 2
1334 self.assertEqual(len(dic), 1)
1335 self.assertEqual(dic[d], 2)
1336 self.assertEqual(dic[e], 2)
1337
1338 def test_computations(self):
1339 a = self.theclass(2002, 1, 31)
1340 b = self.theclass(1956, 1, 31)
1341 diff = a-b
1342 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1343 self.assertEqual(diff.seconds, 0)
1344 self.assertEqual(diff.microseconds, 0)
1345 a = self.theclass(2002, 3, 2, 17, 6)
1346 millisec = timedelta(0, 0, 1000)
1347 hour = timedelta(0, 3600)
1348 day = timedelta(1)
1349 week = timedelta(7)
1350 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1351 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1352 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1353 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1354 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1355 self.assertEqual(a - hour, a + -hour)
1356 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1357 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1358 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1359 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1360 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1361 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1362 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1363 self.assertEqual((a + week) - a, week)
1364 self.assertEqual((a + day) - a, day)
1365 self.assertEqual((a + hour) - a, hour)
1366 self.assertEqual((a + millisec) - a, millisec)
1367 self.assertEqual((a - week) - a, -week)
1368 self.assertEqual((a - day) - a, -day)
1369 self.assertEqual((a - hour) - a, -hour)
1370 self.assertEqual((a - millisec) - a, -millisec)
1371 self.assertEqual(a - (a + week), -week)
1372 self.assertEqual(a - (a + day), -day)
1373 self.assertEqual(a - (a + hour), -hour)
1374 self.assertEqual(a - (a + millisec), -millisec)
1375 self.assertEqual(a - (a - week), week)
1376 self.assertEqual(a - (a - day), day)
1377 self.assertEqual(a - (a - hour), hour)
1378 self.assertEqual(a - (a - millisec), millisec)
1379 self.assertEqual(a + (week + day + hour + millisec),
1380 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1381 self.assertEqual(a + (week + day + hour + millisec),
1382 (((a + week) + day) + hour) + millisec)
1383 self.assertEqual(a - (week + day + hour + millisec),
1384 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1385 self.assertEqual(a - (week + day + hour + millisec),
1386 (((a - week) - day) - hour) - millisec)
1387 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001388 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001389 self.assertRaises(TypeError, lambda: a+i)
1390 self.assertRaises(TypeError, lambda: a-i)
1391 self.assertRaises(TypeError, lambda: i+a)
1392 self.assertRaises(TypeError, lambda: i-a)
1393
1394 # delta - datetime is senseless.
1395 self.assertRaises(TypeError, lambda: day - a)
1396 # mixing datetime and (delta or datetime) via * or // is senseless
1397 self.assertRaises(TypeError, lambda: day * a)
1398 self.assertRaises(TypeError, lambda: a * day)
1399 self.assertRaises(TypeError, lambda: day // a)
1400 self.assertRaises(TypeError, lambda: a // day)
1401 self.assertRaises(TypeError, lambda: a * a)
1402 self.assertRaises(TypeError, lambda: a // a)
1403 # datetime + datetime is senseless
1404 self.assertRaises(TypeError, lambda: a + a)
1405
1406 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001407 args = 6, 7, 23, 20, 59, 1, 64**2
1408 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001409 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001410 green = pickler.dumps(orig, proto)
1411 derived = unpickler.loads(green)
1412 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001413
Guido van Rossum275666f2003-02-07 21:49:01 +00001414 def test_more_pickling(self):
1415 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1416 s = pickle.dumps(a)
1417 b = pickle.loads(s)
1418 self.assertEqual(b.year, 2003)
1419 self.assertEqual(b.month, 2)
1420 self.assertEqual(b.day, 7)
1421
Tim Peters604c0132004-06-07 23:04:33 +00001422 def test_pickling_subclass_datetime(self):
1423 args = 6, 7, 23, 20, 59, 1, 64**2
1424 orig = SubclassDatetime(*args)
1425 for pickler, unpickler, proto in pickle_choices:
1426 green = pickler.dumps(orig, proto)
1427 derived = unpickler.loads(green)
1428 self.assertEqual(orig, derived)
1429
Tim Peters2a799bf2002-12-16 20:18:38 +00001430 def test_more_compare(self):
1431 # The test_compare() inherited from TestDate covers the error cases.
1432 # We just want to test lexicographic ordering on the members datetime
1433 # has that date lacks.
1434 args = [2000, 11, 29, 20, 58, 16, 999998]
1435 t1 = self.theclass(*args)
1436 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001437 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001438 self.failUnless(t1 <= t2)
1439 self.failUnless(t1 >= t2)
1440 self.failUnless(not t1 != t2)
1441 self.failUnless(not t1 < t2)
1442 self.failUnless(not t1 > t2)
1443 self.assertEqual(cmp(t1, t2), 0)
1444 self.assertEqual(cmp(t2, t1), 0)
1445
1446 for i in range(len(args)):
1447 newargs = args[:]
1448 newargs[i] = args[i] + 1
1449 t2 = self.theclass(*newargs) # this is larger than t1
1450 self.failUnless(t1 < t2)
1451 self.failUnless(t2 > t1)
1452 self.failUnless(t1 <= t2)
1453 self.failUnless(t2 >= t1)
1454 self.failUnless(t1 != t2)
1455 self.failUnless(t2 != t1)
1456 self.failUnless(not t1 == t2)
1457 self.failUnless(not t2 == t1)
1458 self.failUnless(not t1 > t2)
1459 self.failUnless(not t2 < t1)
1460 self.failUnless(not t1 >= t2)
1461 self.failUnless(not t2 <= t1)
1462 self.assertEqual(cmp(t1, t2), -1)
1463 self.assertEqual(cmp(t2, t1), 1)
1464
1465
1466 # A helper for timestamp constructor tests.
1467 def verify_field_equality(self, expected, got):
1468 self.assertEqual(expected.tm_year, got.year)
1469 self.assertEqual(expected.tm_mon, got.month)
1470 self.assertEqual(expected.tm_mday, got.day)
1471 self.assertEqual(expected.tm_hour, got.hour)
1472 self.assertEqual(expected.tm_min, got.minute)
1473 self.assertEqual(expected.tm_sec, got.second)
1474
1475 def test_fromtimestamp(self):
1476 import time
1477
1478 ts = time.time()
1479 expected = time.localtime(ts)
1480 got = self.theclass.fromtimestamp(ts)
1481 self.verify_field_equality(expected, got)
1482
1483 def test_utcfromtimestamp(self):
1484 import time
1485
1486 ts = time.time()
1487 expected = time.gmtime(ts)
1488 got = self.theclass.utcfromtimestamp(ts)
1489 self.verify_field_equality(expected, got)
1490
Thomas Wouters477c8d52006-05-27 19:21:47 +00001491 def test_microsecond_rounding(self):
1492 # Test whether fromtimestamp "rounds up" floats that are less
1493 # than one microsecond smaller than an integer.
1494 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1495 self.theclass.fromtimestamp(1))
1496
Tim Peters1b6f7a92004-06-20 02:50:16 +00001497 def test_insane_fromtimestamp(self):
1498 # It's possible that some platform maps time_t to double,
1499 # and that this test will fail there. This test should
1500 # exempt such platforms (provided they return reasonable
1501 # results!).
1502 for insane in -1e200, 1e200:
1503 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1504 insane)
1505
1506 def test_insane_utcfromtimestamp(self):
1507 # It's possible that some platform maps time_t to double,
1508 # and that this test will fail there. This test should
1509 # exempt such platforms (provided they return reasonable
1510 # results!).
1511 for insane in -1e200, 1e200:
1512 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1513 insane)
1514
Guido van Rossumd8faa362007-04-27 19:54:29 +00001515 def test_negative_float_fromtimestamp(self):
1516 # Windows doesn't accept negative timestamps
1517 if os.name == "nt":
1518 return
1519 # The result is tz-dependent; at least test that this doesn't
1520 # fail (like it did before bug 1646728 was fixed).
1521 self.theclass.fromtimestamp(-1.05)
1522
1523 def test_negative_float_utcfromtimestamp(self):
1524 # Windows doesn't accept negative timestamps
1525 if os.name == "nt":
1526 return
1527 d = self.theclass.utcfromtimestamp(-1.05)
1528 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1529
Tim Peters2a799bf2002-12-16 20:18:38 +00001530 def test_utcnow(self):
1531 import time
1532
1533 # Call it a success if utcnow() and utcfromtimestamp() are within
1534 # a second of each other.
1535 tolerance = timedelta(seconds=1)
1536 for dummy in range(3):
1537 from_now = self.theclass.utcnow()
1538 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1539 if abs(from_timestamp - from_now) <= tolerance:
1540 break
1541 # Else try again a few times.
1542 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1543
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001544 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001545 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001546
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001547 string = '2004-12-01 13:02:47.197'
1548 format = '%Y-%m-%d %H:%M:%S.%f'
1549 result, frac = _strptime._strptime(string, format)
1550 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001551 got = self.theclass.strptime(string, format)
1552 self.assertEqual(expected, got)
1553
Tim Peters2a799bf2002-12-16 20:18:38 +00001554 def test_more_timetuple(self):
1555 # This tests fields beyond those tested by the TestDate.test_timetuple.
1556 t = self.theclass(2004, 12, 31, 6, 22, 33)
1557 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1558 self.assertEqual(t.timetuple(),
1559 (t.year, t.month, t.day,
1560 t.hour, t.minute, t.second,
1561 t.weekday(),
1562 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1563 -1))
1564 tt = t.timetuple()
1565 self.assertEqual(tt.tm_year, t.year)
1566 self.assertEqual(tt.tm_mon, t.month)
1567 self.assertEqual(tt.tm_mday, t.day)
1568 self.assertEqual(tt.tm_hour, t.hour)
1569 self.assertEqual(tt.tm_min, t.minute)
1570 self.assertEqual(tt.tm_sec, t.second)
1571 self.assertEqual(tt.tm_wday, t.weekday())
1572 self.assertEqual(tt.tm_yday, t.toordinal() -
1573 date(t.year, 1, 1).toordinal() + 1)
1574 self.assertEqual(tt.tm_isdst, -1)
1575
1576 def test_more_strftime(self):
1577 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001578 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1579 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1580 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001581
1582 def test_extract(self):
1583 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1584 self.assertEqual(dt.date(), date(2002, 3, 4))
1585 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1586
1587 def test_combine(self):
1588 d = date(2002, 3, 4)
1589 t = time(18, 45, 3, 1234)
1590 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1591 combine = self.theclass.combine
1592 dt = combine(d, t)
1593 self.assertEqual(dt, expected)
1594
1595 dt = combine(time=t, date=d)
1596 self.assertEqual(dt, expected)
1597
1598 self.assertEqual(d, dt.date())
1599 self.assertEqual(t, dt.time())
1600 self.assertEqual(dt, combine(dt.date(), dt.time()))
1601
1602 self.assertRaises(TypeError, combine) # need an arg
1603 self.assertRaises(TypeError, combine, d) # need two args
1604 self.assertRaises(TypeError, combine, t, d) # args reversed
1605 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1606 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1607
Tim Peters12bf3392002-12-24 05:41:27 +00001608 def test_replace(self):
1609 cls = self.theclass
1610 args = [1, 2, 3, 4, 5, 6, 7]
1611 base = cls(*args)
1612 self.assertEqual(base, base.replace())
1613
1614 i = 0
1615 for name, newval in (("year", 2),
1616 ("month", 3),
1617 ("day", 4),
1618 ("hour", 5),
1619 ("minute", 6),
1620 ("second", 7),
1621 ("microsecond", 8)):
1622 newargs = args[:]
1623 newargs[i] = newval
1624 expected = cls(*newargs)
1625 got = base.replace(**{name: newval})
1626 self.assertEqual(expected, got)
1627 i += 1
1628
1629 # Out of bounds.
1630 base = cls(2000, 2, 29)
1631 self.assertRaises(ValueError, base.replace, year=2001)
1632
Tim Peters80475bb2002-12-25 07:40:55 +00001633 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001634 # Pretty boring! The TZ test is more interesting here. astimezone()
1635 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001636 dt = self.theclass.now()
1637 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001638 self.assertRaises(TypeError, dt.astimezone) # not enough args
1639 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1640 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001641 self.assertRaises(ValueError, dt.astimezone, f) # naive
1642 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001643
Tim Peters52dcce22003-01-23 16:36:11 +00001644 class Bogus(tzinfo):
1645 def utcoffset(self, dt): return None
1646 def dst(self, dt): return timedelta(0)
1647 bog = Bogus()
1648 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1649
1650 class AlsoBogus(tzinfo):
1651 def utcoffset(self, dt): return timedelta(0)
1652 def dst(self, dt): return None
1653 alsobog = AlsoBogus()
1654 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001655
Tim Petersa98924a2003-05-17 05:55:19 +00001656 def test_subclass_datetime(self):
1657
1658 class C(self.theclass):
1659 theAnswer = 42
1660
1661 def __new__(cls, *args, **kws):
1662 temp = kws.copy()
1663 extra = temp.pop('extra')
1664 result = self.theclass.__new__(cls, *args, **temp)
1665 result.extra = extra
1666 return result
1667
1668 def newmeth(self, start):
1669 return start + self.year + self.month + self.second
1670
1671 args = 2003, 4, 14, 12, 13, 41
1672
1673 dt1 = self.theclass(*args)
1674 dt2 = C(*args, **{'extra': 7})
1675
1676 self.assertEqual(dt2.__class__, C)
1677 self.assertEqual(dt2.theAnswer, 42)
1678 self.assertEqual(dt2.extra, 7)
1679 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1680 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1681 dt1.second - 7)
1682
Tim Peters604c0132004-06-07 23:04:33 +00001683class SubclassTime(time):
1684 sub_var = 1
1685
Guido van Rossumd8faa362007-04-27 19:54:29 +00001686class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001687
1688 theclass = time
1689
1690 def test_basic_attributes(self):
1691 t = self.theclass(12, 0)
1692 self.assertEqual(t.hour, 12)
1693 self.assertEqual(t.minute, 0)
1694 self.assertEqual(t.second, 0)
1695 self.assertEqual(t.microsecond, 0)
1696
1697 def test_basic_attributes_nonzero(self):
1698 # Make sure all attributes are non-zero so bugs in
1699 # bit-shifting access show up.
1700 t = self.theclass(12, 59, 59, 8000)
1701 self.assertEqual(t.hour, 12)
1702 self.assertEqual(t.minute, 59)
1703 self.assertEqual(t.second, 59)
1704 self.assertEqual(t.microsecond, 8000)
1705
1706 def test_roundtrip(self):
1707 t = self.theclass(1, 2, 3, 4)
1708
1709 # Verify t -> string -> time identity.
1710 s = repr(t)
1711 self.failUnless(s.startswith('datetime.'))
1712 s = s[9:]
1713 t2 = eval(s)
1714 self.assertEqual(t, t2)
1715
1716 # Verify identity via reconstructing from pieces.
1717 t2 = self.theclass(t.hour, t.minute, t.second,
1718 t.microsecond)
1719 self.assertEqual(t, t2)
1720
1721 def test_comparing(self):
1722 args = [1, 2, 3, 4]
1723 t1 = self.theclass(*args)
1724 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001725 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001726 self.failUnless(t1 <= t2)
1727 self.failUnless(t1 >= t2)
1728 self.failUnless(not t1 != t2)
1729 self.failUnless(not t1 < t2)
1730 self.failUnless(not t1 > t2)
1731 self.assertEqual(cmp(t1, t2), 0)
1732 self.assertEqual(cmp(t2, t1), 0)
1733
1734 for i in range(len(args)):
1735 newargs = args[:]
1736 newargs[i] = args[i] + 1
1737 t2 = self.theclass(*newargs) # this is larger than t1
1738 self.failUnless(t1 < t2)
1739 self.failUnless(t2 > t1)
1740 self.failUnless(t1 <= t2)
1741 self.failUnless(t2 >= t1)
1742 self.failUnless(t1 != t2)
1743 self.failUnless(t2 != t1)
1744 self.failUnless(not t1 == t2)
1745 self.failUnless(not t2 == t1)
1746 self.failUnless(not t1 > t2)
1747 self.failUnless(not t2 < t1)
1748 self.failUnless(not t1 >= t2)
1749 self.failUnless(not t2 <= t1)
1750 self.assertEqual(cmp(t1, t2), -1)
1751 self.assertEqual(cmp(t2, t1), 1)
1752
Tim Peters68124bb2003-02-08 03:46:31 +00001753 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001754 self.assertEqual(t1 == badarg, False)
1755 self.assertEqual(t1 != badarg, True)
1756 self.assertEqual(badarg == t1, False)
1757 self.assertEqual(badarg != t1, True)
1758
Tim Peters2a799bf2002-12-16 20:18:38 +00001759 self.assertRaises(TypeError, lambda: t1 <= badarg)
1760 self.assertRaises(TypeError, lambda: t1 < badarg)
1761 self.assertRaises(TypeError, lambda: t1 > badarg)
1762 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001763 self.assertRaises(TypeError, lambda: badarg <= t1)
1764 self.assertRaises(TypeError, lambda: badarg < t1)
1765 self.assertRaises(TypeError, lambda: badarg > t1)
1766 self.assertRaises(TypeError, lambda: badarg >= t1)
1767
1768 def test_bad_constructor_arguments(self):
1769 # bad hours
1770 self.theclass(0, 0) # no exception
1771 self.theclass(23, 0) # no exception
1772 self.assertRaises(ValueError, self.theclass, -1, 0)
1773 self.assertRaises(ValueError, self.theclass, 24, 0)
1774 # bad minutes
1775 self.theclass(23, 0) # no exception
1776 self.theclass(23, 59) # no exception
1777 self.assertRaises(ValueError, self.theclass, 23, -1)
1778 self.assertRaises(ValueError, self.theclass, 23, 60)
1779 # bad seconds
1780 self.theclass(23, 59, 0) # no exception
1781 self.theclass(23, 59, 59) # no exception
1782 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1783 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1784 # bad microseconds
1785 self.theclass(23, 59, 59, 0) # no exception
1786 self.theclass(23, 59, 59, 999999) # no exception
1787 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1788 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1789
1790 def test_hash_equality(self):
1791 d = self.theclass(23, 30, 17)
1792 e = self.theclass(23, 30, 17)
1793 self.assertEqual(d, e)
1794 self.assertEqual(hash(d), hash(e))
1795
1796 dic = {d: 1}
1797 dic[e] = 2
1798 self.assertEqual(len(dic), 1)
1799 self.assertEqual(dic[d], 2)
1800 self.assertEqual(dic[e], 2)
1801
1802 d = self.theclass(0, 5, 17)
1803 e = self.theclass(0, 5, 17)
1804 self.assertEqual(d, e)
1805 self.assertEqual(hash(d), hash(e))
1806
1807 dic = {d: 1}
1808 dic[e] = 2
1809 self.assertEqual(len(dic), 1)
1810 self.assertEqual(dic[d], 2)
1811 self.assertEqual(dic[e], 2)
1812
1813 def test_isoformat(self):
1814 t = self.theclass(4, 5, 1, 123)
1815 self.assertEqual(t.isoformat(), "04:05:01.000123")
1816 self.assertEqual(t.isoformat(), str(t))
1817
1818 t = self.theclass()
1819 self.assertEqual(t.isoformat(), "00:00:00")
1820 self.assertEqual(t.isoformat(), str(t))
1821
1822 t = self.theclass(microsecond=1)
1823 self.assertEqual(t.isoformat(), "00:00:00.000001")
1824 self.assertEqual(t.isoformat(), str(t))
1825
1826 t = self.theclass(microsecond=10)
1827 self.assertEqual(t.isoformat(), "00:00:00.000010")
1828 self.assertEqual(t.isoformat(), str(t))
1829
1830 t = self.theclass(microsecond=100)
1831 self.assertEqual(t.isoformat(), "00:00:00.000100")
1832 self.assertEqual(t.isoformat(), str(t))
1833
1834 t = self.theclass(microsecond=1000)
1835 self.assertEqual(t.isoformat(), "00:00:00.001000")
1836 self.assertEqual(t.isoformat(), str(t))
1837
1838 t = self.theclass(microsecond=10000)
1839 self.assertEqual(t.isoformat(), "00:00:00.010000")
1840 self.assertEqual(t.isoformat(), str(t))
1841
1842 t = self.theclass(microsecond=100000)
1843 self.assertEqual(t.isoformat(), "00:00:00.100000")
1844 self.assertEqual(t.isoformat(), str(t))
1845
Thomas Wouterscf297e42007-02-23 15:07:44 +00001846 def test_1653736(self):
1847 # verify it doesn't accept extra keyword arguments
1848 t = self.theclass(second=1)
1849 self.assertRaises(TypeError, t.isoformat, foo=3)
1850
Tim Peters2a799bf2002-12-16 20:18:38 +00001851 def test_strftime(self):
1852 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001853 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001854 # A naive object replaces %z and %Z with empty strings.
1855 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1856
Eric Smith1ba31142007-09-11 18:06:02 +00001857 def test_format(self):
1858 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001859 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001860
1861 # check that a derived class's __str__() gets called
1862 class A(self.theclass):
1863 def __str__(self):
1864 return 'A'
1865 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001866 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001867
1868 # check that a derived class's strftime gets called
1869 class B(self.theclass):
1870 def strftime(self, format_spec):
1871 return 'B'
1872 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001873 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001874
1875 for fmt in ['%H %M %S',
1876 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001877 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1878 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1879 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001880
Tim Peters2a799bf2002-12-16 20:18:38 +00001881 def test_str(self):
1882 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1883 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1884 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1885 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1886 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1887
1888 def test_repr(self):
1889 name = 'datetime.' + self.theclass.__name__
1890 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1891 "%s(1, 2, 3, 4)" % name)
1892 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1893 "%s(10, 2, 3, 4000)" % name)
1894 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1895 "%s(0, 2, 3, 400000)" % name)
1896 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1897 "%s(12, 2, 3)" % name)
1898 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1899 "%s(23, 15)" % name)
1900
1901 def test_resolution_info(self):
1902 self.assert_(isinstance(self.theclass.min, self.theclass))
1903 self.assert_(isinstance(self.theclass.max, self.theclass))
1904 self.assert_(isinstance(self.theclass.resolution, timedelta))
1905 self.assert_(self.theclass.max > self.theclass.min)
1906
1907 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001908 args = 20, 59, 16, 64**2
1909 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001910 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001911 green = pickler.dumps(orig, proto)
1912 derived = unpickler.loads(green)
1913 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001914
Tim Peters604c0132004-06-07 23:04:33 +00001915 def test_pickling_subclass_time(self):
1916 args = 20, 59, 16, 64**2
1917 orig = SubclassTime(*args)
1918 for pickler, unpickler, proto in pickle_choices:
1919 green = pickler.dumps(orig, proto)
1920 derived = unpickler.loads(green)
1921 self.assertEqual(orig, derived)
1922
Tim Peters2a799bf2002-12-16 20:18:38 +00001923 def test_bool(self):
1924 cls = self.theclass
1925 self.failUnless(cls(1))
1926 self.failUnless(cls(0, 1))
1927 self.failUnless(cls(0, 0, 1))
1928 self.failUnless(cls(0, 0, 0, 1))
1929 self.failUnless(not cls(0))
1930 self.failUnless(not cls())
1931
Tim Peters12bf3392002-12-24 05:41:27 +00001932 def test_replace(self):
1933 cls = self.theclass
1934 args = [1, 2, 3, 4]
1935 base = cls(*args)
1936 self.assertEqual(base, base.replace())
1937
1938 i = 0
1939 for name, newval in (("hour", 5),
1940 ("minute", 6),
1941 ("second", 7),
1942 ("microsecond", 8)):
1943 newargs = args[:]
1944 newargs[i] = newval
1945 expected = cls(*newargs)
1946 got = base.replace(**{name: newval})
1947 self.assertEqual(expected, got)
1948 i += 1
1949
1950 # Out of bounds.
1951 base = cls(1)
1952 self.assertRaises(ValueError, base.replace, hour=24)
1953 self.assertRaises(ValueError, base.replace, minute=-1)
1954 self.assertRaises(ValueError, base.replace, second=100)
1955 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1956
Tim Petersa98924a2003-05-17 05:55:19 +00001957 def test_subclass_time(self):
1958
1959 class C(self.theclass):
1960 theAnswer = 42
1961
1962 def __new__(cls, *args, **kws):
1963 temp = kws.copy()
1964 extra = temp.pop('extra')
1965 result = self.theclass.__new__(cls, *args, **temp)
1966 result.extra = extra
1967 return result
1968
1969 def newmeth(self, start):
1970 return start + self.hour + self.second
1971
1972 args = 4, 5, 6
1973
1974 dt1 = self.theclass(*args)
1975 dt2 = C(*args, **{'extra': 7})
1976
1977 self.assertEqual(dt2.__class__, C)
1978 self.assertEqual(dt2.theAnswer, 42)
1979 self.assertEqual(dt2.extra, 7)
1980 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1981 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1982
Armin Rigof4afb212005-11-07 07:15:48 +00001983 def test_backdoor_resistance(self):
1984 # see TestDate.test_backdoor_resistance().
1985 base = '2:59.0'
1986 for hour_byte in ' ', '9', chr(24), '\xff':
1987 self.assertRaises(TypeError, self.theclass,
1988 hour_byte + base[1:])
1989
Tim Peters855fe882002-12-22 03:43:39 +00001990# A mixin for classes with a tzinfo= argument. Subclasses must define
1991# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001992# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001993class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001994
Tim Petersbad8ff02002-12-30 20:52:32 +00001995 def test_argument_passing(self):
1996 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001997 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001998 class introspective(tzinfo):
1999 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002000 def utcoffset(self, dt):
2001 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002002 dst = utcoffset
2003
2004 obj = cls(1, 2, 3, tzinfo=introspective())
2005
Tim Peters0bf60bd2003-01-08 20:40:01 +00002006 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002007 self.assertEqual(obj.tzname(), expected)
2008
Tim Peters0bf60bd2003-01-08 20:40:01 +00002009 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002010 self.assertEqual(obj.utcoffset(), expected)
2011 self.assertEqual(obj.dst(), expected)
2012
Tim Peters855fe882002-12-22 03:43:39 +00002013 def test_bad_tzinfo_classes(self):
2014 cls = self.theclass
2015 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002016
Tim Peters855fe882002-12-22 03:43:39 +00002017 class NiceTry(object):
2018 def __init__(self): pass
2019 def utcoffset(self, dt): pass
2020 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2021
2022 class BetterTry(tzinfo):
2023 def __init__(self): pass
2024 def utcoffset(self, dt): pass
2025 b = BetterTry()
2026 t = cls(1, 1, 1, tzinfo=b)
2027 self.failUnless(t.tzinfo is b)
2028
2029 def test_utc_offset_out_of_bounds(self):
2030 class Edgy(tzinfo):
2031 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002032 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002033 def utcoffset(self, dt):
2034 return self.offset
2035
2036 cls = self.theclass
2037 for offset, legit in ((-1440, False),
2038 (-1439, True),
2039 (1439, True),
2040 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002041 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002042 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002043 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002044 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002045 else:
2046 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002047 if legit:
2048 aofs = abs(offset)
2049 h, m = divmod(aofs, 60)
2050 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002051 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002052 t = t.timetz()
2053 self.assertEqual(str(t), "01:02:03" + tag)
2054 else:
2055 self.assertRaises(ValueError, str, t)
2056
2057 def test_tzinfo_classes(self):
2058 cls = self.theclass
2059 class C1(tzinfo):
2060 def utcoffset(self, dt): return None
2061 def dst(self, dt): return None
2062 def tzname(self, dt): return None
2063 for t in (cls(1, 1, 1),
2064 cls(1, 1, 1, tzinfo=None),
2065 cls(1, 1, 1, tzinfo=C1())):
2066 self.failUnless(t.utcoffset() is None)
2067 self.failUnless(t.dst() is None)
2068 self.failUnless(t.tzname() is None)
2069
Tim Peters855fe882002-12-22 03:43:39 +00002070 class C3(tzinfo):
2071 def utcoffset(self, dt): return timedelta(minutes=-1439)
2072 def dst(self, dt): return timedelta(minutes=1439)
2073 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002074 t = cls(1, 1, 1, tzinfo=C3())
2075 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2076 self.assertEqual(t.dst(), timedelta(minutes=1439))
2077 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002078
2079 # Wrong types.
2080 class C4(tzinfo):
2081 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002082 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002083 def tzname(self, dt): return 0
2084 t = cls(1, 1, 1, tzinfo=C4())
2085 self.assertRaises(TypeError, t.utcoffset)
2086 self.assertRaises(TypeError, t.dst)
2087 self.assertRaises(TypeError, t.tzname)
2088
2089 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002090 class C6(tzinfo):
2091 def utcoffset(self, dt): return timedelta(hours=-24)
2092 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002093 t = cls(1, 1, 1, tzinfo=C6())
2094 self.assertRaises(ValueError, t.utcoffset)
2095 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002096
2097 # Not a whole number of minutes.
2098 class C7(tzinfo):
2099 def utcoffset(self, dt): return timedelta(seconds=61)
2100 def dst(self, dt): return timedelta(microseconds=-81)
2101 t = cls(1, 1, 1, tzinfo=C7())
2102 self.assertRaises(ValueError, t.utcoffset)
2103 self.assertRaises(ValueError, t.dst)
2104
Tim Peters4c0db782002-12-26 05:01:19 +00002105 def test_aware_compare(self):
2106 cls = self.theclass
2107
Tim Peters60c76e42002-12-27 00:41:11 +00002108 # Ensure that utcoffset() gets ignored if the comparands have
2109 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002110 class OperandDependentOffset(tzinfo):
2111 def utcoffset(self, t):
2112 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002113 # d0 and d1 equal after adjustment
2114 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002115 else:
Tim Peters397301e2003-01-02 21:28:08 +00002116 # d2 off in the weeds
2117 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002118
2119 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2120 d0 = base.replace(minute=3)
2121 d1 = base.replace(minute=9)
2122 d2 = base.replace(minute=11)
2123 for x in d0, d1, d2:
2124 for y in d0, d1, d2:
2125 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002126 expected = cmp(x.minute, y.minute)
2127 self.assertEqual(got, expected)
2128
2129 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002130 # Note that a time can't actually have an operand-depedent offset,
2131 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2132 # so skip this test for time.
2133 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002134 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2135 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2136 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2137 for x in d0, d1, d2:
2138 for y in d0, d1, d2:
2139 got = cmp(x, y)
2140 if (x is d0 or x is d1) and (y is d0 or y is d1):
2141 expected = 0
2142 elif x is y is d2:
2143 expected = 0
2144 elif x is d2:
2145 expected = -1
2146 else:
2147 assert y is d2
2148 expected = 1
2149 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002150
Tim Peters855fe882002-12-22 03:43:39 +00002151
Tim Peters0bf60bd2003-01-08 20:40:01 +00002152# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002153class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002154 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002155
2156 def test_empty(self):
2157 t = self.theclass()
2158 self.assertEqual(t.hour, 0)
2159 self.assertEqual(t.minute, 0)
2160 self.assertEqual(t.second, 0)
2161 self.assertEqual(t.microsecond, 0)
2162 self.failUnless(t.tzinfo is None)
2163
Tim Peters2a799bf2002-12-16 20:18:38 +00002164 def test_zones(self):
2165 est = FixedOffset(-300, "EST", 1)
2166 utc = FixedOffset(0, "UTC", -2)
2167 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002168 t1 = time( 7, 47, tzinfo=est)
2169 t2 = time(12, 47, tzinfo=utc)
2170 t3 = time(13, 47, tzinfo=met)
2171 t4 = time(microsecond=40)
2172 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002173
2174 self.assertEqual(t1.tzinfo, est)
2175 self.assertEqual(t2.tzinfo, utc)
2176 self.assertEqual(t3.tzinfo, met)
2177 self.failUnless(t4.tzinfo is None)
2178 self.assertEqual(t5.tzinfo, utc)
2179
Tim Peters855fe882002-12-22 03:43:39 +00002180 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2181 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2182 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002183 self.failUnless(t4.utcoffset() is None)
2184 self.assertRaises(TypeError, t1.utcoffset, "no args")
2185
2186 self.assertEqual(t1.tzname(), "EST")
2187 self.assertEqual(t2.tzname(), "UTC")
2188 self.assertEqual(t3.tzname(), "MET")
2189 self.failUnless(t4.tzname() is None)
2190 self.assertRaises(TypeError, t1.tzname, "no args")
2191
Tim Peters855fe882002-12-22 03:43:39 +00002192 self.assertEqual(t1.dst(), timedelta(minutes=1))
2193 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2194 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002195 self.failUnless(t4.dst() is None)
2196 self.assertRaises(TypeError, t1.dst, "no args")
2197
2198 self.assertEqual(hash(t1), hash(t2))
2199 self.assertEqual(hash(t1), hash(t3))
2200 self.assertEqual(hash(t2), hash(t3))
2201
2202 self.assertEqual(t1, t2)
2203 self.assertEqual(t1, t3)
2204 self.assertEqual(t2, t3)
2205 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2206 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2207 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2208
2209 self.assertEqual(str(t1), "07:47:00-05:00")
2210 self.assertEqual(str(t2), "12:47:00+00:00")
2211 self.assertEqual(str(t3), "13:47:00+01:00")
2212 self.assertEqual(str(t4), "00:00:00.000040")
2213 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2214
2215 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2216 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2217 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2218 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2219 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2220
Tim Peters0bf60bd2003-01-08 20:40:01 +00002221 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002222 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2223 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2224 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2225 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2226 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2227
2228 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2229 "07:47:00 %Z=EST %z=-0500")
2230 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2231 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2232
2233 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002234 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002235 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2236 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2237
Tim Petersb92bb712002-12-21 17:44:07 +00002238 # Check that an invalid tzname result raises an exception.
2239 class Badtzname(tzinfo):
2240 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002241 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002242 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2243 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002244
2245 def test_hash_edge_cases(self):
2246 # Offsets that overflow a basic time.
2247 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2248 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2249 self.assertEqual(hash(t1), hash(t2))
2250
2251 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2252 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2253 self.assertEqual(hash(t1), hash(t2))
2254
Tim Peters2a799bf2002-12-16 20:18:38 +00002255 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002256 # Try one without a tzinfo.
2257 args = 20, 59, 16, 64**2
2258 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002259 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002260 green = pickler.dumps(orig, proto)
2261 derived = unpickler.loads(green)
2262 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002263
2264 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002265 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002266 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002267 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002268 green = pickler.dumps(orig, proto)
2269 derived = unpickler.loads(green)
2270 self.assertEqual(orig, derived)
2271 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2272 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2273 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002274
2275 def test_more_bool(self):
2276 # Test cases with non-None tzinfo.
2277 cls = self.theclass
2278
2279 t = cls(0, tzinfo=FixedOffset(-300, ""))
2280 self.failUnless(t)
2281
2282 t = cls(5, tzinfo=FixedOffset(-300, ""))
2283 self.failUnless(t)
2284
2285 t = cls(5, tzinfo=FixedOffset(300, ""))
2286 self.failUnless(not t)
2287
2288 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2289 self.failUnless(not t)
2290
2291 # Mostly ensuring this doesn't overflow internally.
2292 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2293 self.failUnless(t)
2294
2295 # But this should yield a value error -- the utcoffset is bogus.
2296 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2297 self.assertRaises(ValueError, lambda: bool(t))
2298
2299 # Likewise.
2300 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2301 self.assertRaises(ValueError, lambda: bool(t))
2302
Tim Peters12bf3392002-12-24 05:41:27 +00002303 def test_replace(self):
2304 cls = self.theclass
2305 z100 = FixedOffset(100, "+100")
2306 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2307 args = [1, 2, 3, 4, z100]
2308 base = cls(*args)
2309 self.assertEqual(base, base.replace())
2310
2311 i = 0
2312 for name, newval in (("hour", 5),
2313 ("minute", 6),
2314 ("second", 7),
2315 ("microsecond", 8),
2316 ("tzinfo", zm200)):
2317 newargs = args[:]
2318 newargs[i] = newval
2319 expected = cls(*newargs)
2320 got = base.replace(**{name: newval})
2321 self.assertEqual(expected, got)
2322 i += 1
2323
2324 # Ensure we can get rid of a tzinfo.
2325 self.assertEqual(base.tzname(), "+100")
2326 base2 = base.replace(tzinfo=None)
2327 self.failUnless(base2.tzinfo is None)
2328 self.failUnless(base2.tzname() is None)
2329
2330 # Ensure we can add one.
2331 base3 = base2.replace(tzinfo=z100)
2332 self.assertEqual(base, base3)
2333 self.failUnless(base.tzinfo is base3.tzinfo)
2334
2335 # Out of bounds.
2336 base = cls(1)
2337 self.assertRaises(ValueError, base.replace, hour=24)
2338 self.assertRaises(ValueError, base.replace, minute=-1)
2339 self.assertRaises(ValueError, base.replace, second=100)
2340 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2341
Tim Peters60c76e42002-12-27 00:41:11 +00002342 def test_mixed_compare(self):
2343 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002344 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002345 self.assertEqual(t1, t2)
2346 t2 = t2.replace(tzinfo=None)
2347 self.assertEqual(t1, t2)
2348 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2349 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002350 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2351 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002352
Tim Peters0bf60bd2003-01-08 20:40:01 +00002353 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002354 class Varies(tzinfo):
2355 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002356 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002357 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002358 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002359 return self.offset
2360
2361 v = Varies()
2362 t1 = t2.replace(tzinfo=v)
2363 t2 = t2.replace(tzinfo=v)
2364 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2365 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2366 self.assertEqual(t1, t2)
2367
2368 # But if they're not identical, it isn't ignored.
2369 t2 = t2.replace(tzinfo=Varies())
2370 self.failUnless(t1 < t2) # t1's offset counter still going up
2371
Tim Petersa98924a2003-05-17 05:55:19 +00002372 def test_subclass_timetz(self):
2373
2374 class C(self.theclass):
2375 theAnswer = 42
2376
2377 def __new__(cls, *args, **kws):
2378 temp = kws.copy()
2379 extra = temp.pop('extra')
2380 result = self.theclass.__new__(cls, *args, **temp)
2381 result.extra = extra
2382 return result
2383
2384 def newmeth(self, start):
2385 return start + self.hour + self.second
2386
2387 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2388
2389 dt1 = self.theclass(*args)
2390 dt2 = C(*args, **{'extra': 7})
2391
2392 self.assertEqual(dt2.__class__, C)
2393 self.assertEqual(dt2.theAnswer, 42)
2394 self.assertEqual(dt2.extra, 7)
2395 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2396 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2397
Tim Peters4c0db782002-12-26 05:01:19 +00002398
Tim Peters0bf60bd2003-01-08 20:40:01 +00002399# Testing datetime objects with a non-None tzinfo.
2400
Guido van Rossumd8faa362007-04-27 19:54:29 +00002401class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002402 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002403
2404 def test_trivial(self):
2405 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2406 self.assertEqual(dt.year, 1)
2407 self.assertEqual(dt.month, 2)
2408 self.assertEqual(dt.day, 3)
2409 self.assertEqual(dt.hour, 4)
2410 self.assertEqual(dt.minute, 5)
2411 self.assertEqual(dt.second, 6)
2412 self.assertEqual(dt.microsecond, 7)
2413 self.assertEqual(dt.tzinfo, None)
2414
2415 def test_even_more_compare(self):
2416 # The test_compare() and test_more_compare() inherited from TestDate
2417 # and TestDateTime covered non-tzinfo cases.
2418
2419 # Smallest possible after UTC adjustment.
2420 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2421 # Largest possible after UTC adjustment.
2422 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2423 tzinfo=FixedOffset(-1439, ""))
2424
2425 # Make sure those compare correctly, and w/o overflow.
2426 self.failUnless(t1 < t2)
2427 self.failUnless(t1 != t2)
2428 self.failUnless(t2 > t1)
2429
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002430 self.assertEqual(t1, t1)
2431 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002432
2433 # Equal afer adjustment.
2434 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2435 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2436 self.assertEqual(t1, t2)
2437
2438 # Change t1 not to subtract a minute, and t1 should be larger.
2439 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2440 self.failUnless(t1 > t2)
2441
2442 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2443 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2444 self.failUnless(t1 < t2)
2445
2446 # Back to the original t1, but make seconds resolve it.
2447 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2448 second=1)
2449 self.failUnless(t1 > t2)
2450
2451 # Likewise, but make microseconds resolve it.
2452 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2453 microsecond=1)
2454 self.failUnless(t1 > t2)
2455
2456 # Make t2 naive and it should fail.
2457 t2 = self.theclass.min
2458 self.assertRaises(TypeError, lambda: t1 == t2)
2459 self.assertEqual(t2, t2)
2460
2461 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2462 class Naive(tzinfo):
2463 def utcoffset(self, dt): return None
2464 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2465 self.assertRaises(TypeError, lambda: t1 == t2)
2466 self.assertEqual(t2, t2)
2467
2468 # OTOH, it's OK to compare two of these mixing the two ways of being
2469 # naive.
2470 t1 = self.theclass(5, 6, 7)
2471 self.assertEqual(t1, t2)
2472
2473 # Try a bogus uctoffset.
2474 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002475 def utcoffset(self, dt):
2476 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002477 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2478 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002479 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002480
Tim Peters2a799bf2002-12-16 20:18:38 +00002481 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002482 # Try one without a tzinfo.
2483 args = 6, 7, 23, 20, 59, 1, 64**2
2484 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002485 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002486 green = pickler.dumps(orig, proto)
2487 derived = unpickler.loads(green)
2488 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002489
2490 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002491 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002492 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002493 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002494 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002495 green = pickler.dumps(orig, proto)
2496 derived = unpickler.loads(green)
2497 self.assertEqual(orig, derived)
2498 self.failUnless(isinstance(derived.tzinfo,
2499 PicklableFixedOffset))
2500 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2501 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002502
2503 def test_extreme_hashes(self):
2504 # If an attempt is made to hash these via subtracting the offset
2505 # then hashing a datetime object, OverflowError results. The
2506 # Python implementation used to blow up here.
2507 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2508 hash(t)
2509 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2510 tzinfo=FixedOffset(-1439, ""))
2511 hash(t)
2512
2513 # OTOH, an OOB offset should blow up.
2514 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2515 self.assertRaises(ValueError, hash, t)
2516
2517 def test_zones(self):
2518 est = FixedOffset(-300, "EST")
2519 utc = FixedOffset(0, "UTC")
2520 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002521 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2522 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2523 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002524 self.assertEqual(t1.tzinfo, est)
2525 self.assertEqual(t2.tzinfo, utc)
2526 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002527 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2528 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2529 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002530 self.assertEqual(t1.tzname(), "EST")
2531 self.assertEqual(t2.tzname(), "UTC")
2532 self.assertEqual(t3.tzname(), "MET")
2533 self.assertEqual(hash(t1), hash(t2))
2534 self.assertEqual(hash(t1), hash(t3))
2535 self.assertEqual(hash(t2), hash(t3))
2536 self.assertEqual(t1, t2)
2537 self.assertEqual(t1, t3)
2538 self.assertEqual(t2, t3)
2539 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2540 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2541 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002542 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002543 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2544 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2545 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2546
2547 def test_combine(self):
2548 met = FixedOffset(60, "MET")
2549 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002550 tz = time(18, 45, 3, 1234, tzinfo=met)
2551 dt = datetime.combine(d, tz)
2552 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002553 tzinfo=met))
2554
2555 def test_extract(self):
2556 met = FixedOffset(60, "MET")
2557 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2558 self.assertEqual(dt.date(), date(2002, 3, 4))
2559 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002560 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002561
2562 def test_tz_aware_arithmetic(self):
2563 import random
2564
2565 now = self.theclass.now()
2566 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002567 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002568 nowaware = self.theclass.combine(now.date(), timeaware)
2569 self.failUnless(nowaware.tzinfo is tz55)
2570 self.assertEqual(nowaware.timetz(), timeaware)
2571
2572 # Can't mix aware and non-aware.
2573 self.assertRaises(TypeError, lambda: now - nowaware)
2574 self.assertRaises(TypeError, lambda: nowaware - now)
2575
Tim Peters0bf60bd2003-01-08 20:40:01 +00002576 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002577 self.assertRaises(TypeError, lambda: now + nowaware)
2578 self.assertRaises(TypeError, lambda: nowaware + now)
2579 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2580
2581 # Subtracting should yield 0.
2582 self.assertEqual(now - now, timedelta(0))
2583 self.assertEqual(nowaware - nowaware, timedelta(0))
2584
2585 # Adding a delta should preserve tzinfo.
2586 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2587 nowawareplus = nowaware + delta
2588 self.failUnless(nowaware.tzinfo is tz55)
2589 nowawareplus2 = delta + nowaware
2590 self.failUnless(nowawareplus2.tzinfo is tz55)
2591 self.assertEqual(nowawareplus, nowawareplus2)
2592
2593 # that - delta should be what we started with, and that - what we
2594 # started with should be delta.
2595 diff = nowawareplus - delta
2596 self.failUnless(diff.tzinfo is tz55)
2597 self.assertEqual(nowaware, diff)
2598 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2599 self.assertEqual(nowawareplus - nowaware, delta)
2600
2601 # Make up a random timezone.
2602 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002603 # Attach it to nowawareplus.
2604 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002605 self.failUnless(nowawareplus.tzinfo is tzr)
2606 # Make sure the difference takes the timezone adjustments into account.
2607 got = nowaware - nowawareplus
2608 # Expected: (nowaware base - nowaware offset) -
2609 # (nowawareplus base - nowawareplus offset) =
2610 # (nowaware base - nowawareplus base) +
2611 # (nowawareplus offset - nowaware offset) =
2612 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002613 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002614 self.assertEqual(got, expected)
2615
2616 # Try max possible difference.
2617 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2618 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2619 tzinfo=FixedOffset(-1439, "max"))
2620 maxdiff = max - min
2621 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2622 timedelta(minutes=2*1439))
2623
2624 def test_tzinfo_now(self):
2625 meth = self.theclass.now
2626 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2627 base = meth()
2628 # Try with and without naming the keyword.
2629 off42 = FixedOffset(42, "42")
2630 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002631 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002632 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002633 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002634 # Bad argument with and w/o naming the keyword.
2635 self.assertRaises(TypeError, meth, 16)
2636 self.assertRaises(TypeError, meth, tzinfo=16)
2637 # Bad keyword name.
2638 self.assertRaises(TypeError, meth, tinfo=off42)
2639 # Too many args.
2640 self.assertRaises(TypeError, meth, off42, off42)
2641
Tim Peters10cadce2003-01-23 19:58:02 +00002642 # We don't know which time zone we're in, and don't have a tzinfo
2643 # class to represent it, so seeing whether a tz argument actually
2644 # does a conversion is tricky.
2645 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2646 utc = FixedOffset(0, "utc", 0)
2647 for dummy in range(3):
2648 now = datetime.now(weirdtz)
2649 self.failUnless(now.tzinfo is weirdtz)
2650 utcnow = datetime.utcnow().replace(tzinfo=utc)
2651 now2 = utcnow.astimezone(weirdtz)
2652 if abs(now - now2) < timedelta(seconds=30):
2653 break
2654 # Else the code is broken, or more than 30 seconds passed between
2655 # calls; assuming the latter, just try again.
2656 else:
2657 # Three strikes and we're out.
2658 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2659
Tim Peters2a799bf2002-12-16 20:18:38 +00002660 def test_tzinfo_fromtimestamp(self):
2661 import time
2662 meth = self.theclass.fromtimestamp
2663 ts = time.time()
2664 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2665 base = meth(ts)
2666 # Try with and without naming the keyword.
2667 off42 = FixedOffset(42, "42")
2668 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002669 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002670 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002671 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002672 # Bad argument with and w/o naming the keyword.
2673 self.assertRaises(TypeError, meth, ts, 16)
2674 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2675 # Bad keyword name.
2676 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2677 # Too many args.
2678 self.assertRaises(TypeError, meth, ts, off42, off42)
2679 # Too few args.
2680 self.assertRaises(TypeError, meth)
2681
Tim Peters2a44a8d2003-01-23 20:53:10 +00002682 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002683 timestamp = 1000000000
2684 utcdatetime = datetime.utcfromtimestamp(timestamp)
2685 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2686 # But on some flavor of Mac, it's nowhere near that. So we can't have
2687 # any idea here what time that actually is, we can only test that
2688 # relative changes match.
2689 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2690 tz = FixedOffset(utcoffset, "tz", 0)
2691 expected = utcdatetime + utcoffset
2692 got = datetime.fromtimestamp(timestamp, tz)
2693 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002694
Tim Peters2a799bf2002-12-16 20:18:38 +00002695 def test_tzinfo_utcnow(self):
2696 meth = self.theclass.utcnow
2697 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2698 base = meth()
2699 # Try with and without naming the keyword; for whatever reason,
2700 # utcnow() doesn't accept a tzinfo argument.
2701 off42 = FixedOffset(42, "42")
2702 self.assertRaises(TypeError, meth, off42)
2703 self.assertRaises(TypeError, meth, tzinfo=off42)
2704
2705 def test_tzinfo_utcfromtimestamp(self):
2706 import time
2707 meth = self.theclass.utcfromtimestamp
2708 ts = time.time()
2709 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2710 base = meth(ts)
2711 # Try with and without naming the keyword; for whatever reason,
2712 # utcfromtimestamp() doesn't accept a tzinfo argument.
2713 off42 = FixedOffset(42, "42")
2714 self.assertRaises(TypeError, meth, ts, off42)
2715 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2716
2717 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002718 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002719 # DST flag.
2720 class DST(tzinfo):
2721 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002722 if isinstance(dstvalue, int):
2723 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002724 self.dstvalue = dstvalue
2725 def dst(self, dt):
2726 return self.dstvalue
2727
2728 cls = self.theclass
2729 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2730 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2731 t = d.timetuple()
2732 self.assertEqual(1, t.tm_year)
2733 self.assertEqual(1, t.tm_mon)
2734 self.assertEqual(1, t.tm_mday)
2735 self.assertEqual(10, t.tm_hour)
2736 self.assertEqual(20, t.tm_min)
2737 self.assertEqual(30, t.tm_sec)
2738 self.assertEqual(0, t.tm_wday)
2739 self.assertEqual(1, t.tm_yday)
2740 self.assertEqual(flag, t.tm_isdst)
2741
2742 # dst() returns wrong type.
2743 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2744
2745 # dst() at the edge.
2746 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2747 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2748
2749 # dst() out of range.
2750 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2751 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2752
2753 def test_utctimetuple(self):
2754 class DST(tzinfo):
2755 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002756 if isinstance(dstvalue, int):
2757 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002758 self.dstvalue = dstvalue
2759 def dst(self, dt):
2760 return self.dstvalue
2761
2762 cls = self.theclass
2763 # This can't work: DST didn't implement utcoffset.
2764 self.assertRaises(NotImplementedError,
2765 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2766
2767 class UOFS(DST):
2768 def __init__(self, uofs, dofs=None):
2769 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002770 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002771 def utcoffset(self, dt):
2772 return self.uofs
2773
2774 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2775 # in effect for a UTC time.
2776 for dstvalue in -33, 33, 0, None:
2777 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2778 t = d.utctimetuple()
2779 self.assertEqual(d.year, t.tm_year)
2780 self.assertEqual(d.month, t.tm_mon)
2781 self.assertEqual(d.day, t.tm_mday)
2782 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2783 self.assertEqual(13, t.tm_min)
2784 self.assertEqual(d.second, t.tm_sec)
2785 self.assertEqual(d.weekday(), t.tm_wday)
2786 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2787 t.tm_yday)
2788 self.assertEqual(0, t.tm_isdst)
2789
2790 # At the edges, UTC adjustment can normalize into years out-of-range
2791 # for a datetime object. Ensure that a correct timetuple is
2792 # created anyway.
2793 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2794 # That goes back 1 minute less than a full day.
2795 t = tiny.utctimetuple()
2796 self.assertEqual(t.tm_year, MINYEAR-1)
2797 self.assertEqual(t.tm_mon, 12)
2798 self.assertEqual(t.tm_mday, 31)
2799 self.assertEqual(t.tm_hour, 0)
2800 self.assertEqual(t.tm_min, 1)
2801 self.assertEqual(t.tm_sec, 37)
2802 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2803 self.assertEqual(t.tm_isdst, 0)
2804
2805 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2806 # That goes forward 1 minute less than a full day.
2807 t = huge.utctimetuple()
2808 self.assertEqual(t.tm_year, MAXYEAR+1)
2809 self.assertEqual(t.tm_mon, 1)
2810 self.assertEqual(t.tm_mday, 1)
2811 self.assertEqual(t.tm_hour, 23)
2812 self.assertEqual(t.tm_min, 58)
2813 self.assertEqual(t.tm_sec, 37)
2814 self.assertEqual(t.tm_yday, 1)
2815 self.assertEqual(t.tm_isdst, 0)
2816
2817 def test_tzinfo_isoformat(self):
2818 zero = FixedOffset(0, "+00:00")
2819 plus = FixedOffset(220, "+03:40")
2820 minus = FixedOffset(-231, "-03:51")
2821 unknown = FixedOffset(None, "")
2822
2823 cls = self.theclass
2824 datestr = '0001-02-03'
2825 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002826 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002827 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2828 timestr = '04:05:59' + (us and '.987001' or '')
2829 ofsstr = ofs is not None and d.tzname() or ''
2830 tailstr = timestr + ofsstr
2831 iso = d.isoformat()
2832 self.assertEqual(iso, datestr + 'T' + tailstr)
2833 self.assertEqual(iso, d.isoformat('T'))
2834 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002835 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002836 self.assertEqual(str(d), datestr + ' ' + tailstr)
2837
Tim Peters12bf3392002-12-24 05:41:27 +00002838 def test_replace(self):
2839 cls = self.theclass
2840 z100 = FixedOffset(100, "+100")
2841 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2842 args = [1, 2, 3, 4, 5, 6, 7, z100]
2843 base = cls(*args)
2844 self.assertEqual(base, base.replace())
2845
2846 i = 0
2847 for name, newval in (("year", 2),
2848 ("month", 3),
2849 ("day", 4),
2850 ("hour", 5),
2851 ("minute", 6),
2852 ("second", 7),
2853 ("microsecond", 8),
2854 ("tzinfo", zm200)):
2855 newargs = args[:]
2856 newargs[i] = newval
2857 expected = cls(*newargs)
2858 got = base.replace(**{name: newval})
2859 self.assertEqual(expected, got)
2860 i += 1
2861
2862 # Ensure we can get rid of a tzinfo.
2863 self.assertEqual(base.tzname(), "+100")
2864 base2 = base.replace(tzinfo=None)
2865 self.failUnless(base2.tzinfo is None)
2866 self.failUnless(base2.tzname() is None)
2867
2868 # Ensure we can add one.
2869 base3 = base2.replace(tzinfo=z100)
2870 self.assertEqual(base, base3)
2871 self.failUnless(base.tzinfo is base3.tzinfo)
2872
2873 # Out of bounds.
2874 base = cls(2000, 2, 29)
2875 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002876
Tim Peters80475bb2002-12-25 07:40:55 +00002877 def test_more_astimezone(self):
2878 # The inherited test_astimezone covered some trivial and error cases.
2879 fnone = FixedOffset(None, "None")
2880 f44m = FixedOffset(44, "44")
2881 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2882
Tim Peters10cadce2003-01-23 19:58:02 +00002883 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002884 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002885 # Replacing with degenerate tzinfo raises an exception.
2886 self.assertRaises(ValueError, dt.astimezone, fnone)
2887 # Ditto with None tz.
2888 self.assertRaises(TypeError, dt.astimezone, None)
2889 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002890 x = dt.astimezone(dt.tzinfo)
2891 self.failUnless(x.tzinfo is f44m)
2892 self.assertEqual(x.date(), dt.date())
2893 self.assertEqual(x.time(), dt.time())
2894
2895 # Replacing with different tzinfo does adjust.
2896 got = dt.astimezone(fm5h)
2897 self.failUnless(got.tzinfo is fm5h)
2898 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2899 expected = dt - dt.utcoffset() # in effect, convert to UTC
2900 expected += fm5h.utcoffset(dt) # and from there to local time
2901 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2902 self.assertEqual(got.date(), expected.date())
2903 self.assertEqual(got.time(), expected.time())
2904 self.assertEqual(got.timetz(), expected.timetz())
2905 self.failUnless(got.tzinfo is expected.tzinfo)
2906 self.assertEqual(got, expected)
2907
Tim Peters4c0db782002-12-26 05:01:19 +00002908 def test_aware_subtract(self):
2909 cls = self.theclass
2910
Tim Peters60c76e42002-12-27 00:41:11 +00002911 # Ensure that utcoffset() is ignored when the operands have the
2912 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002913 class OperandDependentOffset(tzinfo):
2914 def utcoffset(self, t):
2915 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002916 # d0 and d1 equal after adjustment
2917 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002918 else:
Tim Peters397301e2003-01-02 21:28:08 +00002919 # d2 off in the weeds
2920 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002921
2922 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2923 d0 = base.replace(minute=3)
2924 d1 = base.replace(minute=9)
2925 d2 = base.replace(minute=11)
2926 for x in d0, d1, d2:
2927 for y in d0, d1, d2:
2928 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002929 expected = timedelta(minutes=x.minute - y.minute)
2930 self.assertEqual(got, expected)
2931
2932 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2933 # ignored.
2934 base = cls(8, 9, 10, 11, 12, 13, 14)
2935 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2936 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2937 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2938 for x in d0, d1, d2:
2939 for y in d0, d1, d2:
2940 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002941 if (x is d0 or x is d1) and (y is d0 or y is d1):
2942 expected = timedelta(0)
2943 elif x is y is d2:
2944 expected = timedelta(0)
2945 elif x is d2:
2946 expected = timedelta(minutes=(11-59)-0)
2947 else:
2948 assert y is d2
2949 expected = timedelta(minutes=0-(11-59))
2950 self.assertEqual(got, expected)
2951
Tim Peters60c76e42002-12-27 00:41:11 +00002952 def test_mixed_compare(self):
2953 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002954 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002955 self.assertEqual(t1, t2)
2956 t2 = t2.replace(tzinfo=None)
2957 self.assertEqual(t1, t2)
2958 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2959 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002960 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2961 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002962
Tim Peters0bf60bd2003-01-08 20:40:01 +00002963 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002964 class Varies(tzinfo):
2965 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002966 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002967 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002968 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002969 return self.offset
2970
2971 v = Varies()
2972 t1 = t2.replace(tzinfo=v)
2973 t2 = t2.replace(tzinfo=v)
2974 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2975 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2976 self.assertEqual(t1, t2)
2977
2978 # But if they're not identical, it isn't ignored.
2979 t2 = t2.replace(tzinfo=Varies())
2980 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002981
Tim Petersa98924a2003-05-17 05:55:19 +00002982 def test_subclass_datetimetz(self):
2983
2984 class C(self.theclass):
2985 theAnswer = 42
2986
2987 def __new__(cls, *args, **kws):
2988 temp = kws.copy()
2989 extra = temp.pop('extra')
2990 result = self.theclass.__new__(cls, *args, **temp)
2991 result.extra = extra
2992 return result
2993
2994 def newmeth(self, start):
2995 return start + self.hour + self.year
2996
2997 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2998
2999 dt1 = self.theclass(*args)
3000 dt2 = C(*args, **{'extra': 7})
3001
3002 self.assertEqual(dt2.__class__, C)
3003 self.assertEqual(dt2.theAnswer, 42)
3004 self.assertEqual(dt2.extra, 7)
3005 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3006 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3007
Tim Peters621818b2002-12-29 23:44:49 +00003008# Pain to set up DST-aware tzinfo classes.
3009
3010def first_sunday_on_or_after(dt):
3011 days_to_go = 6 - dt.weekday()
3012 if days_to_go:
3013 dt += timedelta(days_to_go)
3014 return dt
3015
3016ZERO = timedelta(0)
3017HOUR = timedelta(hours=1)
3018DAY = timedelta(days=1)
3019# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3020DSTSTART = datetime(1, 4, 1, 2)
3021# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003022# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3023# being standard time on that day, there is no spelling in local time of
3024# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3025DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003026
3027class USTimeZone(tzinfo):
3028
3029 def __init__(self, hours, reprname, stdname, dstname):
3030 self.stdoffset = timedelta(hours=hours)
3031 self.reprname = reprname
3032 self.stdname = stdname
3033 self.dstname = dstname
3034
3035 def __repr__(self):
3036 return self.reprname
3037
3038 def tzname(self, dt):
3039 if self.dst(dt):
3040 return self.dstname
3041 else:
3042 return self.stdname
3043
3044 def utcoffset(self, dt):
3045 return self.stdoffset + self.dst(dt)
3046
3047 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003048 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003049 # An exception instead may be sensible here, in one or more of
3050 # the cases.
3051 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003052 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003053
3054 # Find first Sunday in April.
3055 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3056 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3057
3058 # Find last Sunday in October.
3059 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3060 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3061
Tim Peters621818b2002-12-29 23:44:49 +00003062 # Can't compare naive to aware objects, so strip the timezone from
3063 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003064 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003065 return HOUR
3066 else:
3067 return ZERO
3068
Tim Peters521fc152002-12-31 17:36:56 +00003069Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3070Central = USTimeZone(-6, "Central", "CST", "CDT")
3071Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3072Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003073utc_real = FixedOffset(0, "UTC", 0)
3074# For better test coverage, we want another flavor of UTC that's west of
3075# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003076utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003077
3078class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003079 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003080 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003081 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003082
Tim Peters0bf60bd2003-01-08 20:40:01 +00003083 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003084
Tim Peters521fc152002-12-31 17:36:56 +00003085 # Check a time that's inside DST.
3086 def checkinside(self, dt, tz, utc, dston, dstoff):
3087 self.assertEqual(dt.dst(), HOUR)
3088
3089 # Conversion to our own timezone is always an identity.
3090 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003091
3092 asutc = dt.astimezone(utc)
3093 there_and_back = asutc.astimezone(tz)
3094
3095 # Conversion to UTC and back isn't always an identity here,
3096 # because there are redundant spellings (in local time) of
3097 # UTC time when DST begins: the clock jumps from 1:59:59
3098 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3099 # make sense then. The classes above treat 2:MM:SS as
3100 # daylight time then (it's "after 2am"), really an alias
3101 # for 1:MM:SS standard time. The latter form is what
3102 # conversion back from UTC produces.
3103 if dt.date() == dston.date() and dt.hour == 2:
3104 # We're in the redundant hour, and coming back from
3105 # UTC gives the 1:MM:SS standard-time spelling.
3106 self.assertEqual(there_and_back + HOUR, dt)
3107 # Although during was considered to be in daylight
3108 # time, there_and_back is not.
3109 self.assertEqual(there_and_back.dst(), ZERO)
3110 # They're the same times in UTC.
3111 self.assertEqual(there_and_back.astimezone(utc),
3112 dt.astimezone(utc))
3113 else:
3114 # We're not in the redundant hour.
3115 self.assertEqual(dt, there_and_back)
3116
Tim Peters327098a2003-01-20 22:54:38 +00003117 # Because we have a redundant spelling when DST begins, there is
3118 # (unforunately) an hour when DST ends that can't be spelled at all in
3119 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3120 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3121 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3122 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3123 # expressed in local time. Nevertheless, we want conversion back
3124 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003125 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003126 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003127 if dt.date() == dstoff.date() and dt.hour == 0:
3128 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003129 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003130 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3131 nexthour_utc += HOUR
3132 nexthour_tz = nexthour_utc.astimezone(tz)
3133 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003134 else:
Tim Peters327098a2003-01-20 22:54:38 +00003135 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003136
3137 # Check a time that's outside DST.
3138 def checkoutside(self, dt, tz, utc):
3139 self.assertEqual(dt.dst(), ZERO)
3140
3141 # Conversion to our own timezone is always an identity.
3142 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003143
3144 # Converting to UTC and back is an identity too.
3145 asutc = dt.astimezone(utc)
3146 there_and_back = asutc.astimezone(tz)
3147 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003148
Tim Peters1024bf82002-12-30 17:09:40 +00003149 def convert_between_tz_and_utc(self, tz, utc):
3150 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003151 # Because 1:MM on the day DST ends is taken as being standard time,
3152 # there is no spelling in tz for the last hour of daylight time.
3153 # For purposes of the test, the last hour of DST is 0:MM, which is
3154 # taken as being daylight time (and 1:MM is taken as being standard
3155 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003156 dstoff = self.dstoff.replace(tzinfo=tz)
3157 for delta in (timedelta(weeks=13),
3158 DAY,
3159 HOUR,
3160 timedelta(minutes=1),
3161 timedelta(microseconds=1)):
3162
Tim Peters521fc152002-12-31 17:36:56 +00003163 self.checkinside(dston, tz, utc, dston, dstoff)
3164 for during in dston + delta, dstoff - delta:
3165 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003166
Tim Peters521fc152002-12-31 17:36:56 +00003167 self.checkoutside(dstoff, tz, utc)
3168 for outside in dston - delta, dstoff + delta:
3169 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003170
Tim Peters621818b2002-12-29 23:44:49 +00003171 def test_easy(self):
3172 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003173 self.convert_between_tz_and_utc(Eastern, utc_real)
3174 self.convert_between_tz_and_utc(Pacific, utc_real)
3175 self.convert_between_tz_and_utc(Eastern, utc_fake)
3176 self.convert_between_tz_and_utc(Pacific, utc_fake)
3177 # The next is really dancing near the edge. It works because
3178 # Pacific and Eastern are far enough apart that their "problem
3179 # hours" don't overlap.
3180 self.convert_between_tz_and_utc(Eastern, Pacific)
3181 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003182 # OTOH, these fail! Don't enable them. The difficulty is that
3183 # the edge case tests assume that every hour is representable in
3184 # the "utc" class. This is always true for a fixed-offset tzinfo
3185 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3186 # For these adjacent DST-aware time zones, the range of time offsets
3187 # tested ends up creating hours in the one that aren't representable
3188 # in the other. For the same reason, we would see failures in the
3189 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3190 # offset deltas in convert_between_tz_and_utc().
3191 #
3192 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3193 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003194
Tim Petersf3615152003-01-01 21:51:37 +00003195 def test_tricky(self):
3196 # 22:00 on day before daylight starts.
3197 fourback = self.dston - timedelta(hours=4)
3198 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003199 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003200 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3201 # 2", we should get the 3 spelling.
3202 # If we plug 22:00 the day before into Eastern, it "looks like std
3203 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3204 # to 22:00 lands on 2:00, which makes no sense in local time (the
3205 # local clock jumps from 1 to 3). The point here is to make sure we
3206 # get the 3 spelling.
3207 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003208 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003209 self.assertEqual(expected, got)
3210
3211 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3212 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003213 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003214 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3215 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3216 # spelling.
3217 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003218 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003219 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003220
Tim Petersadf64202003-01-04 06:03:15 +00003221 # Now on the day DST ends, we want "repeat an hour" behavior.
3222 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3223 # EST 23:MM 0:MM 1:MM 2:MM
3224 # EDT 0:MM 1:MM 2:MM 3:MM
3225 # wall 0:MM 1:MM 1:MM 2:MM against these
3226 for utc in utc_real, utc_fake:
3227 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003228 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003229 # Convert that to UTC.
3230 first_std_hour -= tz.utcoffset(None)
3231 # Adjust for possibly fake UTC.
3232 asutc = first_std_hour + utc.utcoffset(None)
3233 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3234 # tz=Eastern.
3235 asutcbase = asutc.replace(tzinfo=utc)
3236 for tzhour in (0, 1, 1, 2):
3237 expectedbase = self.dstoff.replace(hour=tzhour)
3238 for minute in 0, 30, 59:
3239 expected = expectedbase.replace(minute=minute)
3240 asutc = asutcbase.replace(minute=minute)
3241 astz = asutc.astimezone(tz)
3242 self.assertEqual(astz.replace(tzinfo=None), expected)
3243 asutcbase += HOUR
3244
3245
Tim Peters710fb152003-01-02 19:35:54 +00003246 def test_bogus_dst(self):
3247 class ok(tzinfo):
3248 def utcoffset(self, dt): return HOUR
3249 def dst(self, dt): return HOUR
3250
3251 now = self.theclass.now().replace(tzinfo=utc_real)
3252 # Doesn't blow up.
3253 now.astimezone(ok())
3254
3255 # Does blow up.
3256 class notok(ok):
3257 def dst(self, dt): return None
3258 self.assertRaises(ValueError, now.astimezone, notok())
3259
Tim Peters52dcce22003-01-23 16:36:11 +00003260 def test_fromutc(self):
3261 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3262 now = datetime.utcnow().replace(tzinfo=utc_real)
3263 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3264 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3265 enow = Eastern.fromutc(now) # doesn't blow up
3266 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3267 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3268 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3269
3270 # Always converts UTC to standard time.
3271 class FauxUSTimeZone(USTimeZone):
3272 def fromutc(self, dt):
3273 return dt + self.stdoffset
3274 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3275
3276 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3277 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3278 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3279
3280 # Check around DST start.
3281 start = self.dston.replace(hour=4, tzinfo=Eastern)
3282 fstart = start.replace(tzinfo=FEastern)
3283 for wall in 23, 0, 1, 3, 4, 5:
3284 expected = start.replace(hour=wall)
3285 if wall == 23:
3286 expected -= timedelta(days=1)
3287 got = Eastern.fromutc(start)
3288 self.assertEqual(expected, got)
3289
3290 expected = fstart + FEastern.stdoffset
3291 got = FEastern.fromutc(fstart)
3292 self.assertEqual(expected, got)
3293
3294 # Ensure astimezone() calls fromutc() too.
3295 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3296 self.assertEqual(expected, got)
3297
3298 start += HOUR
3299 fstart += HOUR
3300
3301 # Check around DST end.
3302 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3303 fstart = start.replace(tzinfo=FEastern)
3304 for wall in 0, 1, 1, 2, 3, 4:
3305 expected = start.replace(hour=wall)
3306 got = Eastern.fromutc(start)
3307 self.assertEqual(expected, got)
3308
3309 expected = fstart + FEastern.stdoffset
3310 got = FEastern.fromutc(fstart)
3311 self.assertEqual(expected, got)
3312
3313 # Ensure astimezone() calls fromutc() too.
3314 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3315 self.assertEqual(expected, got)
3316
3317 start += HOUR
3318 fstart += HOUR
3319
Tim Peters710fb152003-01-02 19:35:54 +00003320
Tim Peters528ca532004-09-16 01:30:50 +00003321#############################################################################
3322# oddballs
3323
3324class Oddballs(unittest.TestCase):
3325
3326 def test_bug_1028306(self):
3327 # Trying to compare a date to a datetime should act like a mixed-
3328 # type comparison, despite that datetime is a subclass of date.
3329 as_date = date.today()
3330 as_datetime = datetime.combine(as_date, time())
3331 self.assert_(as_date != as_datetime)
3332 self.assert_(as_datetime != as_date)
3333 self.assert_(not as_date == as_datetime)
3334 self.assert_(not as_datetime == as_date)
3335 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3336 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3337 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3338 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3339 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3340 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3341 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3342 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3343
3344 # Neverthelss, comparison should work with the base-class (date)
3345 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003346 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003347 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003348 as_different = as_datetime.replace(day= different_day)
3349 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003350
3351 # And date should compare with other subclasses of date. If a
3352 # subclass wants to stop this, it's up to the subclass to do so.
3353 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3354 self.assertEqual(as_date, date_sc)
3355 self.assertEqual(date_sc, as_date)
3356
3357 # Ditto for datetimes.
3358 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3359 as_date.day, 0, 0, 0)
3360 self.assertEqual(as_datetime, datetime_sc)
3361 self.assertEqual(datetime_sc, as_datetime)
3362
Tim Peters2a799bf2002-12-16 20:18:38 +00003363def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003364 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003365
3366if __name__ == "__main__":
3367 test_main()