blob: 8ac71607b8fe2ae833e6169dd81f783886b396f7 [file] [log] [blame]
Tim Peters0bf60bd2003-01-08 20:40:01 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
Tim Peters2a799bf2002-12-16 20:18:38 +00005
Guido van Rossumd8faa362007-04-27 19:54:29 +00006import os
Tim Peters2a799bf2002-12-16 20:18:38 +00007import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00008import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00009import unittest
10
11from test import test_support
12
13from datetime import MINYEAR, MAXYEAR
14from datetime import timedelta
15from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000016from datetime import time
17from datetime import date, datetime
18
Guido van Rossumbe6fe542007-07-19 23:55:34 +000019pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
20assert len(pickle_choices) == 3
Guido van Rossum177e41a2003-01-30 22:06:23 +000021
Tim Peters68124bb2003-02-08 03:46:31 +000022# An arbitrary collection of objects of non-datetime types, for testing
23# mixed-type comparisons.
Guido van Rossume2a383d2007-01-15 16:59:06 +000024OTHERSTUFF = (10, 10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000025
Tim Peters2a799bf2002-12-16 20:18:38 +000026
27#############################################################################
28# module tests
29
30class TestModule(unittest.TestCase):
31
32 def test_constants(self):
33 import datetime
34 self.assertEqual(datetime.MINYEAR, 1)
35 self.assertEqual(datetime.MAXYEAR, 9999)
36
37#############################################################################
38# tzinfo tests
39
40class FixedOffset(tzinfo):
41 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000042 if isinstance(offset, int):
43 offset = timedelta(minutes=offset)
44 if isinstance(dstoffset, int):
45 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000046 self.__offset = offset
47 self.__name = name
48 self.__dstoffset = dstoffset
49 def __repr__(self):
50 return self.__name.lower()
51 def utcoffset(self, dt):
52 return self.__offset
53 def tzname(self, dt):
54 return self.__name
55 def dst(self, dt):
56 return self.__dstoffset
57
Tim Petersfb8472c2002-12-21 05:04:42 +000058class PicklableFixedOffset(FixedOffset):
59 def __init__(self, offset=None, name=None, dstoffset=None):
60 FixedOffset.__init__(self, offset, name, dstoffset)
61
Tim Peters2a799bf2002-12-16 20:18:38 +000062class TestTZInfo(unittest.TestCase):
63
64 def test_non_abstractness(self):
65 # In order to allow subclasses to get pickled, the C implementation
66 # wasn't able to get away with having __init__ raise
67 # NotImplementedError.
68 useless = tzinfo()
69 dt = datetime.max
70 self.assertRaises(NotImplementedError, useless.tzname, dt)
71 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
72 self.assertRaises(NotImplementedError, useless.dst, dt)
73
74 def test_subclass_must_override(self):
75 class NotEnough(tzinfo):
76 def __init__(self, offset, name):
77 self.__offset = offset
78 self.__name = name
79 self.failUnless(issubclass(NotEnough, tzinfo))
80 ne = NotEnough(3, "NotByALongShot")
81 self.failUnless(isinstance(ne, tzinfo))
82
83 dt = datetime.now()
84 self.assertRaises(NotImplementedError, ne.tzname, dt)
85 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
86 self.assertRaises(NotImplementedError, ne.dst, dt)
87
88 def test_normal(self):
89 fo = FixedOffset(3, "Three")
90 self.failUnless(isinstance(fo, tzinfo))
91 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +000092 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +000093 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +000094 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +000095
96 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +000097 # There's no point to pickling tzinfo objects on their own (they
98 # carry no data), but they need to be picklable anyway else
99 # concrete subclasses can't be pickled.
100 orig = tzinfo.__new__(tzinfo)
101 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000102 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000103 green = pickler.dumps(orig, proto)
104 derived = unpickler.loads(green)
105 self.failUnless(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000106
107 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000108 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000109 offset = timedelta(minutes=-300)
110 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000111 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000112 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000113 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000114 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000115 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000116 green = pickler.dumps(orig, proto)
117 derived = unpickler.loads(green)
118 self.failUnless(isinstance(derived, tzinfo))
119 self.failUnless(type(derived) is PicklableFixedOffset)
120 self.assertEqual(derived.utcoffset(None), offset)
121 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000122
123#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000124# Base clase for testing a particular aspect of timedelta, time, date and
125# datetime comparisons.
126
Guido van Rossumd8faa362007-04-27 19:54:29 +0000127class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000128 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
129
130 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
131 # legit constructor.
132
133 def test_harmless_mixed_comparison(self):
134 me = self.theclass(1, 1, 1)
135
136 self.failIf(me == ())
137 self.failUnless(me != ())
138 self.failIf(() == me)
139 self.failUnless(() != me)
140
Guido van Rossume2a383d2007-01-15 16:59:06 +0000141 self.failUnless(me in [1, 20, [], me])
142 self.failIf(me not in [1, 20, [], me])
Tim Peters07534a62003-02-07 22:50:28 +0000143
Guido van Rossume2a383d2007-01-15 16:59:06 +0000144 self.failUnless([] in [me, 1, 20, []])
145 self.failIf([] not in [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000146
147 def test_harmful_mixed_comparison(self):
148 me = self.theclass(1, 1, 1)
149
150 self.assertRaises(TypeError, lambda: me < ())
151 self.assertRaises(TypeError, lambda: me <= ())
152 self.assertRaises(TypeError, lambda: me > ())
153 self.assertRaises(TypeError, lambda: me >= ())
154
155 self.assertRaises(TypeError, lambda: () < me)
156 self.assertRaises(TypeError, lambda: () <= me)
157 self.assertRaises(TypeError, lambda: () > me)
158 self.assertRaises(TypeError, lambda: () >= me)
159
160 self.assertRaises(TypeError, cmp, (), me)
161 self.assertRaises(TypeError, cmp, me, ())
162
163#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000164# timedelta tests
165
Guido van Rossumd8faa362007-04-27 19:54:29 +0000166class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000167
168 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000169
170 def test_constructor(self):
171 eq = self.assertEqual
172 td = timedelta
173
174 # Check keyword args to constructor
175 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
176 milliseconds=0, microseconds=0))
177 eq(td(1), td(days=1))
178 eq(td(0, 1), td(seconds=1))
179 eq(td(0, 0, 1), td(microseconds=1))
180 eq(td(weeks=1), td(days=7))
181 eq(td(days=1), td(hours=24))
182 eq(td(hours=1), td(minutes=60))
183 eq(td(minutes=1), td(seconds=60))
184 eq(td(seconds=1), td(milliseconds=1000))
185 eq(td(milliseconds=1), td(microseconds=1000))
186
187 # Check float args to constructor
188 eq(td(weeks=1.0/7), td(days=1))
189 eq(td(days=1.0/24), td(hours=1))
190 eq(td(hours=1.0/60), td(minutes=1))
191 eq(td(minutes=1.0/60), td(seconds=1))
192 eq(td(seconds=0.001), td(milliseconds=1))
193 eq(td(milliseconds=0.001), td(microseconds=1))
194
195 def test_computations(self):
196 eq = self.assertEqual
197 td = timedelta
198
199 a = td(7) # One week
200 b = td(0, 60) # One minute
201 c = td(0, 0, 1000) # One millisecond
202 eq(a+b+c, td(7, 60, 1000))
203 eq(a-b, td(6, 24*3600 - 60))
204 eq(-a, td(-7))
205 eq(+a, td(7))
206 eq(-b, td(-1, 24*3600 - 60))
207 eq(-c, td(-1, 24*3600 - 1, 999000))
208 eq(abs(a), a)
209 eq(abs(-a), a)
210 eq(td(6, 24*3600), a)
211 eq(td(0, 0, 60*1000000), b)
212 eq(a*10, td(70))
213 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000214 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000215 eq(b*10, td(0, 600))
216 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000217 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000218 eq(c*10, td(0, 0, 10000))
219 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000220 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000221 eq(a*-1, -a)
222 eq(b*-2, -b-b)
223 eq(c*-2, -c+-c)
224 eq(b*(60*24), (b*60)*24)
225 eq(b*(60*24), (60*b)*24)
226 eq(c*1000, td(0, 1))
227 eq(1000*c, td(0, 1))
228 eq(a//7, td(1))
229 eq(b//10, td(0, 6))
230 eq(c//1000, td(0, 0, 1))
231 eq(a//10, td(0, 7*24*360))
232 eq(a//3600000, td(0, 0, 7*24*1000))
233
234 def test_disallowed_computations(self):
235 a = timedelta(42)
236
237 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000238 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000239 self.assertRaises(TypeError, lambda: a+i)
240 self.assertRaises(TypeError, lambda: a-i)
241 self.assertRaises(TypeError, lambda: i+a)
242 self.assertRaises(TypeError, lambda: i-a)
243
244 # Mul/div by float isn't supported.
245 x = 2.3
246 self.assertRaises(TypeError, lambda: a*x)
247 self.assertRaises(TypeError, lambda: x*a)
248 self.assertRaises(TypeError, lambda: a/x)
249 self.assertRaises(TypeError, lambda: x/a)
250 self.assertRaises(TypeError, lambda: a // x)
251 self.assertRaises(TypeError, lambda: x // a)
252
253 # Divison of int by timedelta doesn't make sense.
254 # Division by zero doesn't make sense.
Guido van Rossume2a383d2007-01-15 16:59:06 +0000255 for zero in 0, 0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000256 self.assertRaises(TypeError, lambda: zero // a)
257 self.assertRaises(ZeroDivisionError, lambda: a // zero)
258
259 def test_basic_attributes(self):
260 days, seconds, us = 1, 7, 31
261 td = timedelta(days, seconds, us)
262 self.assertEqual(td.days, days)
263 self.assertEqual(td.seconds, seconds)
264 self.assertEqual(td.microseconds, us)
265
266 def test_carries(self):
267 t1 = timedelta(days=100,
268 weeks=-7,
269 hours=-24*(100-49),
270 minutes=-3,
271 seconds=12,
272 microseconds=(3*60 - 12) * 1e6 + 1)
273 t2 = timedelta(microseconds=1)
274 self.assertEqual(t1, t2)
275
276 def test_hash_equality(self):
277 t1 = timedelta(days=100,
278 weeks=-7,
279 hours=-24*(100-49),
280 minutes=-3,
281 seconds=12,
282 microseconds=(3*60 - 12) * 1000000)
283 t2 = timedelta()
284 self.assertEqual(hash(t1), hash(t2))
285
286 t1 += timedelta(weeks=7)
287 t2 += timedelta(days=7*7)
288 self.assertEqual(t1, t2)
289 self.assertEqual(hash(t1), hash(t2))
290
291 d = {t1: 1}
292 d[t2] = 2
293 self.assertEqual(len(d), 1)
294 self.assertEqual(d[t1], 2)
295
296 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000297 args = 12, 34, 56
298 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000299 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000300 green = pickler.dumps(orig, proto)
301 derived = unpickler.loads(green)
302 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000303
304 def test_compare(self):
305 t1 = timedelta(2, 3, 4)
306 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000307 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000308 self.failUnless(t1 <= t2)
309 self.failUnless(t1 >= t2)
310 self.failUnless(not t1 != t2)
311 self.failUnless(not t1 < t2)
312 self.failUnless(not t1 > t2)
313 self.assertEqual(cmp(t1, t2), 0)
314 self.assertEqual(cmp(t2, t1), 0)
315
316 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
317 t2 = timedelta(*args) # this is larger than t1
318 self.failUnless(t1 < t2)
319 self.failUnless(t2 > t1)
320 self.failUnless(t1 <= t2)
321 self.failUnless(t2 >= t1)
322 self.failUnless(t1 != t2)
323 self.failUnless(t2 != t1)
324 self.failUnless(not t1 == t2)
325 self.failUnless(not t2 == t1)
326 self.failUnless(not t1 > t2)
327 self.failUnless(not t2 < t1)
328 self.failUnless(not t1 >= t2)
329 self.failUnless(not t2 <= t1)
330 self.assertEqual(cmp(t1, t2), -1)
331 self.assertEqual(cmp(t2, t1), 1)
332
Tim Peters68124bb2003-02-08 03:46:31 +0000333 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000334 self.assertEqual(t1 == badarg, False)
335 self.assertEqual(t1 != badarg, True)
336 self.assertEqual(badarg == t1, False)
337 self.assertEqual(badarg != t1, True)
338
Tim Peters2a799bf2002-12-16 20:18:38 +0000339 self.assertRaises(TypeError, lambda: t1 <= badarg)
340 self.assertRaises(TypeError, lambda: t1 < badarg)
341 self.assertRaises(TypeError, lambda: t1 > badarg)
342 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000343 self.assertRaises(TypeError, lambda: badarg <= t1)
344 self.assertRaises(TypeError, lambda: badarg < t1)
345 self.assertRaises(TypeError, lambda: badarg > t1)
346 self.assertRaises(TypeError, lambda: badarg >= t1)
347
348 def test_str(self):
349 td = timedelta
350 eq = self.assertEqual
351
352 eq(str(td(1)), "1 day, 0:00:00")
353 eq(str(td(-1)), "-1 day, 0:00:00")
354 eq(str(td(2)), "2 days, 0:00:00")
355 eq(str(td(-2)), "-2 days, 0:00:00")
356
357 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
358 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
359 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
360 "-210 days, 23:12:34")
361
362 eq(str(td(milliseconds=1)), "0:00:00.001000")
363 eq(str(td(microseconds=3)), "0:00:00.000003")
364
365 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
366 microseconds=999999)),
367 "999999999 days, 23:59:59.999999")
368
369 def test_roundtrip(self):
370 for td in (timedelta(days=999999999, hours=23, minutes=59,
371 seconds=59, microseconds=999999),
372 timedelta(days=-999999999),
373 timedelta(days=1, seconds=2, microseconds=3)):
374
375 # Verify td -> string -> td identity.
376 s = repr(td)
377 self.failUnless(s.startswith('datetime.'))
378 s = s[9:]
379 td2 = eval(s)
380 self.assertEqual(td, td2)
381
382 # Verify identity via reconstructing from pieces.
383 td2 = timedelta(td.days, td.seconds, td.microseconds)
384 self.assertEqual(td, td2)
385
386 def test_resolution_info(self):
387 self.assert_(isinstance(timedelta.min, timedelta))
388 self.assert_(isinstance(timedelta.max, timedelta))
389 self.assert_(isinstance(timedelta.resolution, timedelta))
390 self.assert_(timedelta.max > timedelta.min)
391 self.assertEqual(timedelta.min, timedelta(-999999999))
392 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
393 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
394
395 def test_overflow(self):
396 tiny = timedelta.resolution
397
398 td = timedelta.min + tiny
399 td -= tiny # no problem
400 self.assertRaises(OverflowError, td.__sub__, tiny)
401 self.assertRaises(OverflowError, td.__add__, -tiny)
402
403 td = timedelta.max - tiny
404 td += tiny # no problem
405 self.assertRaises(OverflowError, td.__add__, tiny)
406 self.assertRaises(OverflowError, td.__sub__, -tiny)
407
408 self.assertRaises(OverflowError, lambda: -timedelta.max)
409
410 def test_microsecond_rounding(self):
411 td = timedelta
412 eq = self.assertEqual
413
414 # Single-field rounding.
415 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
416 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
417 eq(td(milliseconds=0.6/1000), td(microseconds=1))
418 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
419
420 # Rounding due to contributions from more than one field.
421 us_per_hour = 3600e6
422 us_per_day = us_per_hour * 24
423 eq(td(days=.4/us_per_day), td(0))
424 eq(td(hours=.2/us_per_hour), td(0))
425 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
426
427 eq(td(days=-.4/us_per_day), td(0))
428 eq(td(hours=-.2/us_per_hour), td(0))
429 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
430
431 def test_massive_normalization(self):
432 td = timedelta(microseconds=-1)
433 self.assertEqual((td.days, td.seconds, td.microseconds),
434 (-1, 24*3600-1, 999999))
435
436 def test_bool(self):
437 self.failUnless(timedelta(1))
438 self.failUnless(timedelta(0, 1))
439 self.failUnless(timedelta(0, 0, 1))
440 self.failUnless(timedelta(microseconds=1))
441 self.failUnless(not timedelta(0))
442
Tim Petersb0c854d2003-05-17 15:57:00 +0000443 def test_subclass_timedelta(self):
444
445 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000446 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000447 def from_td(td):
448 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000449
450 def as_hours(self):
451 sum = (self.days * 24 +
452 self.seconds / 3600.0 +
453 self.microseconds / 3600e6)
454 return round(sum)
455
456 t1 = T(days=1)
457 self.assert_(type(t1) is T)
458 self.assertEqual(t1.as_hours(), 24)
459
460 t2 = T(days=-1, seconds=-3600)
461 self.assert_(type(t2) is T)
462 self.assertEqual(t2.as_hours(), -25)
463
464 t3 = t1 + t2
465 self.assert_(type(t3) is timedelta)
466 t4 = T.from_td(t3)
467 self.assert_(type(t4) is T)
468 self.assertEqual(t3.days, t4.days)
469 self.assertEqual(t3.seconds, t4.seconds)
470 self.assertEqual(t3.microseconds, t4.microseconds)
471 self.assertEqual(str(t3), str(t4))
472 self.assertEqual(t4.as_hours(), -1)
473
Tim Peters2a799bf2002-12-16 20:18:38 +0000474#############################################################################
475# date tests
476
477class TestDateOnly(unittest.TestCase):
478 # Tests here won't pass if also run on datetime objects, so don't
479 # subclass this to test datetimes too.
480
481 def test_delta_non_days_ignored(self):
482 dt = date(2000, 1, 2)
483 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
484 microseconds=5)
485 days = timedelta(delta.days)
486 self.assertEqual(days, timedelta(1))
487
488 dt2 = dt + delta
489 self.assertEqual(dt2, dt + days)
490
491 dt2 = delta + dt
492 self.assertEqual(dt2, dt + days)
493
494 dt2 = dt - delta
495 self.assertEqual(dt2, dt - days)
496
497 delta = -delta
498 days = timedelta(delta.days)
499 self.assertEqual(days, timedelta(-2))
500
501 dt2 = dt + delta
502 self.assertEqual(dt2, dt + days)
503
504 dt2 = delta + dt
505 self.assertEqual(dt2, dt + days)
506
507 dt2 = dt - delta
508 self.assertEqual(dt2, dt - days)
509
Tim Peters604c0132004-06-07 23:04:33 +0000510class SubclassDate(date):
511 sub_var = 1
512
Guido van Rossumd8faa362007-04-27 19:54:29 +0000513class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000514 # Tests here should pass for both dates and datetimes, except for a
515 # few tests that TestDateTime overrides.
516
517 theclass = date
518
519 def test_basic_attributes(self):
520 dt = self.theclass(2002, 3, 1)
521 self.assertEqual(dt.year, 2002)
522 self.assertEqual(dt.month, 3)
523 self.assertEqual(dt.day, 1)
524
525 def test_roundtrip(self):
526 for dt in (self.theclass(1, 2, 3),
527 self.theclass.today()):
528 # Verify dt -> string -> date identity.
529 s = repr(dt)
530 self.failUnless(s.startswith('datetime.'))
531 s = s[9:]
532 dt2 = eval(s)
533 self.assertEqual(dt, dt2)
534
535 # Verify identity via reconstructing from pieces.
536 dt2 = self.theclass(dt.year, dt.month, dt.day)
537 self.assertEqual(dt, dt2)
538
539 def test_ordinal_conversions(self):
540 # Check some fixed values.
541 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
542 (1, 12, 31, 365),
543 (2, 1, 1, 366),
544 # first example from "Calendrical Calculations"
545 (1945, 11, 12, 710347)]:
546 d = self.theclass(y, m, d)
547 self.assertEqual(n, d.toordinal())
548 fromord = self.theclass.fromordinal(n)
549 self.assertEqual(d, fromord)
550 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000551 # if we're checking something fancier than a date, verify
552 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000553 self.assertEqual(fromord.hour, 0)
554 self.assertEqual(fromord.minute, 0)
555 self.assertEqual(fromord.second, 0)
556 self.assertEqual(fromord.microsecond, 0)
557
Tim Peters0bf60bd2003-01-08 20:40:01 +0000558 # Check first and last days of year spottily across the whole
559 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000560 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000561 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
562 d = self.theclass(year, 1, 1)
563 n = d.toordinal()
564 d2 = self.theclass.fromordinal(n)
565 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000566 # Verify that moving back a day gets to the end of year-1.
567 if year > 1:
568 d = self.theclass.fromordinal(n-1)
569 d2 = self.theclass(year-1, 12, 31)
570 self.assertEqual(d, d2)
571 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000572
573 # Test every day in a leap-year and a non-leap year.
574 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
575 for year, isleap in (2000, True), (2002, False):
576 n = self.theclass(year, 1, 1).toordinal()
577 for month, maxday in zip(range(1, 13), dim):
578 if month == 2 and isleap:
579 maxday += 1
580 for day in range(1, maxday+1):
581 d = self.theclass(year, month, day)
582 self.assertEqual(d.toordinal(), n)
583 self.assertEqual(d, self.theclass.fromordinal(n))
584 n += 1
585
586 def test_extreme_ordinals(self):
587 a = self.theclass.min
588 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
589 aord = a.toordinal()
590 b = a.fromordinal(aord)
591 self.assertEqual(a, b)
592
593 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
594
595 b = a + timedelta(days=1)
596 self.assertEqual(b.toordinal(), aord + 1)
597 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
598
599 a = self.theclass.max
600 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
601 aord = a.toordinal()
602 b = a.fromordinal(aord)
603 self.assertEqual(a, b)
604
605 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
606
607 b = a - timedelta(days=1)
608 self.assertEqual(b.toordinal(), aord - 1)
609 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
610
611 def test_bad_constructor_arguments(self):
612 # bad years
613 self.theclass(MINYEAR, 1, 1) # no exception
614 self.theclass(MAXYEAR, 1, 1) # no exception
615 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
616 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
617 # bad months
618 self.theclass(2000, 1, 1) # no exception
619 self.theclass(2000, 12, 1) # no exception
620 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
621 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
622 # bad days
623 self.theclass(2000, 2, 29) # no exception
624 self.theclass(2004, 2, 29) # no exception
625 self.theclass(2400, 2, 29) # no exception
626 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
627 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
628 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
629 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
630 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
631 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
632
633 def test_hash_equality(self):
634 d = self.theclass(2000, 12, 31)
635 # same thing
636 e = self.theclass(2000, 12, 31)
637 self.assertEqual(d, e)
638 self.assertEqual(hash(d), hash(e))
639
640 dic = {d: 1}
641 dic[e] = 2
642 self.assertEqual(len(dic), 1)
643 self.assertEqual(dic[d], 2)
644 self.assertEqual(dic[e], 2)
645
646 d = self.theclass(2001, 1, 1)
647 # same thing
648 e = self.theclass(2001, 1, 1)
649 self.assertEqual(d, e)
650 self.assertEqual(hash(d), hash(e))
651
652 dic = {d: 1}
653 dic[e] = 2
654 self.assertEqual(len(dic), 1)
655 self.assertEqual(dic[d], 2)
656 self.assertEqual(dic[e], 2)
657
658 def test_computations(self):
659 a = self.theclass(2002, 1, 31)
660 b = self.theclass(1956, 1, 31)
661
662 diff = a-b
663 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
664 self.assertEqual(diff.seconds, 0)
665 self.assertEqual(diff.microseconds, 0)
666
667 day = timedelta(1)
668 week = timedelta(7)
669 a = self.theclass(2002, 3, 2)
670 self.assertEqual(a + day, self.theclass(2002, 3, 3))
671 self.assertEqual(day + a, self.theclass(2002, 3, 3))
672 self.assertEqual(a - day, self.theclass(2002, 3, 1))
673 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
674 self.assertEqual(a + week, self.theclass(2002, 3, 9))
675 self.assertEqual(a - week, self.theclass(2002, 2, 23))
676 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
677 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
678 self.assertEqual((a + week) - a, week)
679 self.assertEqual((a + day) - a, day)
680 self.assertEqual((a - week) - a, -week)
681 self.assertEqual((a - day) - a, -day)
682 self.assertEqual(a - (a + week), -week)
683 self.assertEqual(a - (a + day), -day)
684 self.assertEqual(a - (a - week), week)
685 self.assertEqual(a - (a - day), day)
686
687 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000688 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000689 self.assertRaises(TypeError, lambda: a+i)
690 self.assertRaises(TypeError, lambda: a-i)
691 self.assertRaises(TypeError, lambda: i+a)
692 self.assertRaises(TypeError, lambda: i-a)
693
694 # delta - date is senseless.
695 self.assertRaises(TypeError, lambda: day - a)
696 # mixing date and (delta or date) via * or // is senseless
697 self.assertRaises(TypeError, lambda: day * a)
698 self.assertRaises(TypeError, lambda: a * day)
699 self.assertRaises(TypeError, lambda: day // a)
700 self.assertRaises(TypeError, lambda: a // day)
701 self.assertRaises(TypeError, lambda: a * a)
702 self.assertRaises(TypeError, lambda: a // a)
703 # date + date is senseless
704 self.assertRaises(TypeError, lambda: a + a)
705
706 def test_overflow(self):
707 tiny = self.theclass.resolution
708
709 dt = self.theclass.min + tiny
710 dt -= tiny # no problem
711 self.assertRaises(OverflowError, dt.__sub__, tiny)
712 self.assertRaises(OverflowError, dt.__add__, -tiny)
713
714 dt = self.theclass.max - tiny
715 dt += tiny # no problem
716 self.assertRaises(OverflowError, dt.__add__, tiny)
717 self.assertRaises(OverflowError, dt.__sub__, -tiny)
718
719 def test_fromtimestamp(self):
720 import time
721
722 # Try an arbitrary fixed value.
723 year, month, day = 1999, 9, 19
724 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
725 d = self.theclass.fromtimestamp(ts)
726 self.assertEqual(d.year, year)
727 self.assertEqual(d.month, month)
728 self.assertEqual(d.day, day)
729
Tim Peters1b6f7a92004-06-20 02:50:16 +0000730 def test_insane_fromtimestamp(self):
731 # It's possible that some platform maps time_t to double,
732 # and that this test will fail there. This test should
733 # exempt such platforms (provided they return reasonable
734 # results!).
735 for insane in -1e200, 1e200:
736 self.assertRaises(ValueError, self.theclass.fromtimestamp,
737 insane)
738
Tim Peters2a799bf2002-12-16 20:18:38 +0000739 def test_today(self):
740 import time
741
742 # We claim that today() is like fromtimestamp(time.time()), so
743 # prove it.
744 for dummy in range(3):
745 today = self.theclass.today()
746 ts = time.time()
747 todayagain = self.theclass.fromtimestamp(ts)
748 if today == todayagain:
749 break
750 # There are several legit reasons that could fail:
751 # 1. It recently became midnight, between the today() and the
752 # time() calls.
753 # 2. The platform time() has such fine resolution that we'll
754 # never get the same value twice.
755 # 3. The platform time() has poor resolution, and we just
756 # happened to call today() right before a resolution quantum
757 # boundary.
758 # 4. The system clock got fiddled between calls.
759 # In any case, wait a little while and try again.
760 time.sleep(0.1)
761
762 # It worked or it didn't. If it didn't, assume it's reason #2, and
763 # let the test pass if they're within half a second of each other.
764 self.failUnless(today == todayagain or
765 abs(todayagain - today) < timedelta(seconds=0.5))
766
767 def test_weekday(self):
768 for i in range(7):
769 # March 4, 2002 is a Monday
770 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
771 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
772 # January 2, 1956 is a Monday
773 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
774 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
775
776 def test_isocalendar(self):
777 # Check examples from
778 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
779 for i in range(7):
780 d = self.theclass(2003, 12, 22+i)
781 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
782 d = self.theclass(2003, 12, 29) + timedelta(i)
783 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
784 d = self.theclass(2004, 1, 5+i)
785 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
786 d = self.theclass(2009, 12, 21+i)
787 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
788 d = self.theclass(2009, 12, 28) + timedelta(i)
789 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
790 d = self.theclass(2010, 1, 4+i)
791 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
792
793 def test_iso_long_years(self):
794 # Calculate long ISO years and compare to table from
795 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
796 ISO_LONG_YEARS_TABLE = """
797 4 32 60 88
798 9 37 65 93
799 15 43 71 99
800 20 48 76
801 26 54 82
802
803 105 133 161 189
804 111 139 167 195
805 116 144 172
806 122 150 178
807 128 156 184
808
809 201 229 257 285
810 207 235 263 291
811 212 240 268 296
812 218 246 274
813 224 252 280
814
815 303 331 359 387
816 308 336 364 392
817 314 342 370 398
818 320 348 376
819 325 353 381
820 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000821 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000822 L = []
823 for i in range(400):
824 d = self.theclass(2000+i, 12, 31)
825 d1 = self.theclass(1600+i, 12, 31)
826 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
827 if d.isocalendar()[1] == 53:
828 L.append(i)
829 self.assertEqual(L, iso_long_years)
830
831 def test_isoformat(self):
832 t = self.theclass(2, 3, 2)
833 self.assertEqual(t.isoformat(), "0002-03-02")
834
835 def test_ctime(self):
836 t = self.theclass(2002, 3, 2)
837 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
838
839 def test_strftime(self):
840 t = self.theclass(2005, 3, 2)
841 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000842 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000843 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000844
845 self.assertRaises(TypeError, t.strftime) # needs an arg
846 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
847 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
848
849 # A naive object replaces %z and %Z w/ empty strings.
850 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
851
Eric Smith1ba31142007-09-11 18:06:02 +0000852 def test_format(self):
853 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000854 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000855
856 # check that a derived class's __str__() gets called
857 class A(self.theclass):
858 def __str__(self):
859 return 'A'
860 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000861 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000862
863 # check that a derived class's strftime gets called
864 class B(self.theclass):
865 def strftime(self, format_spec):
866 return 'B'
867 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000868 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000869
870 for fmt in ["m:%m d:%d y:%y",
871 "m:%m d:%d y:%y H:%H M:%M S:%S",
872 "%z %Z",
873 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +0000874 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
875 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
876 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +0000877
Tim Peters2a799bf2002-12-16 20:18:38 +0000878 def test_resolution_info(self):
879 self.assert_(isinstance(self.theclass.min, self.theclass))
880 self.assert_(isinstance(self.theclass.max, self.theclass))
881 self.assert_(isinstance(self.theclass.resolution, timedelta))
882 self.assert_(self.theclass.max > self.theclass.min)
883
884 def test_extreme_timedelta(self):
885 big = self.theclass.max - self.theclass.min
886 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
887 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
888 # n == 315537897599999999 ~= 2**58.13
889 justasbig = timedelta(0, 0, n)
890 self.assertEqual(big, justasbig)
891 self.assertEqual(self.theclass.min + big, self.theclass.max)
892 self.assertEqual(self.theclass.max - big, self.theclass.min)
893
894 def test_timetuple(self):
895 for i in range(7):
896 # January 2, 1956 is a Monday (0)
897 d = self.theclass(1956, 1, 2+i)
898 t = d.timetuple()
899 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
900 # February 1, 1956 is a Wednesday (2)
901 d = self.theclass(1956, 2, 1+i)
902 t = d.timetuple()
903 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
904 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
905 # of the year.
906 d = self.theclass(1956, 3, 1+i)
907 t = d.timetuple()
908 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
909 self.assertEqual(t.tm_year, 1956)
910 self.assertEqual(t.tm_mon, 3)
911 self.assertEqual(t.tm_mday, 1+i)
912 self.assertEqual(t.tm_hour, 0)
913 self.assertEqual(t.tm_min, 0)
914 self.assertEqual(t.tm_sec, 0)
915 self.assertEqual(t.tm_wday, (3+i)%7)
916 self.assertEqual(t.tm_yday, 61+i)
917 self.assertEqual(t.tm_isdst, -1)
918
919 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000920 args = 6, 7, 23
921 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000922 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000923 green = pickler.dumps(orig, proto)
924 derived = unpickler.loads(green)
925 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000926
927 def test_compare(self):
928 t1 = self.theclass(2, 3, 4)
929 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000930 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000931 self.failUnless(t1 <= t2)
932 self.failUnless(t1 >= t2)
933 self.failUnless(not t1 != t2)
934 self.failUnless(not t1 < t2)
935 self.failUnless(not t1 > t2)
936 self.assertEqual(cmp(t1, t2), 0)
937 self.assertEqual(cmp(t2, t1), 0)
938
939 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
940 t2 = self.theclass(*args) # this is larger than t1
941 self.failUnless(t1 < t2)
942 self.failUnless(t2 > t1)
943 self.failUnless(t1 <= t2)
944 self.failUnless(t2 >= t1)
945 self.failUnless(t1 != t2)
946 self.failUnless(t2 != t1)
947 self.failUnless(not t1 == t2)
948 self.failUnless(not t2 == t1)
949 self.failUnless(not t1 > t2)
950 self.failUnless(not t2 < t1)
951 self.failUnless(not t1 >= t2)
952 self.failUnless(not t2 <= t1)
953 self.assertEqual(cmp(t1, t2), -1)
954 self.assertEqual(cmp(t2, t1), 1)
955
Tim Peters68124bb2003-02-08 03:46:31 +0000956 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000957 self.assertEqual(t1 == badarg, False)
958 self.assertEqual(t1 != badarg, True)
959 self.assertEqual(badarg == t1, False)
960 self.assertEqual(badarg != t1, True)
961
Tim Peters2a799bf2002-12-16 20:18:38 +0000962 self.assertRaises(TypeError, lambda: t1 < badarg)
963 self.assertRaises(TypeError, lambda: t1 > badarg)
964 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000965 self.assertRaises(TypeError, lambda: badarg <= t1)
966 self.assertRaises(TypeError, lambda: badarg < t1)
967 self.assertRaises(TypeError, lambda: badarg > t1)
968 self.assertRaises(TypeError, lambda: badarg >= t1)
969
Tim Peters8d81a012003-01-24 22:36:34 +0000970 def test_mixed_compare(self):
971 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000972
973 # Our class can be compared for equality to other classes
974 self.assertEqual(our == 1, False)
975 self.assertEqual(1 == our, False)
976 self.assertEqual(our != 1, True)
977 self.assertEqual(1 != our, True)
978
979 # But the ordering is undefined
980 self.assertRaises(TypeError, lambda: our < 1)
981 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000982 self.assertRaises(TypeError, cmp, our, 1)
983 self.assertRaises(TypeError, cmp, 1, our)
984
Guido van Rossum19960592006-08-24 17:29:38 +0000985 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +0000986
Guido van Rossum19960592006-08-24 17:29:38 +0000987 class SomeClass:
988 pass
989
990 their = SomeClass()
991 self.assertEqual(our == their, False)
992 self.assertEqual(their == our, False)
993 self.assertEqual(our != their, True)
994 self.assertEqual(their != our, True)
995 self.assertRaises(TypeError, lambda: our < their)
996 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000997 self.assertRaises(TypeError, cmp, our, their)
Guido van Rossum19960592006-08-24 17:29:38 +0000998 self.assertRaises(TypeError, cmp, their, our)
Tim Peters8d81a012003-01-24 22:36:34 +0000999
Guido van Rossum19960592006-08-24 17:29:38 +00001000 # However, if the other class explicitly defines ordering
1001 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001002
Guido van Rossum19960592006-08-24 17:29:38 +00001003 class LargerThanAnything:
1004 def __lt__(self, other):
1005 return False
1006 def __le__(self, other):
1007 return isinstance(other, LargerThanAnything)
1008 def __eq__(self, other):
1009 return isinstance(other, LargerThanAnything)
1010 def __ne__(self, other):
1011 return not isinstance(other, LargerThanAnything)
1012 def __gt__(self, other):
1013 return not isinstance(other, LargerThanAnything)
1014 def __ge__(self, other):
1015 return True
1016
1017 their = LargerThanAnything()
1018 self.assertEqual(our == their, False)
1019 self.assertEqual(their == our, False)
1020 self.assertEqual(our != their, True)
1021 self.assertEqual(their != our, True)
1022 self.assertEqual(our < their, True)
1023 self.assertEqual(their < our, False)
1024 self.assertEqual(cmp(our, their), -1)
1025 self.assertEqual(cmp(their, our), 1)
Tim Peters8d81a012003-01-24 22:36:34 +00001026
Tim Peters2a799bf2002-12-16 20:18:38 +00001027 def test_bool(self):
1028 # All dates are considered true.
1029 self.failUnless(self.theclass.min)
1030 self.failUnless(self.theclass.max)
1031
Guido van Rossum04110fb2007-08-24 16:32:05 +00001032 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001033 # For nasty technical reasons, we can't handle years before 1900.
1034 cls = self.theclass
1035 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1036 for y in 1, 49, 51, 99, 100, 1000, 1899:
1037 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001038
1039 def test_replace(self):
1040 cls = self.theclass
1041 args = [1, 2, 3]
1042 base = cls(*args)
1043 self.assertEqual(base, base.replace())
1044
1045 i = 0
1046 for name, newval in (("year", 2),
1047 ("month", 3),
1048 ("day", 4)):
1049 newargs = args[:]
1050 newargs[i] = newval
1051 expected = cls(*newargs)
1052 got = base.replace(**{name: newval})
1053 self.assertEqual(expected, got)
1054 i += 1
1055
1056 # Out of bounds.
1057 base = cls(2000, 2, 29)
1058 self.assertRaises(ValueError, base.replace, year=2001)
1059
Tim Petersa98924a2003-05-17 05:55:19 +00001060 def test_subclass_date(self):
1061
1062 class C(self.theclass):
1063 theAnswer = 42
1064
1065 def __new__(cls, *args, **kws):
1066 temp = kws.copy()
1067 extra = temp.pop('extra')
1068 result = self.theclass.__new__(cls, *args, **temp)
1069 result.extra = extra
1070 return result
1071
1072 def newmeth(self, start):
1073 return start + self.year + self.month
1074
1075 args = 2003, 4, 14
1076
1077 dt1 = self.theclass(*args)
1078 dt2 = C(*args, **{'extra': 7})
1079
1080 self.assertEqual(dt2.__class__, C)
1081 self.assertEqual(dt2.theAnswer, 42)
1082 self.assertEqual(dt2.extra, 7)
1083 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1084 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1085
Tim Peters604c0132004-06-07 23:04:33 +00001086 def test_pickling_subclass_date(self):
1087
1088 args = 6, 7, 23
1089 orig = SubclassDate(*args)
1090 for pickler, unpickler, proto in pickle_choices:
1091 green = pickler.dumps(orig, proto)
1092 derived = unpickler.loads(green)
1093 self.assertEqual(orig, derived)
1094
Tim Peters3f606292004-03-21 23:38:41 +00001095 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001096 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001097 # This is a low-overhead backdoor. A user can (by intent or
1098 # mistake) pass a string directly, which (if it's the right length)
1099 # will get treated like a pickle, and bypass the normal sanity
1100 # checks in the constructor. This can create insane objects.
1101 # The constructor doesn't want to burn the time to validate all
1102 # fields, but does check the month field. This stops, e.g.,
1103 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001104 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001105 if not issubclass(self.theclass, datetime):
1106 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001107 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001108 self.assertRaises(TypeError, self.theclass,
1109 base[:2] + month_byte + base[3:])
1110 for ord_byte in range(1, 13):
1111 # This shouldn't blow up because of the month byte alone. If
1112 # the implementation changes to do more-careful checking, it may
1113 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001114 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001115
Tim Peters2a799bf2002-12-16 20:18:38 +00001116#############################################################################
1117# datetime tests
1118
Tim Peters604c0132004-06-07 23:04:33 +00001119class SubclassDatetime(datetime):
1120 sub_var = 1
1121
Tim Peters2a799bf2002-12-16 20:18:38 +00001122class TestDateTime(TestDate):
1123
1124 theclass = datetime
1125
1126 def test_basic_attributes(self):
1127 dt = self.theclass(2002, 3, 1, 12, 0)
1128 self.assertEqual(dt.year, 2002)
1129 self.assertEqual(dt.month, 3)
1130 self.assertEqual(dt.day, 1)
1131 self.assertEqual(dt.hour, 12)
1132 self.assertEqual(dt.minute, 0)
1133 self.assertEqual(dt.second, 0)
1134 self.assertEqual(dt.microsecond, 0)
1135
1136 def test_basic_attributes_nonzero(self):
1137 # Make sure all attributes are non-zero so bugs in
1138 # bit-shifting access show up.
1139 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1140 self.assertEqual(dt.year, 2002)
1141 self.assertEqual(dt.month, 3)
1142 self.assertEqual(dt.day, 1)
1143 self.assertEqual(dt.hour, 12)
1144 self.assertEqual(dt.minute, 59)
1145 self.assertEqual(dt.second, 59)
1146 self.assertEqual(dt.microsecond, 8000)
1147
1148 def test_roundtrip(self):
1149 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1150 self.theclass.now()):
1151 # Verify dt -> string -> datetime identity.
1152 s = repr(dt)
1153 self.failUnless(s.startswith('datetime.'))
1154 s = s[9:]
1155 dt2 = eval(s)
1156 self.assertEqual(dt, dt2)
1157
1158 # Verify identity via reconstructing from pieces.
1159 dt2 = self.theclass(dt.year, dt.month, dt.day,
1160 dt.hour, dt.minute, dt.second,
1161 dt.microsecond)
1162 self.assertEqual(dt, dt2)
1163
1164 def test_isoformat(self):
1165 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1166 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1167 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1168 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1169 # str is ISO format with the separator forced to a blank.
1170 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1171
1172 t = self.theclass(2, 3, 2)
1173 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1174 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1175 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1176 # str is ISO format with the separator forced to a blank.
1177 self.assertEqual(str(t), "0002-03-02 00:00:00")
1178
Eric Smith1ba31142007-09-11 18:06:02 +00001179 def test_format(self):
1180 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001181 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001182
1183 # check that a derived class's __str__() gets called
1184 class A(self.theclass):
1185 def __str__(self):
1186 return 'A'
1187 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001188 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001189
1190 # check that a derived class's strftime gets called
1191 class B(self.theclass):
1192 def strftime(self, format_spec):
1193 return 'B'
1194 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001195 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001196
1197 for fmt in ["m:%m d:%d y:%y",
1198 "m:%m d:%d y:%y H:%H M:%M S:%S",
1199 "%z %Z",
1200 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001201 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1202 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1203 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001204
Tim Peters2a799bf2002-12-16 20:18:38 +00001205 def test_more_ctime(self):
1206 # Test fields that TestDate doesn't touch.
1207 import time
1208
1209 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1210 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1211 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1212 # out. The difference is that t.ctime() produces " 2" for the day,
1213 # but platform ctime() produces "02" for the day. According to
1214 # C99, t.ctime() is correct here.
1215 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1216
1217 # So test a case where that difference doesn't matter.
1218 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1219 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1220
1221 def test_tz_independent_comparing(self):
1222 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1223 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1224 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1225 self.assertEqual(dt1, dt3)
1226 self.assert_(dt2 > dt3)
1227
1228 # Make sure comparison doesn't forget microseconds, and isn't done
1229 # via comparing a float timestamp (an IEEE double doesn't have enough
1230 # precision to span microsecond resolution across years 1 thru 9999,
1231 # so comparing via timestamp necessarily calls some distinct values
1232 # equal).
1233 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1234 us = timedelta(microseconds=1)
1235 dt2 = dt1 + us
1236 self.assertEqual(dt2 - dt1, us)
1237 self.assert_(dt1 < dt2)
1238
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001239 def test_strftime_with_bad_tzname_replace(self):
1240 # verify ok if tzinfo.tzname().replace() returns a non-string
1241 class MyTzInfo(FixedOffset):
1242 def tzname(self, dt):
1243 class MyStr(str):
1244 def replace(self, *args):
1245 return None
1246 return MyStr('name')
1247 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1248 self.assertRaises(TypeError, t.strftime, '%Z')
1249
Tim Peters2a799bf2002-12-16 20:18:38 +00001250 def test_bad_constructor_arguments(self):
1251 # bad years
1252 self.theclass(MINYEAR, 1, 1) # no exception
1253 self.theclass(MAXYEAR, 1, 1) # no exception
1254 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1255 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1256 # bad months
1257 self.theclass(2000, 1, 1) # no exception
1258 self.theclass(2000, 12, 1) # no exception
1259 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1260 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1261 # bad days
1262 self.theclass(2000, 2, 29) # no exception
1263 self.theclass(2004, 2, 29) # no exception
1264 self.theclass(2400, 2, 29) # no exception
1265 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1266 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1267 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1268 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1269 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1270 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1271 # bad hours
1272 self.theclass(2000, 1, 31, 0) # no exception
1273 self.theclass(2000, 1, 31, 23) # no exception
1274 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1275 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1276 # bad minutes
1277 self.theclass(2000, 1, 31, 23, 0) # no exception
1278 self.theclass(2000, 1, 31, 23, 59) # no exception
1279 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1280 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1281 # bad seconds
1282 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1283 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1284 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1285 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1286 # bad microseconds
1287 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1288 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1289 self.assertRaises(ValueError, self.theclass,
1290 2000, 1, 31, 23, 59, 59, -1)
1291 self.assertRaises(ValueError, self.theclass,
1292 2000, 1, 31, 23, 59, 59,
1293 1000000)
1294
1295 def test_hash_equality(self):
1296 d = self.theclass(2000, 12, 31, 23, 30, 17)
1297 e = self.theclass(2000, 12, 31, 23, 30, 17)
1298 self.assertEqual(d, e)
1299 self.assertEqual(hash(d), hash(e))
1300
1301 dic = {d: 1}
1302 dic[e] = 2
1303 self.assertEqual(len(dic), 1)
1304 self.assertEqual(dic[d], 2)
1305 self.assertEqual(dic[e], 2)
1306
1307 d = self.theclass(2001, 1, 1, 0, 5, 17)
1308 e = self.theclass(2001, 1, 1, 0, 5, 17)
1309 self.assertEqual(d, e)
1310 self.assertEqual(hash(d), hash(e))
1311
1312 dic = {d: 1}
1313 dic[e] = 2
1314 self.assertEqual(len(dic), 1)
1315 self.assertEqual(dic[d], 2)
1316 self.assertEqual(dic[e], 2)
1317
1318 def test_computations(self):
1319 a = self.theclass(2002, 1, 31)
1320 b = self.theclass(1956, 1, 31)
1321 diff = a-b
1322 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1323 self.assertEqual(diff.seconds, 0)
1324 self.assertEqual(diff.microseconds, 0)
1325 a = self.theclass(2002, 3, 2, 17, 6)
1326 millisec = timedelta(0, 0, 1000)
1327 hour = timedelta(0, 3600)
1328 day = timedelta(1)
1329 week = timedelta(7)
1330 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1331 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1332 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1333 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1334 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1335 self.assertEqual(a - hour, a + -hour)
1336 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1337 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1338 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1339 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1340 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1341 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1342 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1343 self.assertEqual((a + week) - a, week)
1344 self.assertEqual((a + day) - a, day)
1345 self.assertEqual((a + hour) - a, hour)
1346 self.assertEqual((a + millisec) - a, millisec)
1347 self.assertEqual((a - week) - a, -week)
1348 self.assertEqual((a - day) - a, -day)
1349 self.assertEqual((a - hour) - a, -hour)
1350 self.assertEqual((a - millisec) - a, -millisec)
1351 self.assertEqual(a - (a + week), -week)
1352 self.assertEqual(a - (a + day), -day)
1353 self.assertEqual(a - (a + hour), -hour)
1354 self.assertEqual(a - (a + millisec), -millisec)
1355 self.assertEqual(a - (a - week), week)
1356 self.assertEqual(a - (a - day), day)
1357 self.assertEqual(a - (a - hour), hour)
1358 self.assertEqual(a - (a - millisec), millisec)
1359 self.assertEqual(a + (week + day + hour + millisec),
1360 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1361 self.assertEqual(a + (week + day + hour + millisec),
1362 (((a + week) + day) + hour) + millisec)
1363 self.assertEqual(a - (week + day + hour + millisec),
1364 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1365 self.assertEqual(a - (week + day + hour + millisec),
1366 (((a - week) - day) - hour) - millisec)
1367 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001368 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001369 self.assertRaises(TypeError, lambda: a+i)
1370 self.assertRaises(TypeError, lambda: a-i)
1371 self.assertRaises(TypeError, lambda: i+a)
1372 self.assertRaises(TypeError, lambda: i-a)
1373
1374 # delta - datetime is senseless.
1375 self.assertRaises(TypeError, lambda: day - a)
1376 # mixing datetime and (delta or datetime) via * or // is senseless
1377 self.assertRaises(TypeError, lambda: day * a)
1378 self.assertRaises(TypeError, lambda: a * day)
1379 self.assertRaises(TypeError, lambda: day // a)
1380 self.assertRaises(TypeError, lambda: a // day)
1381 self.assertRaises(TypeError, lambda: a * a)
1382 self.assertRaises(TypeError, lambda: a // a)
1383 # datetime + datetime is senseless
1384 self.assertRaises(TypeError, lambda: a + a)
1385
1386 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001387 args = 6, 7, 23, 20, 59, 1, 64**2
1388 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001389 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001390 green = pickler.dumps(orig, proto)
1391 derived = unpickler.loads(green)
1392 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001393
Guido van Rossum275666f2003-02-07 21:49:01 +00001394 def test_more_pickling(self):
1395 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1396 s = pickle.dumps(a)
1397 b = pickle.loads(s)
1398 self.assertEqual(b.year, 2003)
1399 self.assertEqual(b.month, 2)
1400 self.assertEqual(b.day, 7)
1401
Tim Peters604c0132004-06-07 23:04:33 +00001402 def test_pickling_subclass_datetime(self):
1403 args = 6, 7, 23, 20, 59, 1, 64**2
1404 orig = SubclassDatetime(*args)
1405 for pickler, unpickler, proto in pickle_choices:
1406 green = pickler.dumps(orig, proto)
1407 derived = unpickler.loads(green)
1408 self.assertEqual(orig, derived)
1409
Tim Peters2a799bf2002-12-16 20:18:38 +00001410 def test_more_compare(self):
1411 # The test_compare() inherited from TestDate covers the error cases.
1412 # We just want to test lexicographic ordering on the members datetime
1413 # has that date lacks.
1414 args = [2000, 11, 29, 20, 58, 16, 999998]
1415 t1 = self.theclass(*args)
1416 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001417 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001418 self.failUnless(t1 <= t2)
1419 self.failUnless(t1 >= t2)
1420 self.failUnless(not t1 != t2)
1421 self.failUnless(not t1 < t2)
1422 self.failUnless(not t1 > t2)
1423 self.assertEqual(cmp(t1, t2), 0)
1424 self.assertEqual(cmp(t2, t1), 0)
1425
1426 for i in range(len(args)):
1427 newargs = args[:]
1428 newargs[i] = args[i] + 1
1429 t2 = self.theclass(*newargs) # this is larger than t1
1430 self.failUnless(t1 < t2)
1431 self.failUnless(t2 > t1)
1432 self.failUnless(t1 <= t2)
1433 self.failUnless(t2 >= t1)
1434 self.failUnless(t1 != t2)
1435 self.failUnless(t2 != t1)
1436 self.failUnless(not t1 == t2)
1437 self.failUnless(not t2 == t1)
1438 self.failUnless(not t1 > t2)
1439 self.failUnless(not t2 < t1)
1440 self.failUnless(not t1 >= t2)
1441 self.failUnless(not t2 <= t1)
1442 self.assertEqual(cmp(t1, t2), -1)
1443 self.assertEqual(cmp(t2, t1), 1)
1444
1445
1446 # A helper for timestamp constructor tests.
1447 def verify_field_equality(self, expected, got):
1448 self.assertEqual(expected.tm_year, got.year)
1449 self.assertEqual(expected.tm_mon, got.month)
1450 self.assertEqual(expected.tm_mday, got.day)
1451 self.assertEqual(expected.tm_hour, got.hour)
1452 self.assertEqual(expected.tm_min, got.minute)
1453 self.assertEqual(expected.tm_sec, got.second)
1454
1455 def test_fromtimestamp(self):
1456 import time
1457
1458 ts = time.time()
1459 expected = time.localtime(ts)
1460 got = self.theclass.fromtimestamp(ts)
1461 self.verify_field_equality(expected, got)
1462
1463 def test_utcfromtimestamp(self):
1464 import time
1465
1466 ts = time.time()
1467 expected = time.gmtime(ts)
1468 got = self.theclass.utcfromtimestamp(ts)
1469 self.verify_field_equality(expected, got)
1470
Thomas Wouters477c8d52006-05-27 19:21:47 +00001471 def test_microsecond_rounding(self):
1472 # Test whether fromtimestamp "rounds up" floats that are less
1473 # than one microsecond smaller than an integer.
1474 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1475 self.theclass.fromtimestamp(1))
1476
Tim Peters1b6f7a92004-06-20 02:50:16 +00001477 def test_insane_fromtimestamp(self):
1478 # It's possible that some platform maps time_t to double,
1479 # and that this test will fail there. This test should
1480 # exempt such platforms (provided they return reasonable
1481 # results!).
1482 for insane in -1e200, 1e200:
1483 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1484 insane)
1485
1486 def test_insane_utcfromtimestamp(self):
1487 # It's possible that some platform maps time_t to double,
1488 # and that this test will fail there. This test should
1489 # exempt such platforms (provided they return reasonable
1490 # results!).
1491 for insane in -1e200, 1e200:
1492 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1493 insane)
1494
Guido van Rossumd8faa362007-04-27 19:54:29 +00001495 def test_negative_float_fromtimestamp(self):
1496 # Windows doesn't accept negative timestamps
1497 if os.name == "nt":
1498 return
1499 # The result is tz-dependent; at least test that this doesn't
1500 # fail (like it did before bug 1646728 was fixed).
1501 self.theclass.fromtimestamp(-1.05)
1502
1503 def test_negative_float_utcfromtimestamp(self):
1504 # Windows doesn't accept negative timestamps
1505 if os.name == "nt":
1506 return
1507 d = self.theclass.utcfromtimestamp(-1.05)
1508 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1509
Tim Peters2a799bf2002-12-16 20:18:38 +00001510 def test_utcnow(self):
1511 import time
1512
1513 # Call it a success if utcnow() and utcfromtimestamp() are within
1514 # a second of each other.
1515 tolerance = timedelta(seconds=1)
1516 for dummy in range(3):
1517 from_now = self.theclass.utcnow()
1518 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1519 if abs(from_timestamp - from_now) <= tolerance:
1520 break
1521 # Else try again a few times.
1522 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1523
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001524 def test_strptime(self):
1525 import time
1526
1527 string = '2004-12-01 13:02:47'
1528 format = '%Y-%m-%d %H:%M:%S'
1529 expected = self.theclass(*(time.strptime(string, format)[0:6]))
1530 got = self.theclass.strptime(string, format)
1531 self.assertEqual(expected, got)
1532
Tim Peters2a799bf2002-12-16 20:18:38 +00001533 def test_more_timetuple(self):
1534 # This tests fields beyond those tested by the TestDate.test_timetuple.
1535 t = self.theclass(2004, 12, 31, 6, 22, 33)
1536 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1537 self.assertEqual(t.timetuple(),
1538 (t.year, t.month, t.day,
1539 t.hour, t.minute, t.second,
1540 t.weekday(),
1541 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1542 -1))
1543 tt = t.timetuple()
1544 self.assertEqual(tt.tm_year, t.year)
1545 self.assertEqual(tt.tm_mon, t.month)
1546 self.assertEqual(tt.tm_mday, t.day)
1547 self.assertEqual(tt.tm_hour, t.hour)
1548 self.assertEqual(tt.tm_min, t.minute)
1549 self.assertEqual(tt.tm_sec, t.second)
1550 self.assertEqual(tt.tm_wday, t.weekday())
1551 self.assertEqual(tt.tm_yday, t.toordinal() -
1552 date(t.year, 1, 1).toordinal() + 1)
1553 self.assertEqual(tt.tm_isdst, -1)
1554
1555 def test_more_strftime(self):
1556 # This tests fields beyond those tested by the TestDate.test_strftime.
1557 t = self.theclass(2004, 12, 31, 6, 22, 33)
1558 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1559 "12 31 04 33 22 06 366")
1560
1561 def test_extract(self):
1562 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1563 self.assertEqual(dt.date(), date(2002, 3, 4))
1564 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1565
1566 def test_combine(self):
1567 d = date(2002, 3, 4)
1568 t = time(18, 45, 3, 1234)
1569 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1570 combine = self.theclass.combine
1571 dt = combine(d, t)
1572 self.assertEqual(dt, expected)
1573
1574 dt = combine(time=t, date=d)
1575 self.assertEqual(dt, expected)
1576
1577 self.assertEqual(d, dt.date())
1578 self.assertEqual(t, dt.time())
1579 self.assertEqual(dt, combine(dt.date(), dt.time()))
1580
1581 self.assertRaises(TypeError, combine) # need an arg
1582 self.assertRaises(TypeError, combine, d) # need two args
1583 self.assertRaises(TypeError, combine, t, d) # args reversed
1584 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1585 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1586
Tim Peters12bf3392002-12-24 05:41:27 +00001587 def test_replace(self):
1588 cls = self.theclass
1589 args = [1, 2, 3, 4, 5, 6, 7]
1590 base = cls(*args)
1591 self.assertEqual(base, base.replace())
1592
1593 i = 0
1594 for name, newval in (("year", 2),
1595 ("month", 3),
1596 ("day", 4),
1597 ("hour", 5),
1598 ("minute", 6),
1599 ("second", 7),
1600 ("microsecond", 8)):
1601 newargs = args[:]
1602 newargs[i] = newval
1603 expected = cls(*newargs)
1604 got = base.replace(**{name: newval})
1605 self.assertEqual(expected, got)
1606 i += 1
1607
1608 # Out of bounds.
1609 base = cls(2000, 2, 29)
1610 self.assertRaises(ValueError, base.replace, year=2001)
1611
Tim Peters80475bb2002-12-25 07:40:55 +00001612 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001613 # Pretty boring! The TZ test is more interesting here. astimezone()
1614 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001615 dt = self.theclass.now()
1616 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001617 self.assertRaises(TypeError, dt.astimezone) # not enough args
1618 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1619 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001620 self.assertRaises(ValueError, dt.astimezone, f) # naive
1621 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001622
Tim Peters52dcce22003-01-23 16:36:11 +00001623 class Bogus(tzinfo):
1624 def utcoffset(self, dt): return None
1625 def dst(self, dt): return timedelta(0)
1626 bog = Bogus()
1627 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1628
1629 class AlsoBogus(tzinfo):
1630 def utcoffset(self, dt): return timedelta(0)
1631 def dst(self, dt): return None
1632 alsobog = AlsoBogus()
1633 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001634
Tim Petersa98924a2003-05-17 05:55:19 +00001635 def test_subclass_datetime(self):
1636
1637 class C(self.theclass):
1638 theAnswer = 42
1639
1640 def __new__(cls, *args, **kws):
1641 temp = kws.copy()
1642 extra = temp.pop('extra')
1643 result = self.theclass.__new__(cls, *args, **temp)
1644 result.extra = extra
1645 return result
1646
1647 def newmeth(self, start):
1648 return start + self.year + self.month + self.second
1649
1650 args = 2003, 4, 14, 12, 13, 41
1651
1652 dt1 = self.theclass(*args)
1653 dt2 = C(*args, **{'extra': 7})
1654
1655 self.assertEqual(dt2.__class__, C)
1656 self.assertEqual(dt2.theAnswer, 42)
1657 self.assertEqual(dt2.extra, 7)
1658 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1659 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1660 dt1.second - 7)
1661
Tim Peters604c0132004-06-07 23:04:33 +00001662class SubclassTime(time):
1663 sub_var = 1
1664
Guido van Rossumd8faa362007-04-27 19:54:29 +00001665class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001666
1667 theclass = time
1668
1669 def test_basic_attributes(self):
1670 t = self.theclass(12, 0)
1671 self.assertEqual(t.hour, 12)
1672 self.assertEqual(t.minute, 0)
1673 self.assertEqual(t.second, 0)
1674 self.assertEqual(t.microsecond, 0)
1675
1676 def test_basic_attributes_nonzero(self):
1677 # Make sure all attributes are non-zero so bugs in
1678 # bit-shifting access show up.
1679 t = self.theclass(12, 59, 59, 8000)
1680 self.assertEqual(t.hour, 12)
1681 self.assertEqual(t.minute, 59)
1682 self.assertEqual(t.second, 59)
1683 self.assertEqual(t.microsecond, 8000)
1684
1685 def test_roundtrip(self):
1686 t = self.theclass(1, 2, 3, 4)
1687
1688 # Verify t -> string -> time identity.
1689 s = repr(t)
1690 self.failUnless(s.startswith('datetime.'))
1691 s = s[9:]
1692 t2 = eval(s)
1693 self.assertEqual(t, t2)
1694
1695 # Verify identity via reconstructing from pieces.
1696 t2 = self.theclass(t.hour, t.minute, t.second,
1697 t.microsecond)
1698 self.assertEqual(t, t2)
1699
1700 def test_comparing(self):
1701 args = [1, 2, 3, 4]
1702 t1 = self.theclass(*args)
1703 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001704 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001705 self.failUnless(t1 <= t2)
1706 self.failUnless(t1 >= t2)
1707 self.failUnless(not t1 != t2)
1708 self.failUnless(not t1 < t2)
1709 self.failUnless(not t1 > t2)
1710 self.assertEqual(cmp(t1, t2), 0)
1711 self.assertEqual(cmp(t2, t1), 0)
1712
1713 for i in range(len(args)):
1714 newargs = args[:]
1715 newargs[i] = args[i] + 1
1716 t2 = self.theclass(*newargs) # this is larger than t1
1717 self.failUnless(t1 < t2)
1718 self.failUnless(t2 > t1)
1719 self.failUnless(t1 <= t2)
1720 self.failUnless(t2 >= t1)
1721 self.failUnless(t1 != t2)
1722 self.failUnless(t2 != t1)
1723 self.failUnless(not t1 == t2)
1724 self.failUnless(not t2 == t1)
1725 self.failUnless(not t1 > t2)
1726 self.failUnless(not t2 < t1)
1727 self.failUnless(not t1 >= t2)
1728 self.failUnless(not t2 <= t1)
1729 self.assertEqual(cmp(t1, t2), -1)
1730 self.assertEqual(cmp(t2, t1), 1)
1731
Tim Peters68124bb2003-02-08 03:46:31 +00001732 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001733 self.assertEqual(t1 == badarg, False)
1734 self.assertEqual(t1 != badarg, True)
1735 self.assertEqual(badarg == t1, False)
1736 self.assertEqual(badarg != t1, True)
1737
Tim Peters2a799bf2002-12-16 20:18:38 +00001738 self.assertRaises(TypeError, lambda: t1 <= badarg)
1739 self.assertRaises(TypeError, lambda: t1 < badarg)
1740 self.assertRaises(TypeError, lambda: t1 > badarg)
1741 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001742 self.assertRaises(TypeError, lambda: badarg <= t1)
1743 self.assertRaises(TypeError, lambda: badarg < t1)
1744 self.assertRaises(TypeError, lambda: badarg > t1)
1745 self.assertRaises(TypeError, lambda: badarg >= t1)
1746
1747 def test_bad_constructor_arguments(self):
1748 # bad hours
1749 self.theclass(0, 0) # no exception
1750 self.theclass(23, 0) # no exception
1751 self.assertRaises(ValueError, self.theclass, -1, 0)
1752 self.assertRaises(ValueError, self.theclass, 24, 0)
1753 # bad minutes
1754 self.theclass(23, 0) # no exception
1755 self.theclass(23, 59) # no exception
1756 self.assertRaises(ValueError, self.theclass, 23, -1)
1757 self.assertRaises(ValueError, self.theclass, 23, 60)
1758 # bad seconds
1759 self.theclass(23, 59, 0) # no exception
1760 self.theclass(23, 59, 59) # no exception
1761 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1762 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1763 # bad microseconds
1764 self.theclass(23, 59, 59, 0) # no exception
1765 self.theclass(23, 59, 59, 999999) # no exception
1766 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1767 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1768
1769 def test_hash_equality(self):
1770 d = self.theclass(23, 30, 17)
1771 e = self.theclass(23, 30, 17)
1772 self.assertEqual(d, e)
1773 self.assertEqual(hash(d), hash(e))
1774
1775 dic = {d: 1}
1776 dic[e] = 2
1777 self.assertEqual(len(dic), 1)
1778 self.assertEqual(dic[d], 2)
1779 self.assertEqual(dic[e], 2)
1780
1781 d = self.theclass(0, 5, 17)
1782 e = self.theclass(0, 5, 17)
1783 self.assertEqual(d, e)
1784 self.assertEqual(hash(d), hash(e))
1785
1786 dic = {d: 1}
1787 dic[e] = 2
1788 self.assertEqual(len(dic), 1)
1789 self.assertEqual(dic[d], 2)
1790 self.assertEqual(dic[e], 2)
1791
1792 def test_isoformat(self):
1793 t = self.theclass(4, 5, 1, 123)
1794 self.assertEqual(t.isoformat(), "04:05:01.000123")
1795 self.assertEqual(t.isoformat(), str(t))
1796
1797 t = self.theclass()
1798 self.assertEqual(t.isoformat(), "00:00:00")
1799 self.assertEqual(t.isoformat(), str(t))
1800
1801 t = self.theclass(microsecond=1)
1802 self.assertEqual(t.isoformat(), "00:00:00.000001")
1803 self.assertEqual(t.isoformat(), str(t))
1804
1805 t = self.theclass(microsecond=10)
1806 self.assertEqual(t.isoformat(), "00:00:00.000010")
1807 self.assertEqual(t.isoformat(), str(t))
1808
1809 t = self.theclass(microsecond=100)
1810 self.assertEqual(t.isoformat(), "00:00:00.000100")
1811 self.assertEqual(t.isoformat(), str(t))
1812
1813 t = self.theclass(microsecond=1000)
1814 self.assertEqual(t.isoformat(), "00:00:00.001000")
1815 self.assertEqual(t.isoformat(), str(t))
1816
1817 t = self.theclass(microsecond=10000)
1818 self.assertEqual(t.isoformat(), "00:00:00.010000")
1819 self.assertEqual(t.isoformat(), str(t))
1820
1821 t = self.theclass(microsecond=100000)
1822 self.assertEqual(t.isoformat(), "00:00:00.100000")
1823 self.assertEqual(t.isoformat(), str(t))
1824
Thomas Wouterscf297e42007-02-23 15:07:44 +00001825 def test_1653736(self):
1826 # verify it doesn't accept extra keyword arguments
1827 t = self.theclass(second=1)
1828 self.assertRaises(TypeError, t.isoformat, foo=3)
1829
Tim Peters2a799bf2002-12-16 20:18:38 +00001830 def test_strftime(self):
1831 t = self.theclass(1, 2, 3, 4)
1832 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1833 # A naive object replaces %z and %Z with empty strings.
1834 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1835
Eric Smith1ba31142007-09-11 18:06:02 +00001836 def test_format(self):
1837 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001838 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001839
1840 # check that a derived class's __str__() gets called
1841 class A(self.theclass):
1842 def __str__(self):
1843 return 'A'
1844 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001845 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001846
1847 # check that a derived class's strftime gets called
1848 class B(self.theclass):
1849 def strftime(self, format_spec):
1850 return 'B'
1851 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001852 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001853
1854 for fmt in ['%H %M %S',
1855 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001856 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1857 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1858 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001859
Tim Peters2a799bf2002-12-16 20:18:38 +00001860 def test_str(self):
1861 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1862 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1863 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1864 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1865 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1866
1867 def test_repr(self):
1868 name = 'datetime.' + self.theclass.__name__
1869 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1870 "%s(1, 2, 3, 4)" % name)
1871 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1872 "%s(10, 2, 3, 4000)" % name)
1873 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1874 "%s(0, 2, 3, 400000)" % name)
1875 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1876 "%s(12, 2, 3)" % name)
1877 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1878 "%s(23, 15)" % name)
1879
1880 def test_resolution_info(self):
1881 self.assert_(isinstance(self.theclass.min, self.theclass))
1882 self.assert_(isinstance(self.theclass.max, self.theclass))
1883 self.assert_(isinstance(self.theclass.resolution, timedelta))
1884 self.assert_(self.theclass.max > self.theclass.min)
1885
1886 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001887 args = 20, 59, 16, 64**2
1888 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001889 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001890 green = pickler.dumps(orig, proto)
1891 derived = unpickler.loads(green)
1892 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001893
Tim Peters604c0132004-06-07 23:04:33 +00001894 def test_pickling_subclass_time(self):
1895 args = 20, 59, 16, 64**2
1896 orig = SubclassTime(*args)
1897 for pickler, unpickler, proto in pickle_choices:
1898 green = pickler.dumps(orig, proto)
1899 derived = unpickler.loads(green)
1900 self.assertEqual(orig, derived)
1901
Tim Peters2a799bf2002-12-16 20:18:38 +00001902 def test_bool(self):
1903 cls = self.theclass
1904 self.failUnless(cls(1))
1905 self.failUnless(cls(0, 1))
1906 self.failUnless(cls(0, 0, 1))
1907 self.failUnless(cls(0, 0, 0, 1))
1908 self.failUnless(not cls(0))
1909 self.failUnless(not cls())
1910
Tim Peters12bf3392002-12-24 05:41:27 +00001911 def test_replace(self):
1912 cls = self.theclass
1913 args = [1, 2, 3, 4]
1914 base = cls(*args)
1915 self.assertEqual(base, base.replace())
1916
1917 i = 0
1918 for name, newval in (("hour", 5),
1919 ("minute", 6),
1920 ("second", 7),
1921 ("microsecond", 8)):
1922 newargs = args[:]
1923 newargs[i] = newval
1924 expected = cls(*newargs)
1925 got = base.replace(**{name: newval})
1926 self.assertEqual(expected, got)
1927 i += 1
1928
1929 # Out of bounds.
1930 base = cls(1)
1931 self.assertRaises(ValueError, base.replace, hour=24)
1932 self.assertRaises(ValueError, base.replace, minute=-1)
1933 self.assertRaises(ValueError, base.replace, second=100)
1934 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1935
Tim Petersa98924a2003-05-17 05:55:19 +00001936 def test_subclass_time(self):
1937
1938 class C(self.theclass):
1939 theAnswer = 42
1940
1941 def __new__(cls, *args, **kws):
1942 temp = kws.copy()
1943 extra = temp.pop('extra')
1944 result = self.theclass.__new__(cls, *args, **temp)
1945 result.extra = extra
1946 return result
1947
1948 def newmeth(self, start):
1949 return start + self.hour + self.second
1950
1951 args = 4, 5, 6
1952
1953 dt1 = self.theclass(*args)
1954 dt2 = C(*args, **{'extra': 7})
1955
1956 self.assertEqual(dt2.__class__, C)
1957 self.assertEqual(dt2.theAnswer, 42)
1958 self.assertEqual(dt2.extra, 7)
1959 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1960 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1961
Armin Rigof4afb212005-11-07 07:15:48 +00001962 def test_backdoor_resistance(self):
1963 # see TestDate.test_backdoor_resistance().
1964 base = '2:59.0'
1965 for hour_byte in ' ', '9', chr(24), '\xff':
1966 self.assertRaises(TypeError, self.theclass,
1967 hour_byte + base[1:])
1968
Tim Peters855fe882002-12-22 03:43:39 +00001969# A mixin for classes with a tzinfo= argument. Subclasses must define
1970# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001971# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001972class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001973
Tim Petersbad8ff02002-12-30 20:52:32 +00001974 def test_argument_passing(self):
1975 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001976 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001977 class introspective(tzinfo):
1978 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001979 def utcoffset(self, dt):
1980 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001981 dst = utcoffset
1982
1983 obj = cls(1, 2, 3, tzinfo=introspective())
1984
Tim Peters0bf60bd2003-01-08 20:40:01 +00001985 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001986 self.assertEqual(obj.tzname(), expected)
1987
Tim Peters0bf60bd2003-01-08 20:40:01 +00001988 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001989 self.assertEqual(obj.utcoffset(), expected)
1990 self.assertEqual(obj.dst(), expected)
1991
Tim Peters855fe882002-12-22 03:43:39 +00001992 def test_bad_tzinfo_classes(self):
1993 cls = self.theclass
1994 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001995
Tim Peters855fe882002-12-22 03:43:39 +00001996 class NiceTry(object):
1997 def __init__(self): pass
1998 def utcoffset(self, dt): pass
1999 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2000
2001 class BetterTry(tzinfo):
2002 def __init__(self): pass
2003 def utcoffset(self, dt): pass
2004 b = BetterTry()
2005 t = cls(1, 1, 1, tzinfo=b)
2006 self.failUnless(t.tzinfo is b)
2007
2008 def test_utc_offset_out_of_bounds(self):
2009 class Edgy(tzinfo):
2010 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002011 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002012 def utcoffset(self, dt):
2013 return self.offset
2014
2015 cls = self.theclass
2016 for offset, legit in ((-1440, False),
2017 (-1439, True),
2018 (1439, True),
2019 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002020 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002021 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002022 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002023 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002024 else:
2025 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002026 if legit:
2027 aofs = abs(offset)
2028 h, m = divmod(aofs, 60)
2029 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002030 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002031 t = t.timetz()
2032 self.assertEqual(str(t), "01:02:03" + tag)
2033 else:
2034 self.assertRaises(ValueError, str, t)
2035
2036 def test_tzinfo_classes(self):
2037 cls = self.theclass
2038 class C1(tzinfo):
2039 def utcoffset(self, dt): return None
2040 def dst(self, dt): return None
2041 def tzname(self, dt): return None
2042 for t in (cls(1, 1, 1),
2043 cls(1, 1, 1, tzinfo=None),
2044 cls(1, 1, 1, tzinfo=C1())):
2045 self.failUnless(t.utcoffset() is None)
2046 self.failUnless(t.dst() is None)
2047 self.failUnless(t.tzname() is None)
2048
Tim Peters855fe882002-12-22 03:43:39 +00002049 class C3(tzinfo):
2050 def utcoffset(self, dt): return timedelta(minutes=-1439)
2051 def dst(self, dt): return timedelta(minutes=1439)
2052 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002053 t = cls(1, 1, 1, tzinfo=C3())
2054 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2055 self.assertEqual(t.dst(), timedelta(minutes=1439))
2056 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002057
2058 # Wrong types.
2059 class C4(tzinfo):
2060 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002061 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002062 def tzname(self, dt): return 0
2063 t = cls(1, 1, 1, tzinfo=C4())
2064 self.assertRaises(TypeError, t.utcoffset)
2065 self.assertRaises(TypeError, t.dst)
2066 self.assertRaises(TypeError, t.tzname)
2067
2068 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002069 class C6(tzinfo):
2070 def utcoffset(self, dt): return timedelta(hours=-24)
2071 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002072 t = cls(1, 1, 1, tzinfo=C6())
2073 self.assertRaises(ValueError, t.utcoffset)
2074 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002075
2076 # Not a whole number of minutes.
2077 class C7(tzinfo):
2078 def utcoffset(self, dt): return timedelta(seconds=61)
2079 def dst(self, dt): return timedelta(microseconds=-81)
2080 t = cls(1, 1, 1, tzinfo=C7())
2081 self.assertRaises(ValueError, t.utcoffset)
2082 self.assertRaises(ValueError, t.dst)
2083
Tim Peters4c0db782002-12-26 05:01:19 +00002084 def test_aware_compare(self):
2085 cls = self.theclass
2086
Tim Peters60c76e42002-12-27 00:41:11 +00002087 # Ensure that utcoffset() gets ignored if the comparands have
2088 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002089 class OperandDependentOffset(tzinfo):
2090 def utcoffset(self, t):
2091 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002092 # d0 and d1 equal after adjustment
2093 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002094 else:
Tim Peters397301e2003-01-02 21:28:08 +00002095 # d2 off in the weeds
2096 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002097
2098 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2099 d0 = base.replace(minute=3)
2100 d1 = base.replace(minute=9)
2101 d2 = base.replace(minute=11)
2102 for x in d0, d1, d2:
2103 for y in d0, d1, d2:
2104 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002105 expected = cmp(x.minute, y.minute)
2106 self.assertEqual(got, expected)
2107
2108 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002109 # Note that a time can't actually have an operand-depedent offset,
2110 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2111 # so skip this test for time.
2112 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002113 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2114 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2115 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2116 for x in d0, d1, d2:
2117 for y in d0, d1, d2:
2118 got = cmp(x, y)
2119 if (x is d0 or x is d1) and (y is d0 or y is d1):
2120 expected = 0
2121 elif x is y is d2:
2122 expected = 0
2123 elif x is d2:
2124 expected = -1
2125 else:
2126 assert y is d2
2127 expected = 1
2128 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002129
Tim Peters855fe882002-12-22 03:43:39 +00002130
Tim Peters0bf60bd2003-01-08 20:40:01 +00002131# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002132class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002133 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002134
2135 def test_empty(self):
2136 t = self.theclass()
2137 self.assertEqual(t.hour, 0)
2138 self.assertEqual(t.minute, 0)
2139 self.assertEqual(t.second, 0)
2140 self.assertEqual(t.microsecond, 0)
2141 self.failUnless(t.tzinfo is None)
2142
Tim Peters2a799bf2002-12-16 20:18:38 +00002143 def test_zones(self):
2144 est = FixedOffset(-300, "EST", 1)
2145 utc = FixedOffset(0, "UTC", -2)
2146 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002147 t1 = time( 7, 47, tzinfo=est)
2148 t2 = time(12, 47, tzinfo=utc)
2149 t3 = time(13, 47, tzinfo=met)
2150 t4 = time(microsecond=40)
2151 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002152
2153 self.assertEqual(t1.tzinfo, est)
2154 self.assertEqual(t2.tzinfo, utc)
2155 self.assertEqual(t3.tzinfo, met)
2156 self.failUnless(t4.tzinfo is None)
2157 self.assertEqual(t5.tzinfo, utc)
2158
Tim Peters855fe882002-12-22 03:43:39 +00002159 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2160 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2161 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002162 self.failUnless(t4.utcoffset() is None)
2163 self.assertRaises(TypeError, t1.utcoffset, "no args")
2164
2165 self.assertEqual(t1.tzname(), "EST")
2166 self.assertEqual(t2.tzname(), "UTC")
2167 self.assertEqual(t3.tzname(), "MET")
2168 self.failUnless(t4.tzname() is None)
2169 self.assertRaises(TypeError, t1.tzname, "no args")
2170
Tim Peters855fe882002-12-22 03:43:39 +00002171 self.assertEqual(t1.dst(), timedelta(minutes=1))
2172 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2173 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002174 self.failUnless(t4.dst() is None)
2175 self.assertRaises(TypeError, t1.dst, "no args")
2176
2177 self.assertEqual(hash(t1), hash(t2))
2178 self.assertEqual(hash(t1), hash(t3))
2179 self.assertEqual(hash(t2), hash(t3))
2180
2181 self.assertEqual(t1, t2)
2182 self.assertEqual(t1, t3)
2183 self.assertEqual(t2, t3)
2184 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2185 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2186 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2187
2188 self.assertEqual(str(t1), "07:47:00-05:00")
2189 self.assertEqual(str(t2), "12:47:00+00:00")
2190 self.assertEqual(str(t3), "13:47:00+01:00")
2191 self.assertEqual(str(t4), "00:00:00.000040")
2192 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2193
2194 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2195 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2196 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2197 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2198 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2199
Tim Peters0bf60bd2003-01-08 20:40:01 +00002200 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002201 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2202 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2203 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2204 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2205 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2206
2207 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2208 "07:47:00 %Z=EST %z=-0500")
2209 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2210 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2211
2212 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002213 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002214 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2215 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2216
Tim Petersb92bb712002-12-21 17:44:07 +00002217 # Check that an invalid tzname result raises an exception.
2218 class Badtzname(tzinfo):
2219 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002220 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002221 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2222 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002223
2224 def test_hash_edge_cases(self):
2225 # Offsets that overflow a basic time.
2226 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2227 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2228 self.assertEqual(hash(t1), hash(t2))
2229
2230 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2231 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2232 self.assertEqual(hash(t1), hash(t2))
2233
Tim Peters2a799bf2002-12-16 20:18:38 +00002234 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002235 # Try one without a tzinfo.
2236 args = 20, 59, 16, 64**2
2237 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002238 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002239 green = pickler.dumps(orig, proto)
2240 derived = unpickler.loads(green)
2241 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002242
2243 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002244 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002245 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002246 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002247 green = pickler.dumps(orig, proto)
2248 derived = unpickler.loads(green)
2249 self.assertEqual(orig, derived)
2250 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2251 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2252 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002253
2254 def test_more_bool(self):
2255 # Test cases with non-None tzinfo.
2256 cls = self.theclass
2257
2258 t = cls(0, tzinfo=FixedOffset(-300, ""))
2259 self.failUnless(t)
2260
2261 t = cls(5, tzinfo=FixedOffset(-300, ""))
2262 self.failUnless(t)
2263
2264 t = cls(5, tzinfo=FixedOffset(300, ""))
2265 self.failUnless(not t)
2266
2267 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2268 self.failUnless(not t)
2269
2270 # Mostly ensuring this doesn't overflow internally.
2271 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2272 self.failUnless(t)
2273
2274 # But this should yield a value error -- the utcoffset is bogus.
2275 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2276 self.assertRaises(ValueError, lambda: bool(t))
2277
2278 # Likewise.
2279 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2280 self.assertRaises(ValueError, lambda: bool(t))
2281
Tim Peters12bf3392002-12-24 05:41:27 +00002282 def test_replace(self):
2283 cls = self.theclass
2284 z100 = FixedOffset(100, "+100")
2285 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2286 args = [1, 2, 3, 4, z100]
2287 base = cls(*args)
2288 self.assertEqual(base, base.replace())
2289
2290 i = 0
2291 for name, newval in (("hour", 5),
2292 ("minute", 6),
2293 ("second", 7),
2294 ("microsecond", 8),
2295 ("tzinfo", zm200)):
2296 newargs = args[:]
2297 newargs[i] = newval
2298 expected = cls(*newargs)
2299 got = base.replace(**{name: newval})
2300 self.assertEqual(expected, got)
2301 i += 1
2302
2303 # Ensure we can get rid of a tzinfo.
2304 self.assertEqual(base.tzname(), "+100")
2305 base2 = base.replace(tzinfo=None)
2306 self.failUnless(base2.tzinfo is None)
2307 self.failUnless(base2.tzname() is None)
2308
2309 # Ensure we can add one.
2310 base3 = base2.replace(tzinfo=z100)
2311 self.assertEqual(base, base3)
2312 self.failUnless(base.tzinfo is base3.tzinfo)
2313
2314 # Out of bounds.
2315 base = cls(1)
2316 self.assertRaises(ValueError, base.replace, hour=24)
2317 self.assertRaises(ValueError, base.replace, minute=-1)
2318 self.assertRaises(ValueError, base.replace, second=100)
2319 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2320
Tim Peters60c76e42002-12-27 00:41:11 +00002321 def test_mixed_compare(self):
2322 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002323 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002324 self.assertEqual(t1, t2)
2325 t2 = t2.replace(tzinfo=None)
2326 self.assertEqual(t1, t2)
2327 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2328 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002329 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2330 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002331
Tim Peters0bf60bd2003-01-08 20:40:01 +00002332 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002333 class Varies(tzinfo):
2334 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002335 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002336 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002337 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002338 return self.offset
2339
2340 v = Varies()
2341 t1 = t2.replace(tzinfo=v)
2342 t2 = t2.replace(tzinfo=v)
2343 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2344 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2345 self.assertEqual(t1, t2)
2346
2347 # But if they're not identical, it isn't ignored.
2348 t2 = t2.replace(tzinfo=Varies())
2349 self.failUnless(t1 < t2) # t1's offset counter still going up
2350
Tim Petersa98924a2003-05-17 05:55:19 +00002351 def test_subclass_timetz(self):
2352
2353 class C(self.theclass):
2354 theAnswer = 42
2355
2356 def __new__(cls, *args, **kws):
2357 temp = kws.copy()
2358 extra = temp.pop('extra')
2359 result = self.theclass.__new__(cls, *args, **temp)
2360 result.extra = extra
2361 return result
2362
2363 def newmeth(self, start):
2364 return start + self.hour + self.second
2365
2366 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2367
2368 dt1 = self.theclass(*args)
2369 dt2 = C(*args, **{'extra': 7})
2370
2371 self.assertEqual(dt2.__class__, C)
2372 self.assertEqual(dt2.theAnswer, 42)
2373 self.assertEqual(dt2.extra, 7)
2374 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2375 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2376
Tim Peters4c0db782002-12-26 05:01:19 +00002377
Tim Peters0bf60bd2003-01-08 20:40:01 +00002378# Testing datetime objects with a non-None tzinfo.
2379
Guido van Rossumd8faa362007-04-27 19:54:29 +00002380class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002381 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002382
2383 def test_trivial(self):
2384 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2385 self.assertEqual(dt.year, 1)
2386 self.assertEqual(dt.month, 2)
2387 self.assertEqual(dt.day, 3)
2388 self.assertEqual(dt.hour, 4)
2389 self.assertEqual(dt.minute, 5)
2390 self.assertEqual(dt.second, 6)
2391 self.assertEqual(dt.microsecond, 7)
2392 self.assertEqual(dt.tzinfo, None)
2393
2394 def test_even_more_compare(self):
2395 # The test_compare() and test_more_compare() inherited from TestDate
2396 # and TestDateTime covered non-tzinfo cases.
2397
2398 # Smallest possible after UTC adjustment.
2399 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2400 # Largest possible after UTC adjustment.
2401 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2402 tzinfo=FixedOffset(-1439, ""))
2403
2404 # Make sure those compare correctly, and w/o overflow.
2405 self.failUnless(t1 < t2)
2406 self.failUnless(t1 != t2)
2407 self.failUnless(t2 > t1)
2408
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002409 self.assertEqual(t1, t1)
2410 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002411
2412 # Equal afer adjustment.
2413 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2414 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2415 self.assertEqual(t1, t2)
2416
2417 # Change t1 not to subtract a minute, and t1 should be larger.
2418 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2419 self.failUnless(t1 > t2)
2420
2421 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2422 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2423 self.failUnless(t1 < t2)
2424
2425 # Back to the original t1, but make seconds resolve it.
2426 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2427 second=1)
2428 self.failUnless(t1 > t2)
2429
2430 # Likewise, but make microseconds resolve it.
2431 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2432 microsecond=1)
2433 self.failUnless(t1 > t2)
2434
2435 # Make t2 naive and it should fail.
2436 t2 = self.theclass.min
2437 self.assertRaises(TypeError, lambda: t1 == t2)
2438 self.assertEqual(t2, t2)
2439
2440 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2441 class Naive(tzinfo):
2442 def utcoffset(self, dt): return None
2443 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2444 self.assertRaises(TypeError, lambda: t1 == t2)
2445 self.assertEqual(t2, t2)
2446
2447 # OTOH, it's OK to compare two of these mixing the two ways of being
2448 # naive.
2449 t1 = self.theclass(5, 6, 7)
2450 self.assertEqual(t1, t2)
2451
2452 # Try a bogus uctoffset.
2453 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002454 def utcoffset(self, dt):
2455 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002456 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2457 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002458 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002459
Tim Peters2a799bf2002-12-16 20:18:38 +00002460 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002461 # Try one without a tzinfo.
2462 args = 6, 7, 23, 20, 59, 1, 64**2
2463 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002464 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002465 green = pickler.dumps(orig, proto)
2466 derived = unpickler.loads(green)
2467 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002468
2469 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002470 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002471 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002472 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002473 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002474 green = pickler.dumps(orig, proto)
2475 derived = unpickler.loads(green)
2476 self.assertEqual(orig, derived)
2477 self.failUnless(isinstance(derived.tzinfo,
2478 PicklableFixedOffset))
2479 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2480 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002481
2482 def test_extreme_hashes(self):
2483 # If an attempt is made to hash these via subtracting the offset
2484 # then hashing a datetime object, OverflowError results. The
2485 # Python implementation used to blow up here.
2486 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2487 hash(t)
2488 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2489 tzinfo=FixedOffset(-1439, ""))
2490 hash(t)
2491
2492 # OTOH, an OOB offset should blow up.
2493 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2494 self.assertRaises(ValueError, hash, t)
2495
2496 def test_zones(self):
2497 est = FixedOffset(-300, "EST")
2498 utc = FixedOffset(0, "UTC")
2499 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002500 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2501 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2502 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002503 self.assertEqual(t1.tzinfo, est)
2504 self.assertEqual(t2.tzinfo, utc)
2505 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002506 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2507 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2508 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002509 self.assertEqual(t1.tzname(), "EST")
2510 self.assertEqual(t2.tzname(), "UTC")
2511 self.assertEqual(t3.tzname(), "MET")
2512 self.assertEqual(hash(t1), hash(t2))
2513 self.assertEqual(hash(t1), hash(t3))
2514 self.assertEqual(hash(t2), hash(t3))
2515 self.assertEqual(t1, t2)
2516 self.assertEqual(t1, t3)
2517 self.assertEqual(t2, t3)
2518 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2519 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2520 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002521 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002522 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2523 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2524 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2525
2526 def test_combine(self):
2527 met = FixedOffset(60, "MET")
2528 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002529 tz = time(18, 45, 3, 1234, tzinfo=met)
2530 dt = datetime.combine(d, tz)
2531 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002532 tzinfo=met))
2533
2534 def test_extract(self):
2535 met = FixedOffset(60, "MET")
2536 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2537 self.assertEqual(dt.date(), date(2002, 3, 4))
2538 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002539 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002540
2541 def test_tz_aware_arithmetic(self):
2542 import random
2543
2544 now = self.theclass.now()
2545 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002546 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002547 nowaware = self.theclass.combine(now.date(), timeaware)
2548 self.failUnless(nowaware.tzinfo is tz55)
2549 self.assertEqual(nowaware.timetz(), timeaware)
2550
2551 # Can't mix aware and non-aware.
2552 self.assertRaises(TypeError, lambda: now - nowaware)
2553 self.assertRaises(TypeError, lambda: nowaware - now)
2554
Tim Peters0bf60bd2003-01-08 20:40:01 +00002555 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002556 self.assertRaises(TypeError, lambda: now + nowaware)
2557 self.assertRaises(TypeError, lambda: nowaware + now)
2558 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2559
2560 # Subtracting should yield 0.
2561 self.assertEqual(now - now, timedelta(0))
2562 self.assertEqual(nowaware - nowaware, timedelta(0))
2563
2564 # Adding a delta should preserve tzinfo.
2565 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2566 nowawareplus = nowaware + delta
2567 self.failUnless(nowaware.tzinfo is tz55)
2568 nowawareplus2 = delta + nowaware
2569 self.failUnless(nowawareplus2.tzinfo is tz55)
2570 self.assertEqual(nowawareplus, nowawareplus2)
2571
2572 # that - delta should be what we started with, and that - what we
2573 # started with should be delta.
2574 diff = nowawareplus - delta
2575 self.failUnless(diff.tzinfo is tz55)
2576 self.assertEqual(nowaware, diff)
2577 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2578 self.assertEqual(nowawareplus - nowaware, delta)
2579
2580 # Make up a random timezone.
2581 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002582 # Attach it to nowawareplus.
2583 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002584 self.failUnless(nowawareplus.tzinfo is tzr)
2585 # Make sure the difference takes the timezone adjustments into account.
2586 got = nowaware - nowawareplus
2587 # Expected: (nowaware base - nowaware offset) -
2588 # (nowawareplus base - nowawareplus offset) =
2589 # (nowaware base - nowawareplus base) +
2590 # (nowawareplus offset - nowaware offset) =
2591 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002592 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002593 self.assertEqual(got, expected)
2594
2595 # Try max possible difference.
2596 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2597 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2598 tzinfo=FixedOffset(-1439, "max"))
2599 maxdiff = max - min
2600 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2601 timedelta(minutes=2*1439))
2602
2603 def test_tzinfo_now(self):
2604 meth = self.theclass.now
2605 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2606 base = meth()
2607 # Try with and without naming the keyword.
2608 off42 = FixedOffset(42, "42")
2609 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002610 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002611 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002612 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002613 # Bad argument with and w/o naming the keyword.
2614 self.assertRaises(TypeError, meth, 16)
2615 self.assertRaises(TypeError, meth, tzinfo=16)
2616 # Bad keyword name.
2617 self.assertRaises(TypeError, meth, tinfo=off42)
2618 # Too many args.
2619 self.assertRaises(TypeError, meth, off42, off42)
2620
Tim Peters10cadce2003-01-23 19:58:02 +00002621 # We don't know which time zone we're in, and don't have a tzinfo
2622 # class to represent it, so seeing whether a tz argument actually
2623 # does a conversion is tricky.
2624 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2625 utc = FixedOffset(0, "utc", 0)
2626 for dummy in range(3):
2627 now = datetime.now(weirdtz)
2628 self.failUnless(now.tzinfo is weirdtz)
2629 utcnow = datetime.utcnow().replace(tzinfo=utc)
2630 now2 = utcnow.astimezone(weirdtz)
2631 if abs(now - now2) < timedelta(seconds=30):
2632 break
2633 # Else the code is broken, or more than 30 seconds passed between
2634 # calls; assuming the latter, just try again.
2635 else:
2636 # Three strikes and we're out.
2637 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2638
Tim Peters2a799bf2002-12-16 20:18:38 +00002639 def test_tzinfo_fromtimestamp(self):
2640 import time
2641 meth = self.theclass.fromtimestamp
2642 ts = time.time()
2643 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2644 base = meth(ts)
2645 # Try with and without naming the keyword.
2646 off42 = FixedOffset(42, "42")
2647 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002648 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002649 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002650 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002651 # Bad argument with and w/o naming the keyword.
2652 self.assertRaises(TypeError, meth, ts, 16)
2653 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2654 # Bad keyword name.
2655 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2656 # Too many args.
2657 self.assertRaises(TypeError, meth, ts, off42, off42)
2658 # Too few args.
2659 self.assertRaises(TypeError, meth)
2660
Tim Peters2a44a8d2003-01-23 20:53:10 +00002661 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002662 timestamp = 1000000000
2663 utcdatetime = datetime.utcfromtimestamp(timestamp)
2664 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2665 # But on some flavor of Mac, it's nowhere near that. So we can't have
2666 # any idea here what time that actually is, we can only test that
2667 # relative changes match.
2668 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2669 tz = FixedOffset(utcoffset, "tz", 0)
2670 expected = utcdatetime + utcoffset
2671 got = datetime.fromtimestamp(timestamp, tz)
2672 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002673
Tim Peters2a799bf2002-12-16 20:18:38 +00002674 def test_tzinfo_utcnow(self):
2675 meth = self.theclass.utcnow
2676 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2677 base = meth()
2678 # Try with and without naming the keyword; for whatever reason,
2679 # utcnow() doesn't accept a tzinfo argument.
2680 off42 = FixedOffset(42, "42")
2681 self.assertRaises(TypeError, meth, off42)
2682 self.assertRaises(TypeError, meth, tzinfo=off42)
2683
2684 def test_tzinfo_utcfromtimestamp(self):
2685 import time
2686 meth = self.theclass.utcfromtimestamp
2687 ts = time.time()
2688 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2689 base = meth(ts)
2690 # Try with and without naming the keyword; for whatever reason,
2691 # utcfromtimestamp() doesn't accept a tzinfo argument.
2692 off42 = FixedOffset(42, "42")
2693 self.assertRaises(TypeError, meth, ts, off42)
2694 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2695
2696 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002697 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002698 # DST flag.
2699 class DST(tzinfo):
2700 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002701 if isinstance(dstvalue, int):
2702 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002703 self.dstvalue = dstvalue
2704 def dst(self, dt):
2705 return self.dstvalue
2706
2707 cls = self.theclass
2708 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2709 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2710 t = d.timetuple()
2711 self.assertEqual(1, t.tm_year)
2712 self.assertEqual(1, t.tm_mon)
2713 self.assertEqual(1, t.tm_mday)
2714 self.assertEqual(10, t.tm_hour)
2715 self.assertEqual(20, t.tm_min)
2716 self.assertEqual(30, t.tm_sec)
2717 self.assertEqual(0, t.tm_wday)
2718 self.assertEqual(1, t.tm_yday)
2719 self.assertEqual(flag, t.tm_isdst)
2720
2721 # dst() returns wrong type.
2722 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2723
2724 # dst() at the edge.
2725 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2726 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2727
2728 # dst() out of range.
2729 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2730 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2731
2732 def test_utctimetuple(self):
2733 class DST(tzinfo):
2734 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002735 if isinstance(dstvalue, int):
2736 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002737 self.dstvalue = dstvalue
2738 def dst(self, dt):
2739 return self.dstvalue
2740
2741 cls = self.theclass
2742 # This can't work: DST didn't implement utcoffset.
2743 self.assertRaises(NotImplementedError,
2744 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2745
2746 class UOFS(DST):
2747 def __init__(self, uofs, dofs=None):
2748 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002749 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002750 def utcoffset(self, dt):
2751 return self.uofs
2752
2753 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2754 # in effect for a UTC time.
2755 for dstvalue in -33, 33, 0, None:
2756 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2757 t = d.utctimetuple()
2758 self.assertEqual(d.year, t.tm_year)
2759 self.assertEqual(d.month, t.tm_mon)
2760 self.assertEqual(d.day, t.tm_mday)
2761 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2762 self.assertEqual(13, t.tm_min)
2763 self.assertEqual(d.second, t.tm_sec)
2764 self.assertEqual(d.weekday(), t.tm_wday)
2765 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2766 t.tm_yday)
2767 self.assertEqual(0, t.tm_isdst)
2768
2769 # At the edges, UTC adjustment can normalize into years out-of-range
2770 # for a datetime object. Ensure that a correct timetuple is
2771 # created anyway.
2772 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2773 # That goes back 1 minute less than a full day.
2774 t = tiny.utctimetuple()
2775 self.assertEqual(t.tm_year, MINYEAR-1)
2776 self.assertEqual(t.tm_mon, 12)
2777 self.assertEqual(t.tm_mday, 31)
2778 self.assertEqual(t.tm_hour, 0)
2779 self.assertEqual(t.tm_min, 1)
2780 self.assertEqual(t.tm_sec, 37)
2781 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2782 self.assertEqual(t.tm_isdst, 0)
2783
2784 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2785 # That goes forward 1 minute less than a full day.
2786 t = huge.utctimetuple()
2787 self.assertEqual(t.tm_year, MAXYEAR+1)
2788 self.assertEqual(t.tm_mon, 1)
2789 self.assertEqual(t.tm_mday, 1)
2790 self.assertEqual(t.tm_hour, 23)
2791 self.assertEqual(t.tm_min, 58)
2792 self.assertEqual(t.tm_sec, 37)
2793 self.assertEqual(t.tm_yday, 1)
2794 self.assertEqual(t.tm_isdst, 0)
2795
2796 def test_tzinfo_isoformat(self):
2797 zero = FixedOffset(0, "+00:00")
2798 plus = FixedOffset(220, "+03:40")
2799 minus = FixedOffset(-231, "-03:51")
2800 unknown = FixedOffset(None, "")
2801
2802 cls = self.theclass
2803 datestr = '0001-02-03'
2804 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002805 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002806 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2807 timestr = '04:05:59' + (us and '.987001' or '')
2808 ofsstr = ofs is not None and d.tzname() or ''
2809 tailstr = timestr + ofsstr
2810 iso = d.isoformat()
2811 self.assertEqual(iso, datestr + 'T' + tailstr)
2812 self.assertEqual(iso, d.isoformat('T'))
2813 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002814 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002815 self.assertEqual(str(d), datestr + ' ' + tailstr)
2816
Tim Peters12bf3392002-12-24 05:41:27 +00002817 def test_replace(self):
2818 cls = self.theclass
2819 z100 = FixedOffset(100, "+100")
2820 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2821 args = [1, 2, 3, 4, 5, 6, 7, z100]
2822 base = cls(*args)
2823 self.assertEqual(base, base.replace())
2824
2825 i = 0
2826 for name, newval in (("year", 2),
2827 ("month", 3),
2828 ("day", 4),
2829 ("hour", 5),
2830 ("minute", 6),
2831 ("second", 7),
2832 ("microsecond", 8),
2833 ("tzinfo", zm200)):
2834 newargs = args[:]
2835 newargs[i] = newval
2836 expected = cls(*newargs)
2837 got = base.replace(**{name: newval})
2838 self.assertEqual(expected, got)
2839 i += 1
2840
2841 # Ensure we can get rid of a tzinfo.
2842 self.assertEqual(base.tzname(), "+100")
2843 base2 = base.replace(tzinfo=None)
2844 self.failUnless(base2.tzinfo is None)
2845 self.failUnless(base2.tzname() is None)
2846
2847 # Ensure we can add one.
2848 base3 = base2.replace(tzinfo=z100)
2849 self.assertEqual(base, base3)
2850 self.failUnless(base.tzinfo is base3.tzinfo)
2851
2852 # Out of bounds.
2853 base = cls(2000, 2, 29)
2854 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002855
Tim Peters80475bb2002-12-25 07:40:55 +00002856 def test_more_astimezone(self):
2857 # The inherited test_astimezone covered some trivial and error cases.
2858 fnone = FixedOffset(None, "None")
2859 f44m = FixedOffset(44, "44")
2860 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2861
Tim Peters10cadce2003-01-23 19:58:02 +00002862 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002863 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002864 # Replacing with degenerate tzinfo raises an exception.
2865 self.assertRaises(ValueError, dt.astimezone, fnone)
2866 # Ditto with None tz.
2867 self.assertRaises(TypeError, dt.astimezone, None)
2868 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002869 x = dt.astimezone(dt.tzinfo)
2870 self.failUnless(x.tzinfo is f44m)
2871 self.assertEqual(x.date(), dt.date())
2872 self.assertEqual(x.time(), dt.time())
2873
2874 # Replacing with different tzinfo does adjust.
2875 got = dt.astimezone(fm5h)
2876 self.failUnless(got.tzinfo is fm5h)
2877 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2878 expected = dt - dt.utcoffset() # in effect, convert to UTC
2879 expected += fm5h.utcoffset(dt) # and from there to local time
2880 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2881 self.assertEqual(got.date(), expected.date())
2882 self.assertEqual(got.time(), expected.time())
2883 self.assertEqual(got.timetz(), expected.timetz())
2884 self.failUnless(got.tzinfo is expected.tzinfo)
2885 self.assertEqual(got, expected)
2886
Tim Peters4c0db782002-12-26 05:01:19 +00002887 def test_aware_subtract(self):
2888 cls = self.theclass
2889
Tim Peters60c76e42002-12-27 00:41:11 +00002890 # Ensure that utcoffset() is ignored when the operands have the
2891 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002892 class OperandDependentOffset(tzinfo):
2893 def utcoffset(self, t):
2894 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002895 # d0 and d1 equal after adjustment
2896 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002897 else:
Tim Peters397301e2003-01-02 21:28:08 +00002898 # d2 off in the weeds
2899 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002900
2901 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2902 d0 = base.replace(minute=3)
2903 d1 = base.replace(minute=9)
2904 d2 = base.replace(minute=11)
2905 for x in d0, d1, d2:
2906 for y in d0, d1, d2:
2907 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002908 expected = timedelta(minutes=x.minute - y.minute)
2909 self.assertEqual(got, expected)
2910
2911 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2912 # ignored.
2913 base = cls(8, 9, 10, 11, 12, 13, 14)
2914 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2915 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2916 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2917 for x in d0, d1, d2:
2918 for y in d0, d1, d2:
2919 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002920 if (x is d0 or x is d1) and (y is d0 or y is d1):
2921 expected = timedelta(0)
2922 elif x is y is d2:
2923 expected = timedelta(0)
2924 elif x is d2:
2925 expected = timedelta(minutes=(11-59)-0)
2926 else:
2927 assert y is d2
2928 expected = timedelta(minutes=0-(11-59))
2929 self.assertEqual(got, expected)
2930
Tim Peters60c76e42002-12-27 00:41:11 +00002931 def test_mixed_compare(self):
2932 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002933 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002934 self.assertEqual(t1, t2)
2935 t2 = t2.replace(tzinfo=None)
2936 self.assertEqual(t1, t2)
2937 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2938 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002939 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2940 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002941
Tim Peters0bf60bd2003-01-08 20:40:01 +00002942 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002943 class Varies(tzinfo):
2944 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002945 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002946 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002947 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002948 return self.offset
2949
2950 v = Varies()
2951 t1 = t2.replace(tzinfo=v)
2952 t2 = t2.replace(tzinfo=v)
2953 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2954 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2955 self.assertEqual(t1, t2)
2956
2957 # But if they're not identical, it isn't ignored.
2958 t2 = t2.replace(tzinfo=Varies())
2959 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002960
Tim Petersa98924a2003-05-17 05:55:19 +00002961 def test_subclass_datetimetz(self):
2962
2963 class C(self.theclass):
2964 theAnswer = 42
2965
2966 def __new__(cls, *args, **kws):
2967 temp = kws.copy()
2968 extra = temp.pop('extra')
2969 result = self.theclass.__new__(cls, *args, **temp)
2970 result.extra = extra
2971 return result
2972
2973 def newmeth(self, start):
2974 return start + self.hour + self.year
2975
2976 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2977
2978 dt1 = self.theclass(*args)
2979 dt2 = C(*args, **{'extra': 7})
2980
2981 self.assertEqual(dt2.__class__, C)
2982 self.assertEqual(dt2.theAnswer, 42)
2983 self.assertEqual(dt2.extra, 7)
2984 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2985 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2986
Tim Peters621818b2002-12-29 23:44:49 +00002987# Pain to set up DST-aware tzinfo classes.
2988
2989def first_sunday_on_or_after(dt):
2990 days_to_go = 6 - dt.weekday()
2991 if days_to_go:
2992 dt += timedelta(days_to_go)
2993 return dt
2994
2995ZERO = timedelta(0)
2996HOUR = timedelta(hours=1)
2997DAY = timedelta(days=1)
2998# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2999DSTSTART = datetime(1, 4, 1, 2)
3000# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003001# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3002# being standard time on that day, there is no spelling in local time of
3003# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3004DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003005
3006class USTimeZone(tzinfo):
3007
3008 def __init__(self, hours, reprname, stdname, dstname):
3009 self.stdoffset = timedelta(hours=hours)
3010 self.reprname = reprname
3011 self.stdname = stdname
3012 self.dstname = dstname
3013
3014 def __repr__(self):
3015 return self.reprname
3016
3017 def tzname(self, dt):
3018 if self.dst(dt):
3019 return self.dstname
3020 else:
3021 return self.stdname
3022
3023 def utcoffset(self, dt):
3024 return self.stdoffset + self.dst(dt)
3025
3026 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003027 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003028 # An exception instead may be sensible here, in one or more of
3029 # the cases.
3030 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003031 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003032
3033 # Find first Sunday in April.
3034 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3035 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3036
3037 # Find last Sunday in October.
3038 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3039 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3040
Tim Peters621818b2002-12-29 23:44:49 +00003041 # Can't compare naive to aware objects, so strip the timezone from
3042 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003043 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003044 return HOUR
3045 else:
3046 return ZERO
3047
Tim Peters521fc152002-12-31 17:36:56 +00003048Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3049Central = USTimeZone(-6, "Central", "CST", "CDT")
3050Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3051Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003052utc_real = FixedOffset(0, "UTC", 0)
3053# For better test coverage, we want another flavor of UTC that's west of
3054# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003055utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003056
3057class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003058 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003059 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003060 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003061
Tim Peters0bf60bd2003-01-08 20:40:01 +00003062 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003063
Tim Peters521fc152002-12-31 17:36:56 +00003064 # Check a time that's inside DST.
3065 def checkinside(self, dt, tz, utc, dston, dstoff):
3066 self.assertEqual(dt.dst(), HOUR)
3067
3068 # Conversion to our own timezone is always an identity.
3069 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003070
3071 asutc = dt.astimezone(utc)
3072 there_and_back = asutc.astimezone(tz)
3073
3074 # Conversion to UTC and back isn't always an identity here,
3075 # because there are redundant spellings (in local time) of
3076 # UTC time when DST begins: the clock jumps from 1:59:59
3077 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3078 # make sense then. The classes above treat 2:MM:SS as
3079 # daylight time then (it's "after 2am"), really an alias
3080 # for 1:MM:SS standard time. The latter form is what
3081 # conversion back from UTC produces.
3082 if dt.date() == dston.date() and dt.hour == 2:
3083 # We're in the redundant hour, and coming back from
3084 # UTC gives the 1:MM:SS standard-time spelling.
3085 self.assertEqual(there_and_back + HOUR, dt)
3086 # Although during was considered to be in daylight
3087 # time, there_and_back is not.
3088 self.assertEqual(there_and_back.dst(), ZERO)
3089 # They're the same times in UTC.
3090 self.assertEqual(there_and_back.astimezone(utc),
3091 dt.astimezone(utc))
3092 else:
3093 # We're not in the redundant hour.
3094 self.assertEqual(dt, there_and_back)
3095
Tim Peters327098a2003-01-20 22:54:38 +00003096 # Because we have a redundant spelling when DST begins, there is
3097 # (unforunately) an hour when DST ends that can't be spelled at all in
3098 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3099 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3100 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3101 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3102 # expressed in local time. Nevertheless, we want conversion back
3103 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003104 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003105 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003106 if dt.date() == dstoff.date() and dt.hour == 0:
3107 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003108 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003109 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3110 nexthour_utc += HOUR
3111 nexthour_tz = nexthour_utc.astimezone(tz)
3112 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003113 else:
Tim Peters327098a2003-01-20 22:54:38 +00003114 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003115
3116 # Check a time that's outside DST.
3117 def checkoutside(self, dt, tz, utc):
3118 self.assertEqual(dt.dst(), ZERO)
3119
3120 # Conversion to our own timezone is always an identity.
3121 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003122
3123 # Converting to UTC and back is an identity too.
3124 asutc = dt.astimezone(utc)
3125 there_and_back = asutc.astimezone(tz)
3126 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003127
Tim Peters1024bf82002-12-30 17:09:40 +00003128 def convert_between_tz_and_utc(self, tz, utc):
3129 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003130 # Because 1:MM on the day DST ends is taken as being standard time,
3131 # there is no spelling in tz for the last hour of daylight time.
3132 # For purposes of the test, the last hour of DST is 0:MM, which is
3133 # taken as being daylight time (and 1:MM is taken as being standard
3134 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003135 dstoff = self.dstoff.replace(tzinfo=tz)
3136 for delta in (timedelta(weeks=13),
3137 DAY,
3138 HOUR,
3139 timedelta(minutes=1),
3140 timedelta(microseconds=1)):
3141
Tim Peters521fc152002-12-31 17:36:56 +00003142 self.checkinside(dston, tz, utc, dston, dstoff)
3143 for during in dston + delta, dstoff - delta:
3144 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003145
Tim Peters521fc152002-12-31 17:36:56 +00003146 self.checkoutside(dstoff, tz, utc)
3147 for outside in dston - delta, dstoff + delta:
3148 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003149
Tim Peters621818b2002-12-29 23:44:49 +00003150 def test_easy(self):
3151 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003152 self.convert_between_tz_and_utc(Eastern, utc_real)
3153 self.convert_between_tz_and_utc(Pacific, utc_real)
3154 self.convert_between_tz_and_utc(Eastern, utc_fake)
3155 self.convert_between_tz_and_utc(Pacific, utc_fake)
3156 # The next is really dancing near the edge. It works because
3157 # Pacific and Eastern are far enough apart that their "problem
3158 # hours" don't overlap.
3159 self.convert_between_tz_and_utc(Eastern, Pacific)
3160 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003161 # OTOH, these fail! Don't enable them. The difficulty is that
3162 # the edge case tests assume that every hour is representable in
3163 # the "utc" class. This is always true for a fixed-offset tzinfo
3164 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3165 # For these adjacent DST-aware time zones, the range of time offsets
3166 # tested ends up creating hours in the one that aren't representable
3167 # in the other. For the same reason, we would see failures in the
3168 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3169 # offset deltas in convert_between_tz_and_utc().
3170 #
3171 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3172 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003173
Tim Petersf3615152003-01-01 21:51:37 +00003174 def test_tricky(self):
3175 # 22:00 on day before daylight starts.
3176 fourback = self.dston - timedelta(hours=4)
3177 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003178 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003179 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3180 # 2", we should get the 3 spelling.
3181 # If we plug 22:00 the day before into Eastern, it "looks like std
3182 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3183 # to 22:00 lands on 2:00, which makes no sense in local time (the
3184 # local clock jumps from 1 to 3). The point here is to make sure we
3185 # get the 3 spelling.
3186 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003187 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003188 self.assertEqual(expected, got)
3189
3190 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3191 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003192 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003193 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3194 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3195 # spelling.
3196 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003197 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003198 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003199
Tim Petersadf64202003-01-04 06:03:15 +00003200 # Now on the day DST ends, we want "repeat an hour" behavior.
3201 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3202 # EST 23:MM 0:MM 1:MM 2:MM
3203 # EDT 0:MM 1:MM 2:MM 3:MM
3204 # wall 0:MM 1:MM 1:MM 2:MM against these
3205 for utc in utc_real, utc_fake:
3206 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003207 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003208 # Convert that to UTC.
3209 first_std_hour -= tz.utcoffset(None)
3210 # Adjust for possibly fake UTC.
3211 asutc = first_std_hour + utc.utcoffset(None)
3212 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3213 # tz=Eastern.
3214 asutcbase = asutc.replace(tzinfo=utc)
3215 for tzhour in (0, 1, 1, 2):
3216 expectedbase = self.dstoff.replace(hour=tzhour)
3217 for minute in 0, 30, 59:
3218 expected = expectedbase.replace(minute=minute)
3219 asutc = asutcbase.replace(minute=minute)
3220 astz = asutc.astimezone(tz)
3221 self.assertEqual(astz.replace(tzinfo=None), expected)
3222 asutcbase += HOUR
3223
3224
Tim Peters710fb152003-01-02 19:35:54 +00003225 def test_bogus_dst(self):
3226 class ok(tzinfo):
3227 def utcoffset(self, dt): return HOUR
3228 def dst(self, dt): return HOUR
3229
3230 now = self.theclass.now().replace(tzinfo=utc_real)
3231 # Doesn't blow up.
3232 now.astimezone(ok())
3233
3234 # Does blow up.
3235 class notok(ok):
3236 def dst(self, dt): return None
3237 self.assertRaises(ValueError, now.astimezone, notok())
3238
Tim Peters52dcce22003-01-23 16:36:11 +00003239 def test_fromutc(self):
3240 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3241 now = datetime.utcnow().replace(tzinfo=utc_real)
3242 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3243 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3244 enow = Eastern.fromutc(now) # doesn't blow up
3245 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3246 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3247 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3248
3249 # Always converts UTC to standard time.
3250 class FauxUSTimeZone(USTimeZone):
3251 def fromutc(self, dt):
3252 return dt + self.stdoffset
3253 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3254
3255 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3256 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3257 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3258
3259 # Check around DST start.
3260 start = self.dston.replace(hour=4, tzinfo=Eastern)
3261 fstart = start.replace(tzinfo=FEastern)
3262 for wall in 23, 0, 1, 3, 4, 5:
3263 expected = start.replace(hour=wall)
3264 if wall == 23:
3265 expected -= timedelta(days=1)
3266 got = Eastern.fromutc(start)
3267 self.assertEqual(expected, got)
3268
3269 expected = fstart + FEastern.stdoffset
3270 got = FEastern.fromutc(fstart)
3271 self.assertEqual(expected, got)
3272
3273 # Ensure astimezone() calls fromutc() too.
3274 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3275 self.assertEqual(expected, got)
3276
3277 start += HOUR
3278 fstart += HOUR
3279
3280 # Check around DST end.
3281 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3282 fstart = start.replace(tzinfo=FEastern)
3283 for wall in 0, 1, 1, 2, 3, 4:
3284 expected = start.replace(hour=wall)
3285 got = Eastern.fromutc(start)
3286 self.assertEqual(expected, got)
3287
3288 expected = fstart + FEastern.stdoffset
3289 got = FEastern.fromutc(fstart)
3290 self.assertEqual(expected, got)
3291
3292 # Ensure astimezone() calls fromutc() too.
3293 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3294 self.assertEqual(expected, got)
3295
3296 start += HOUR
3297 fstart += HOUR
3298
Tim Peters710fb152003-01-02 19:35:54 +00003299
Tim Peters528ca532004-09-16 01:30:50 +00003300#############################################################################
3301# oddballs
3302
3303class Oddballs(unittest.TestCase):
3304
3305 def test_bug_1028306(self):
3306 # Trying to compare a date to a datetime should act like a mixed-
3307 # type comparison, despite that datetime is a subclass of date.
3308 as_date = date.today()
3309 as_datetime = datetime.combine(as_date, time())
3310 self.assert_(as_date != as_datetime)
3311 self.assert_(as_datetime != as_date)
3312 self.assert_(not as_date == as_datetime)
3313 self.assert_(not as_datetime == as_date)
3314 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3315 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3316 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3317 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3318 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3319 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3320 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3321 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3322
3323 # Neverthelss, comparison should work with the base-class (date)
3324 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003325 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003326 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003327 as_different = as_datetime.replace(day= different_day)
3328 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003329
3330 # And date should compare with other subclasses of date. If a
3331 # subclass wants to stop this, it's up to the subclass to do so.
3332 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3333 self.assertEqual(as_date, date_sc)
3334 self.assertEqual(date_sc, as_date)
3335
3336 # Ditto for datetimes.
3337 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3338 as_date.day, 0, 0, 0)
3339 self.assertEqual(as_datetime, datetime_sc)
3340 self.assertEqual(datetime_sc, as_datetime)
3341
Tim Peters2a799bf2002-12-16 20:18:38 +00003342def test_main():
Guido van Rossumd8faa362007-04-27 19:54:29 +00003343 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003344
3345if __name__ == "__main__":
3346 test_main()