blob: b1a1b3880204cc4f8b5310d1ee8bdd27b38c72e7 [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)
854 self.assertEqual(format(dt, ''), str(dt))
855
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)
861 self.assertEqual(format(a, ''), 'A')
862
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)
868 self.assertEqual(format(b, ''), str(dt))
869
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 ]:
874 self.assertEqual(format(dt, fmt), dt.strftime(fmt))
875 self.assertEqual(format(a, fmt), dt.strftime(fmt))
876 self.assertEqual(format(b, fmt), 'B')
877
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):
1096 # For fast unpickling, the constructor accepts a pickle string.
1097 # 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.
1104 base = '1995-03-25'
1105 if not issubclass(self.theclass, datetime):
1106 base = base[:4]
1107 for month_byte in '9', chr(0), chr(13), '\xff':
1108 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 Rossum39478e82007-08-27 17:23:59 +00001114 self.theclass(bytes(base[:2] + chr(ord_byte) + base[3:], "ascii"))
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)
1181 self.assertEqual(format(dt, ''), str(dt))
1182
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)
1188 self.assertEqual(format(a, ''), 'A')
1189
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)
1195 self.assertEqual(format(b, ''), str(dt))
1196
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 ]:
1201 self.assertEqual(format(dt, fmt), dt.strftime(fmt))
1202 self.assertEqual(format(a, fmt), dt.strftime(fmt))
1203 self.assertEqual(format(b, fmt), 'B')
1204
1205
1206
Tim Peters2a799bf2002-12-16 20:18:38 +00001207 def test_more_ctime(self):
1208 # Test fields that TestDate doesn't touch.
1209 import time
1210
1211 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1212 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1213 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1214 # out. The difference is that t.ctime() produces " 2" for the day,
1215 # but platform ctime() produces "02" for the day. According to
1216 # C99, t.ctime() is correct here.
1217 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1218
1219 # So test a case where that difference doesn't matter.
1220 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1221 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1222
1223 def test_tz_independent_comparing(self):
1224 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1225 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1226 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1227 self.assertEqual(dt1, dt3)
1228 self.assert_(dt2 > dt3)
1229
1230 # Make sure comparison doesn't forget microseconds, and isn't done
1231 # via comparing a float timestamp (an IEEE double doesn't have enough
1232 # precision to span microsecond resolution across years 1 thru 9999,
1233 # so comparing via timestamp necessarily calls some distinct values
1234 # equal).
1235 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1236 us = timedelta(microseconds=1)
1237 dt2 = dt1 + us
1238 self.assertEqual(dt2 - dt1, us)
1239 self.assert_(dt1 < dt2)
1240
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001241 def test_strftime_with_bad_tzname_replace(self):
1242 # verify ok if tzinfo.tzname().replace() returns a non-string
1243 class MyTzInfo(FixedOffset):
1244 def tzname(self, dt):
1245 class MyStr(str):
1246 def replace(self, *args):
1247 return None
1248 return MyStr('name')
1249 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1250 self.assertRaises(TypeError, t.strftime, '%Z')
1251
Tim Peters2a799bf2002-12-16 20:18:38 +00001252 def test_bad_constructor_arguments(self):
1253 # bad years
1254 self.theclass(MINYEAR, 1, 1) # no exception
1255 self.theclass(MAXYEAR, 1, 1) # no exception
1256 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1257 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1258 # bad months
1259 self.theclass(2000, 1, 1) # no exception
1260 self.theclass(2000, 12, 1) # no exception
1261 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1262 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1263 # bad days
1264 self.theclass(2000, 2, 29) # no exception
1265 self.theclass(2004, 2, 29) # no exception
1266 self.theclass(2400, 2, 29) # no exception
1267 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1268 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1269 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1270 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1271 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1272 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1273 # bad hours
1274 self.theclass(2000, 1, 31, 0) # no exception
1275 self.theclass(2000, 1, 31, 23) # no exception
1276 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1277 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1278 # bad minutes
1279 self.theclass(2000, 1, 31, 23, 0) # no exception
1280 self.theclass(2000, 1, 31, 23, 59) # no exception
1281 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1282 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1283 # bad seconds
1284 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1285 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1286 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1287 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1288 # bad microseconds
1289 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1290 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1291 self.assertRaises(ValueError, self.theclass,
1292 2000, 1, 31, 23, 59, 59, -1)
1293 self.assertRaises(ValueError, self.theclass,
1294 2000, 1, 31, 23, 59, 59,
1295 1000000)
1296
1297 def test_hash_equality(self):
1298 d = self.theclass(2000, 12, 31, 23, 30, 17)
1299 e = self.theclass(2000, 12, 31, 23, 30, 17)
1300 self.assertEqual(d, e)
1301 self.assertEqual(hash(d), hash(e))
1302
1303 dic = {d: 1}
1304 dic[e] = 2
1305 self.assertEqual(len(dic), 1)
1306 self.assertEqual(dic[d], 2)
1307 self.assertEqual(dic[e], 2)
1308
1309 d = self.theclass(2001, 1, 1, 0, 5, 17)
1310 e = self.theclass(2001, 1, 1, 0, 5, 17)
1311 self.assertEqual(d, e)
1312 self.assertEqual(hash(d), hash(e))
1313
1314 dic = {d: 1}
1315 dic[e] = 2
1316 self.assertEqual(len(dic), 1)
1317 self.assertEqual(dic[d], 2)
1318 self.assertEqual(dic[e], 2)
1319
1320 def test_computations(self):
1321 a = self.theclass(2002, 1, 31)
1322 b = self.theclass(1956, 1, 31)
1323 diff = a-b
1324 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1325 self.assertEqual(diff.seconds, 0)
1326 self.assertEqual(diff.microseconds, 0)
1327 a = self.theclass(2002, 3, 2, 17, 6)
1328 millisec = timedelta(0, 0, 1000)
1329 hour = timedelta(0, 3600)
1330 day = timedelta(1)
1331 week = timedelta(7)
1332 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1333 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1334 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1335 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1336 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1337 self.assertEqual(a - hour, a + -hour)
1338 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1339 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1340 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1341 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1342 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1343 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1344 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1345 self.assertEqual((a + week) - a, week)
1346 self.assertEqual((a + day) - a, day)
1347 self.assertEqual((a + hour) - a, hour)
1348 self.assertEqual((a + millisec) - a, millisec)
1349 self.assertEqual((a - week) - a, -week)
1350 self.assertEqual((a - day) - a, -day)
1351 self.assertEqual((a - hour) - a, -hour)
1352 self.assertEqual((a - millisec) - a, -millisec)
1353 self.assertEqual(a - (a + week), -week)
1354 self.assertEqual(a - (a + day), -day)
1355 self.assertEqual(a - (a + hour), -hour)
1356 self.assertEqual(a - (a + millisec), -millisec)
1357 self.assertEqual(a - (a - week), week)
1358 self.assertEqual(a - (a - day), day)
1359 self.assertEqual(a - (a - hour), hour)
1360 self.assertEqual(a - (a - millisec), millisec)
1361 self.assertEqual(a + (week + day + hour + millisec),
1362 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1363 self.assertEqual(a + (week + day + hour + millisec),
1364 (((a + week) + day) + hour) + millisec)
1365 self.assertEqual(a - (week + day + hour + millisec),
1366 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1367 self.assertEqual(a - (week + day + hour + millisec),
1368 (((a - week) - day) - hour) - millisec)
1369 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001370 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001371 self.assertRaises(TypeError, lambda: a+i)
1372 self.assertRaises(TypeError, lambda: a-i)
1373 self.assertRaises(TypeError, lambda: i+a)
1374 self.assertRaises(TypeError, lambda: i-a)
1375
1376 # delta - datetime is senseless.
1377 self.assertRaises(TypeError, lambda: day - a)
1378 # mixing datetime and (delta or datetime) via * or // is senseless
1379 self.assertRaises(TypeError, lambda: day * a)
1380 self.assertRaises(TypeError, lambda: a * day)
1381 self.assertRaises(TypeError, lambda: day // a)
1382 self.assertRaises(TypeError, lambda: a // day)
1383 self.assertRaises(TypeError, lambda: a * a)
1384 self.assertRaises(TypeError, lambda: a // a)
1385 # datetime + datetime is senseless
1386 self.assertRaises(TypeError, lambda: a + a)
1387
1388 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001389 args = 6, 7, 23, 20, 59, 1, 64**2
1390 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001391 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001392 green = pickler.dumps(orig, proto)
1393 derived = unpickler.loads(green)
1394 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001395
Guido van Rossum275666f2003-02-07 21:49:01 +00001396 def test_more_pickling(self):
1397 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1398 s = pickle.dumps(a)
1399 b = pickle.loads(s)
1400 self.assertEqual(b.year, 2003)
1401 self.assertEqual(b.month, 2)
1402 self.assertEqual(b.day, 7)
1403
Tim Peters604c0132004-06-07 23:04:33 +00001404 def test_pickling_subclass_datetime(self):
1405 args = 6, 7, 23, 20, 59, 1, 64**2
1406 orig = SubclassDatetime(*args)
1407 for pickler, unpickler, proto in pickle_choices:
1408 green = pickler.dumps(orig, proto)
1409 derived = unpickler.loads(green)
1410 self.assertEqual(orig, derived)
1411
Tim Peters2a799bf2002-12-16 20:18:38 +00001412 def test_more_compare(self):
1413 # The test_compare() inherited from TestDate covers the error cases.
1414 # We just want to test lexicographic ordering on the members datetime
1415 # has that date lacks.
1416 args = [2000, 11, 29, 20, 58, 16, 999998]
1417 t1 = self.theclass(*args)
1418 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001419 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001420 self.failUnless(t1 <= t2)
1421 self.failUnless(t1 >= t2)
1422 self.failUnless(not t1 != t2)
1423 self.failUnless(not t1 < t2)
1424 self.failUnless(not t1 > t2)
1425 self.assertEqual(cmp(t1, t2), 0)
1426 self.assertEqual(cmp(t2, t1), 0)
1427
1428 for i in range(len(args)):
1429 newargs = args[:]
1430 newargs[i] = args[i] + 1
1431 t2 = self.theclass(*newargs) # this is larger than t1
1432 self.failUnless(t1 < t2)
1433 self.failUnless(t2 > t1)
1434 self.failUnless(t1 <= t2)
1435 self.failUnless(t2 >= t1)
1436 self.failUnless(t1 != t2)
1437 self.failUnless(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.failUnless(not t1 >= t2)
1443 self.failUnless(not t2 <= t1)
1444 self.assertEqual(cmp(t1, t2), -1)
1445 self.assertEqual(cmp(t2, t1), 1)
1446
1447
1448 # A helper for timestamp constructor tests.
1449 def verify_field_equality(self, expected, got):
1450 self.assertEqual(expected.tm_year, got.year)
1451 self.assertEqual(expected.tm_mon, got.month)
1452 self.assertEqual(expected.tm_mday, got.day)
1453 self.assertEqual(expected.tm_hour, got.hour)
1454 self.assertEqual(expected.tm_min, got.minute)
1455 self.assertEqual(expected.tm_sec, got.second)
1456
1457 def test_fromtimestamp(self):
1458 import time
1459
1460 ts = time.time()
1461 expected = time.localtime(ts)
1462 got = self.theclass.fromtimestamp(ts)
1463 self.verify_field_equality(expected, got)
1464
1465 def test_utcfromtimestamp(self):
1466 import time
1467
1468 ts = time.time()
1469 expected = time.gmtime(ts)
1470 got = self.theclass.utcfromtimestamp(ts)
1471 self.verify_field_equality(expected, got)
1472
Thomas Wouters477c8d52006-05-27 19:21:47 +00001473 def test_microsecond_rounding(self):
1474 # Test whether fromtimestamp "rounds up" floats that are less
1475 # than one microsecond smaller than an integer.
1476 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1477 self.theclass.fromtimestamp(1))
1478
Tim Peters1b6f7a92004-06-20 02:50:16 +00001479 def test_insane_fromtimestamp(self):
1480 # It's possible that some platform maps time_t to double,
1481 # and that this test will fail there. This test should
1482 # exempt such platforms (provided they return reasonable
1483 # results!).
1484 for insane in -1e200, 1e200:
1485 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1486 insane)
1487
1488 def test_insane_utcfromtimestamp(self):
1489 # It's possible that some platform maps time_t to double,
1490 # and that this test will fail there. This test should
1491 # exempt such platforms (provided they return reasonable
1492 # results!).
1493 for insane in -1e200, 1e200:
1494 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1495 insane)
1496
Guido van Rossumd8faa362007-04-27 19:54:29 +00001497 def test_negative_float_fromtimestamp(self):
1498 # Windows doesn't accept negative timestamps
1499 if os.name == "nt":
1500 return
1501 # The result is tz-dependent; at least test that this doesn't
1502 # fail (like it did before bug 1646728 was fixed).
1503 self.theclass.fromtimestamp(-1.05)
1504
1505 def test_negative_float_utcfromtimestamp(self):
1506 # Windows doesn't accept negative timestamps
1507 if os.name == "nt":
1508 return
1509 d = self.theclass.utcfromtimestamp(-1.05)
1510 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1511
Tim Peters2a799bf2002-12-16 20:18:38 +00001512 def test_utcnow(self):
1513 import time
1514
1515 # Call it a success if utcnow() and utcfromtimestamp() are within
1516 # a second of each other.
1517 tolerance = timedelta(seconds=1)
1518 for dummy in range(3):
1519 from_now = self.theclass.utcnow()
1520 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1521 if abs(from_timestamp - from_now) <= tolerance:
1522 break
1523 # Else try again a few times.
1524 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1525
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001526 def test_strptime(self):
1527 import time
1528
1529 string = '2004-12-01 13:02:47'
1530 format = '%Y-%m-%d %H:%M:%S'
1531 expected = self.theclass(*(time.strptime(string, format)[0:6]))
1532 got = self.theclass.strptime(string, format)
1533 self.assertEqual(expected, got)
1534
Tim Peters2a799bf2002-12-16 20:18:38 +00001535 def test_more_timetuple(self):
1536 # This tests fields beyond those tested by the TestDate.test_timetuple.
1537 t = self.theclass(2004, 12, 31, 6, 22, 33)
1538 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1539 self.assertEqual(t.timetuple(),
1540 (t.year, t.month, t.day,
1541 t.hour, t.minute, t.second,
1542 t.weekday(),
1543 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1544 -1))
1545 tt = t.timetuple()
1546 self.assertEqual(tt.tm_year, t.year)
1547 self.assertEqual(tt.tm_mon, t.month)
1548 self.assertEqual(tt.tm_mday, t.day)
1549 self.assertEqual(tt.tm_hour, t.hour)
1550 self.assertEqual(tt.tm_min, t.minute)
1551 self.assertEqual(tt.tm_sec, t.second)
1552 self.assertEqual(tt.tm_wday, t.weekday())
1553 self.assertEqual(tt.tm_yday, t.toordinal() -
1554 date(t.year, 1, 1).toordinal() + 1)
1555 self.assertEqual(tt.tm_isdst, -1)
1556
1557 def test_more_strftime(self):
1558 # This tests fields beyond those tested by the TestDate.test_strftime.
1559 t = self.theclass(2004, 12, 31, 6, 22, 33)
1560 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1561 "12 31 04 33 22 06 366")
1562
1563 def test_extract(self):
1564 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1565 self.assertEqual(dt.date(), date(2002, 3, 4))
1566 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1567
1568 def test_combine(self):
1569 d = date(2002, 3, 4)
1570 t = time(18, 45, 3, 1234)
1571 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1572 combine = self.theclass.combine
1573 dt = combine(d, t)
1574 self.assertEqual(dt, expected)
1575
1576 dt = combine(time=t, date=d)
1577 self.assertEqual(dt, expected)
1578
1579 self.assertEqual(d, dt.date())
1580 self.assertEqual(t, dt.time())
1581 self.assertEqual(dt, combine(dt.date(), dt.time()))
1582
1583 self.assertRaises(TypeError, combine) # need an arg
1584 self.assertRaises(TypeError, combine, d) # need two args
1585 self.assertRaises(TypeError, combine, t, d) # args reversed
1586 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1587 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1588
Tim Peters12bf3392002-12-24 05:41:27 +00001589 def test_replace(self):
1590 cls = self.theclass
1591 args = [1, 2, 3, 4, 5, 6, 7]
1592 base = cls(*args)
1593 self.assertEqual(base, base.replace())
1594
1595 i = 0
1596 for name, newval in (("year", 2),
1597 ("month", 3),
1598 ("day", 4),
1599 ("hour", 5),
1600 ("minute", 6),
1601 ("second", 7),
1602 ("microsecond", 8)):
1603 newargs = args[:]
1604 newargs[i] = newval
1605 expected = cls(*newargs)
1606 got = base.replace(**{name: newval})
1607 self.assertEqual(expected, got)
1608 i += 1
1609
1610 # Out of bounds.
1611 base = cls(2000, 2, 29)
1612 self.assertRaises(ValueError, base.replace, year=2001)
1613
Tim Peters80475bb2002-12-25 07:40:55 +00001614 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001615 # Pretty boring! The TZ test is more interesting here. astimezone()
1616 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001617 dt = self.theclass.now()
1618 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001619 self.assertRaises(TypeError, dt.astimezone) # not enough args
1620 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1621 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001622 self.assertRaises(ValueError, dt.astimezone, f) # naive
1623 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001624
Tim Peters52dcce22003-01-23 16:36:11 +00001625 class Bogus(tzinfo):
1626 def utcoffset(self, dt): return None
1627 def dst(self, dt): return timedelta(0)
1628 bog = Bogus()
1629 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1630
1631 class AlsoBogus(tzinfo):
1632 def utcoffset(self, dt): return timedelta(0)
1633 def dst(self, dt): return None
1634 alsobog = AlsoBogus()
1635 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001636
Tim Petersa98924a2003-05-17 05:55:19 +00001637 def test_subclass_datetime(self):
1638
1639 class C(self.theclass):
1640 theAnswer = 42
1641
1642 def __new__(cls, *args, **kws):
1643 temp = kws.copy()
1644 extra = temp.pop('extra')
1645 result = self.theclass.__new__(cls, *args, **temp)
1646 result.extra = extra
1647 return result
1648
1649 def newmeth(self, start):
1650 return start + self.year + self.month + self.second
1651
1652 args = 2003, 4, 14, 12, 13, 41
1653
1654 dt1 = self.theclass(*args)
1655 dt2 = C(*args, **{'extra': 7})
1656
1657 self.assertEqual(dt2.__class__, C)
1658 self.assertEqual(dt2.theAnswer, 42)
1659 self.assertEqual(dt2.extra, 7)
1660 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1661 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1662 dt1.second - 7)
1663
Tim Peters604c0132004-06-07 23:04:33 +00001664class SubclassTime(time):
1665 sub_var = 1
1666
Guido van Rossumd8faa362007-04-27 19:54:29 +00001667class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001668
1669 theclass = time
1670
1671 def test_basic_attributes(self):
1672 t = self.theclass(12, 0)
1673 self.assertEqual(t.hour, 12)
1674 self.assertEqual(t.minute, 0)
1675 self.assertEqual(t.second, 0)
1676 self.assertEqual(t.microsecond, 0)
1677
1678 def test_basic_attributes_nonzero(self):
1679 # Make sure all attributes are non-zero so bugs in
1680 # bit-shifting access show up.
1681 t = self.theclass(12, 59, 59, 8000)
1682 self.assertEqual(t.hour, 12)
1683 self.assertEqual(t.minute, 59)
1684 self.assertEqual(t.second, 59)
1685 self.assertEqual(t.microsecond, 8000)
1686
1687 def test_roundtrip(self):
1688 t = self.theclass(1, 2, 3, 4)
1689
1690 # Verify t -> string -> time identity.
1691 s = repr(t)
1692 self.failUnless(s.startswith('datetime.'))
1693 s = s[9:]
1694 t2 = eval(s)
1695 self.assertEqual(t, t2)
1696
1697 # Verify identity via reconstructing from pieces.
1698 t2 = self.theclass(t.hour, t.minute, t.second,
1699 t.microsecond)
1700 self.assertEqual(t, t2)
1701
1702 def test_comparing(self):
1703 args = [1, 2, 3, 4]
1704 t1 = self.theclass(*args)
1705 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001706 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001707 self.failUnless(t1 <= t2)
1708 self.failUnless(t1 >= t2)
1709 self.failUnless(not t1 != t2)
1710 self.failUnless(not t1 < t2)
1711 self.failUnless(not t1 > t2)
1712 self.assertEqual(cmp(t1, t2), 0)
1713 self.assertEqual(cmp(t2, t1), 0)
1714
1715 for i in range(len(args)):
1716 newargs = args[:]
1717 newargs[i] = args[i] + 1
1718 t2 = self.theclass(*newargs) # this is larger than t1
1719 self.failUnless(t1 < t2)
1720 self.failUnless(t2 > t1)
1721 self.failUnless(t1 <= t2)
1722 self.failUnless(t2 >= t1)
1723 self.failUnless(t1 != t2)
1724 self.failUnless(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.failUnless(not t1 >= t2)
1730 self.failUnless(not t2 <= t1)
1731 self.assertEqual(cmp(t1, t2), -1)
1732 self.assertEqual(cmp(t2, t1), 1)
1733
Tim Peters68124bb2003-02-08 03:46:31 +00001734 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001735 self.assertEqual(t1 == badarg, False)
1736 self.assertEqual(t1 != badarg, True)
1737 self.assertEqual(badarg == t1, False)
1738 self.assertEqual(badarg != t1, True)
1739
Tim Peters2a799bf2002-12-16 20:18:38 +00001740 self.assertRaises(TypeError, lambda: t1 <= badarg)
1741 self.assertRaises(TypeError, lambda: t1 < badarg)
1742 self.assertRaises(TypeError, lambda: t1 > badarg)
1743 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001744 self.assertRaises(TypeError, lambda: badarg <= t1)
1745 self.assertRaises(TypeError, lambda: badarg < t1)
1746 self.assertRaises(TypeError, lambda: badarg > t1)
1747 self.assertRaises(TypeError, lambda: badarg >= t1)
1748
1749 def test_bad_constructor_arguments(self):
1750 # bad hours
1751 self.theclass(0, 0) # no exception
1752 self.theclass(23, 0) # no exception
1753 self.assertRaises(ValueError, self.theclass, -1, 0)
1754 self.assertRaises(ValueError, self.theclass, 24, 0)
1755 # bad minutes
1756 self.theclass(23, 0) # no exception
1757 self.theclass(23, 59) # no exception
1758 self.assertRaises(ValueError, self.theclass, 23, -1)
1759 self.assertRaises(ValueError, self.theclass, 23, 60)
1760 # bad seconds
1761 self.theclass(23, 59, 0) # no exception
1762 self.theclass(23, 59, 59) # no exception
1763 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1764 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1765 # bad microseconds
1766 self.theclass(23, 59, 59, 0) # no exception
1767 self.theclass(23, 59, 59, 999999) # no exception
1768 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1769 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1770
1771 def test_hash_equality(self):
1772 d = self.theclass(23, 30, 17)
1773 e = self.theclass(23, 30, 17)
1774 self.assertEqual(d, e)
1775 self.assertEqual(hash(d), hash(e))
1776
1777 dic = {d: 1}
1778 dic[e] = 2
1779 self.assertEqual(len(dic), 1)
1780 self.assertEqual(dic[d], 2)
1781 self.assertEqual(dic[e], 2)
1782
1783 d = self.theclass(0, 5, 17)
1784 e = self.theclass(0, 5, 17)
1785 self.assertEqual(d, e)
1786 self.assertEqual(hash(d), hash(e))
1787
1788 dic = {d: 1}
1789 dic[e] = 2
1790 self.assertEqual(len(dic), 1)
1791 self.assertEqual(dic[d], 2)
1792 self.assertEqual(dic[e], 2)
1793
1794 def test_isoformat(self):
1795 t = self.theclass(4, 5, 1, 123)
1796 self.assertEqual(t.isoformat(), "04:05:01.000123")
1797 self.assertEqual(t.isoformat(), str(t))
1798
1799 t = self.theclass()
1800 self.assertEqual(t.isoformat(), "00:00:00")
1801 self.assertEqual(t.isoformat(), str(t))
1802
1803 t = self.theclass(microsecond=1)
1804 self.assertEqual(t.isoformat(), "00:00:00.000001")
1805 self.assertEqual(t.isoformat(), str(t))
1806
1807 t = self.theclass(microsecond=10)
1808 self.assertEqual(t.isoformat(), "00:00:00.000010")
1809 self.assertEqual(t.isoformat(), str(t))
1810
1811 t = self.theclass(microsecond=100)
1812 self.assertEqual(t.isoformat(), "00:00:00.000100")
1813 self.assertEqual(t.isoformat(), str(t))
1814
1815 t = self.theclass(microsecond=1000)
1816 self.assertEqual(t.isoformat(), "00:00:00.001000")
1817 self.assertEqual(t.isoformat(), str(t))
1818
1819 t = self.theclass(microsecond=10000)
1820 self.assertEqual(t.isoformat(), "00:00:00.010000")
1821 self.assertEqual(t.isoformat(), str(t))
1822
1823 t = self.theclass(microsecond=100000)
1824 self.assertEqual(t.isoformat(), "00:00:00.100000")
1825 self.assertEqual(t.isoformat(), str(t))
1826
Thomas Wouterscf297e42007-02-23 15:07:44 +00001827 def test_1653736(self):
1828 # verify it doesn't accept extra keyword arguments
1829 t = self.theclass(second=1)
1830 self.assertRaises(TypeError, t.isoformat, foo=3)
1831
Tim Peters2a799bf2002-12-16 20:18:38 +00001832 def test_strftime(self):
1833 t = self.theclass(1, 2, 3, 4)
1834 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1835 # A naive object replaces %z and %Z with empty strings.
1836 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1837
Eric Smith1ba31142007-09-11 18:06:02 +00001838 def test_format(self):
1839 t = self.theclass(1, 2, 3, 4)
1840 self.assertEqual(format(t, ''), str(t))
1841
1842 # check that a derived class's __str__() gets called
1843 class A(self.theclass):
1844 def __str__(self):
1845 return 'A'
1846 a = A(1, 2, 3, 4)
1847 self.assertEqual(format(a, ''), 'A')
1848
1849 # check that a derived class's strftime gets called
1850 class B(self.theclass):
1851 def strftime(self, format_spec):
1852 return 'B'
1853 b = B(1, 2, 3, 4)
1854 self.assertEqual(format(b, ''), str(t))
1855
1856 for fmt in ['%H %M %S',
1857 ]:
1858 self.assertEqual(format(t, fmt), t.strftime(fmt))
1859 self.assertEqual(format(a, fmt), t.strftime(fmt))
1860 self.assertEqual(format(b, fmt), 'B')
1861
Tim Peters2a799bf2002-12-16 20:18:38 +00001862 def test_str(self):
1863 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1864 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1865 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1866 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1867 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1868
1869 def test_repr(self):
1870 name = 'datetime.' + self.theclass.__name__
1871 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1872 "%s(1, 2, 3, 4)" % name)
1873 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1874 "%s(10, 2, 3, 4000)" % name)
1875 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1876 "%s(0, 2, 3, 400000)" % name)
1877 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1878 "%s(12, 2, 3)" % name)
1879 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1880 "%s(23, 15)" % name)
1881
1882 def test_resolution_info(self):
1883 self.assert_(isinstance(self.theclass.min, self.theclass))
1884 self.assert_(isinstance(self.theclass.max, self.theclass))
1885 self.assert_(isinstance(self.theclass.resolution, timedelta))
1886 self.assert_(self.theclass.max > self.theclass.min)
1887
1888 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001889 args = 20, 59, 16, 64**2
1890 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001891 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001892 green = pickler.dumps(orig, proto)
1893 derived = unpickler.loads(green)
1894 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001895
Tim Peters604c0132004-06-07 23:04:33 +00001896 def test_pickling_subclass_time(self):
1897 args = 20, 59, 16, 64**2
1898 orig = SubclassTime(*args)
1899 for pickler, unpickler, proto in pickle_choices:
1900 green = pickler.dumps(orig, proto)
1901 derived = unpickler.loads(green)
1902 self.assertEqual(orig, derived)
1903
Tim Peters2a799bf2002-12-16 20:18:38 +00001904 def test_bool(self):
1905 cls = self.theclass
1906 self.failUnless(cls(1))
1907 self.failUnless(cls(0, 1))
1908 self.failUnless(cls(0, 0, 1))
1909 self.failUnless(cls(0, 0, 0, 1))
1910 self.failUnless(not cls(0))
1911 self.failUnless(not cls())
1912
Tim Peters12bf3392002-12-24 05:41:27 +00001913 def test_replace(self):
1914 cls = self.theclass
1915 args = [1, 2, 3, 4]
1916 base = cls(*args)
1917 self.assertEqual(base, base.replace())
1918
1919 i = 0
1920 for name, newval in (("hour", 5),
1921 ("minute", 6),
1922 ("second", 7),
1923 ("microsecond", 8)):
1924 newargs = args[:]
1925 newargs[i] = newval
1926 expected = cls(*newargs)
1927 got = base.replace(**{name: newval})
1928 self.assertEqual(expected, got)
1929 i += 1
1930
1931 # Out of bounds.
1932 base = cls(1)
1933 self.assertRaises(ValueError, base.replace, hour=24)
1934 self.assertRaises(ValueError, base.replace, minute=-1)
1935 self.assertRaises(ValueError, base.replace, second=100)
1936 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1937
Tim Petersa98924a2003-05-17 05:55:19 +00001938 def test_subclass_time(self):
1939
1940 class C(self.theclass):
1941 theAnswer = 42
1942
1943 def __new__(cls, *args, **kws):
1944 temp = kws.copy()
1945 extra = temp.pop('extra')
1946 result = self.theclass.__new__(cls, *args, **temp)
1947 result.extra = extra
1948 return result
1949
1950 def newmeth(self, start):
1951 return start + self.hour + self.second
1952
1953 args = 4, 5, 6
1954
1955 dt1 = self.theclass(*args)
1956 dt2 = C(*args, **{'extra': 7})
1957
1958 self.assertEqual(dt2.__class__, C)
1959 self.assertEqual(dt2.theAnswer, 42)
1960 self.assertEqual(dt2.extra, 7)
1961 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1962 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1963
Armin Rigof4afb212005-11-07 07:15:48 +00001964 def test_backdoor_resistance(self):
1965 # see TestDate.test_backdoor_resistance().
1966 base = '2:59.0'
1967 for hour_byte in ' ', '9', chr(24), '\xff':
1968 self.assertRaises(TypeError, self.theclass,
1969 hour_byte + base[1:])
1970
Tim Peters855fe882002-12-22 03:43:39 +00001971# A mixin for classes with a tzinfo= argument. Subclasses must define
1972# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001973# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001974class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001975
Tim Petersbad8ff02002-12-30 20:52:32 +00001976 def test_argument_passing(self):
1977 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001978 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001979 class introspective(tzinfo):
1980 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001981 def utcoffset(self, dt):
1982 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001983 dst = utcoffset
1984
1985 obj = cls(1, 2, 3, tzinfo=introspective())
1986
Tim Peters0bf60bd2003-01-08 20:40:01 +00001987 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001988 self.assertEqual(obj.tzname(), expected)
1989
Tim Peters0bf60bd2003-01-08 20:40:01 +00001990 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001991 self.assertEqual(obj.utcoffset(), expected)
1992 self.assertEqual(obj.dst(), expected)
1993
Tim Peters855fe882002-12-22 03:43:39 +00001994 def test_bad_tzinfo_classes(self):
1995 cls = self.theclass
1996 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001997
Tim Peters855fe882002-12-22 03:43:39 +00001998 class NiceTry(object):
1999 def __init__(self): pass
2000 def utcoffset(self, dt): pass
2001 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2002
2003 class BetterTry(tzinfo):
2004 def __init__(self): pass
2005 def utcoffset(self, dt): pass
2006 b = BetterTry()
2007 t = cls(1, 1, 1, tzinfo=b)
2008 self.failUnless(t.tzinfo is b)
2009
2010 def test_utc_offset_out_of_bounds(self):
2011 class Edgy(tzinfo):
2012 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002013 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002014 def utcoffset(self, dt):
2015 return self.offset
2016
2017 cls = self.theclass
2018 for offset, legit in ((-1440, False),
2019 (-1439, True),
2020 (1439, True),
2021 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002022 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002023 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002024 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002025 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002026 else:
2027 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002028 if legit:
2029 aofs = abs(offset)
2030 h, m = divmod(aofs, 60)
2031 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002032 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002033 t = t.timetz()
2034 self.assertEqual(str(t), "01:02:03" + tag)
2035 else:
2036 self.assertRaises(ValueError, str, t)
2037
2038 def test_tzinfo_classes(self):
2039 cls = self.theclass
2040 class C1(tzinfo):
2041 def utcoffset(self, dt): return None
2042 def dst(self, dt): return None
2043 def tzname(self, dt): return None
2044 for t in (cls(1, 1, 1),
2045 cls(1, 1, 1, tzinfo=None),
2046 cls(1, 1, 1, tzinfo=C1())):
2047 self.failUnless(t.utcoffset() is None)
2048 self.failUnless(t.dst() is None)
2049 self.failUnless(t.tzname() is None)
2050
Tim Peters855fe882002-12-22 03:43:39 +00002051 class C3(tzinfo):
2052 def utcoffset(self, dt): return timedelta(minutes=-1439)
2053 def dst(self, dt): return timedelta(minutes=1439)
2054 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002055 t = cls(1, 1, 1, tzinfo=C3())
2056 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2057 self.assertEqual(t.dst(), timedelta(minutes=1439))
2058 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002059
2060 # Wrong types.
2061 class C4(tzinfo):
2062 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002063 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002064 def tzname(self, dt): return 0
2065 t = cls(1, 1, 1, tzinfo=C4())
2066 self.assertRaises(TypeError, t.utcoffset)
2067 self.assertRaises(TypeError, t.dst)
2068 self.assertRaises(TypeError, t.tzname)
2069
2070 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002071 class C6(tzinfo):
2072 def utcoffset(self, dt): return timedelta(hours=-24)
2073 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002074 t = cls(1, 1, 1, tzinfo=C6())
2075 self.assertRaises(ValueError, t.utcoffset)
2076 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002077
2078 # Not a whole number of minutes.
2079 class C7(tzinfo):
2080 def utcoffset(self, dt): return timedelta(seconds=61)
2081 def dst(self, dt): return timedelta(microseconds=-81)
2082 t = cls(1, 1, 1, tzinfo=C7())
2083 self.assertRaises(ValueError, t.utcoffset)
2084 self.assertRaises(ValueError, t.dst)
2085
Tim Peters4c0db782002-12-26 05:01:19 +00002086 def test_aware_compare(self):
2087 cls = self.theclass
2088
Tim Peters60c76e42002-12-27 00:41:11 +00002089 # Ensure that utcoffset() gets ignored if the comparands have
2090 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002091 class OperandDependentOffset(tzinfo):
2092 def utcoffset(self, t):
2093 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002094 # d0 and d1 equal after adjustment
2095 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002096 else:
Tim Peters397301e2003-01-02 21:28:08 +00002097 # d2 off in the weeds
2098 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002099
2100 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2101 d0 = base.replace(minute=3)
2102 d1 = base.replace(minute=9)
2103 d2 = base.replace(minute=11)
2104 for x in d0, d1, d2:
2105 for y in d0, d1, d2:
2106 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002107 expected = cmp(x.minute, y.minute)
2108 self.assertEqual(got, expected)
2109
2110 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002111 # Note that a time can't actually have an operand-depedent offset,
2112 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2113 # so skip this test for time.
2114 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002115 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2116 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2117 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2118 for x in d0, d1, d2:
2119 for y in d0, d1, d2:
2120 got = cmp(x, y)
2121 if (x is d0 or x is d1) and (y is d0 or y is d1):
2122 expected = 0
2123 elif x is y is d2:
2124 expected = 0
2125 elif x is d2:
2126 expected = -1
2127 else:
2128 assert y is d2
2129 expected = 1
2130 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002131
Tim Peters855fe882002-12-22 03:43:39 +00002132
Tim Peters0bf60bd2003-01-08 20:40:01 +00002133# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002134class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002135 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002136
2137 def test_empty(self):
2138 t = self.theclass()
2139 self.assertEqual(t.hour, 0)
2140 self.assertEqual(t.minute, 0)
2141 self.assertEqual(t.second, 0)
2142 self.assertEqual(t.microsecond, 0)
2143 self.failUnless(t.tzinfo is None)
2144
Tim Peters2a799bf2002-12-16 20:18:38 +00002145 def test_zones(self):
2146 est = FixedOffset(-300, "EST", 1)
2147 utc = FixedOffset(0, "UTC", -2)
2148 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002149 t1 = time( 7, 47, tzinfo=est)
2150 t2 = time(12, 47, tzinfo=utc)
2151 t3 = time(13, 47, tzinfo=met)
2152 t4 = time(microsecond=40)
2153 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002154
2155 self.assertEqual(t1.tzinfo, est)
2156 self.assertEqual(t2.tzinfo, utc)
2157 self.assertEqual(t3.tzinfo, met)
2158 self.failUnless(t4.tzinfo is None)
2159 self.assertEqual(t5.tzinfo, utc)
2160
Tim Peters855fe882002-12-22 03:43:39 +00002161 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2162 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2163 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002164 self.failUnless(t4.utcoffset() is None)
2165 self.assertRaises(TypeError, t1.utcoffset, "no args")
2166
2167 self.assertEqual(t1.tzname(), "EST")
2168 self.assertEqual(t2.tzname(), "UTC")
2169 self.assertEqual(t3.tzname(), "MET")
2170 self.failUnless(t4.tzname() is None)
2171 self.assertRaises(TypeError, t1.tzname, "no args")
2172
Tim Peters855fe882002-12-22 03:43:39 +00002173 self.assertEqual(t1.dst(), timedelta(minutes=1))
2174 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2175 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002176 self.failUnless(t4.dst() is None)
2177 self.assertRaises(TypeError, t1.dst, "no args")
2178
2179 self.assertEqual(hash(t1), hash(t2))
2180 self.assertEqual(hash(t1), hash(t3))
2181 self.assertEqual(hash(t2), hash(t3))
2182
2183 self.assertEqual(t1, t2)
2184 self.assertEqual(t1, t3)
2185 self.assertEqual(t2, t3)
2186 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2187 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2188 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2189
2190 self.assertEqual(str(t1), "07:47:00-05:00")
2191 self.assertEqual(str(t2), "12:47:00+00:00")
2192 self.assertEqual(str(t3), "13:47:00+01:00")
2193 self.assertEqual(str(t4), "00:00:00.000040")
2194 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2195
2196 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2197 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2198 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2199 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2200 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2201
Tim Peters0bf60bd2003-01-08 20:40:01 +00002202 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002203 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2204 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2205 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2206 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2207 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2208
2209 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2210 "07:47:00 %Z=EST %z=-0500")
2211 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2212 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2213
2214 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002215 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002216 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2217 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2218
Tim Petersb92bb712002-12-21 17:44:07 +00002219 # Check that an invalid tzname result raises an exception.
2220 class Badtzname(tzinfo):
2221 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002222 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002223 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2224 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002225
2226 def test_hash_edge_cases(self):
2227 # Offsets that overflow a basic time.
2228 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2229 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2230 self.assertEqual(hash(t1), hash(t2))
2231
2232 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2233 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2234 self.assertEqual(hash(t1), hash(t2))
2235
Tim Peters2a799bf2002-12-16 20:18:38 +00002236 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002237 # Try one without a tzinfo.
2238 args = 20, 59, 16, 64**2
2239 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002240 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002241 green = pickler.dumps(orig, proto)
2242 derived = unpickler.loads(green)
2243 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002244
2245 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002246 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002247 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002248 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002249 green = pickler.dumps(orig, proto)
2250 derived = unpickler.loads(green)
2251 self.assertEqual(orig, derived)
2252 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2253 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2254 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002255
2256 def test_more_bool(self):
2257 # Test cases with non-None tzinfo.
2258 cls = self.theclass
2259
2260 t = cls(0, tzinfo=FixedOffset(-300, ""))
2261 self.failUnless(t)
2262
2263 t = cls(5, tzinfo=FixedOffset(-300, ""))
2264 self.failUnless(t)
2265
2266 t = cls(5, tzinfo=FixedOffset(300, ""))
2267 self.failUnless(not t)
2268
2269 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2270 self.failUnless(not t)
2271
2272 # Mostly ensuring this doesn't overflow internally.
2273 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2274 self.failUnless(t)
2275
2276 # But this should yield a value error -- the utcoffset is bogus.
2277 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2278 self.assertRaises(ValueError, lambda: bool(t))
2279
2280 # Likewise.
2281 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2282 self.assertRaises(ValueError, lambda: bool(t))
2283
Tim Peters12bf3392002-12-24 05:41:27 +00002284 def test_replace(self):
2285 cls = self.theclass
2286 z100 = FixedOffset(100, "+100")
2287 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2288 args = [1, 2, 3, 4, z100]
2289 base = cls(*args)
2290 self.assertEqual(base, base.replace())
2291
2292 i = 0
2293 for name, newval in (("hour", 5),
2294 ("minute", 6),
2295 ("second", 7),
2296 ("microsecond", 8),
2297 ("tzinfo", zm200)):
2298 newargs = args[:]
2299 newargs[i] = newval
2300 expected = cls(*newargs)
2301 got = base.replace(**{name: newval})
2302 self.assertEqual(expected, got)
2303 i += 1
2304
2305 # Ensure we can get rid of a tzinfo.
2306 self.assertEqual(base.tzname(), "+100")
2307 base2 = base.replace(tzinfo=None)
2308 self.failUnless(base2.tzinfo is None)
2309 self.failUnless(base2.tzname() is None)
2310
2311 # Ensure we can add one.
2312 base3 = base2.replace(tzinfo=z100)
2313 self.assertEqual(base, base3)
2314 self.failUnless(base.tzinfo is base3.tzinfo)
2315
2316 # Out of bounds.
2317 base = cls(1)
2318 self.assertRaises(ValueError, base.replace, hour=24)
2319 self.assertRaises(ValueError, base.replace, minute=-1)
2320 self.assertRaises(ValueError, base.replace, second=100)
2321 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2322
Tim Peters60c76e42002-12-27 00:41:11 +00002323 def test_mixed_compare(self):
2324 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002325 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002326 self.assertEqual(t1, t2)
2327 t2 = t2.replace(tzinfo=None)
2328 self.assertEqual(t1, t2)
2329 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2330 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002331 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2332 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002333
Tim Peters0bf60bd2003-01-08 20:40:01 +00002334 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002335 class Varies(tzinfo):
2336 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002337 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002338 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002339 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002340 return self.offset
2341
2342 v = Varies()
2343 t1 = t2.replace(tzinfo=v)
2344 t2 = t2.replace(tzinfo=v)
2345 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2346 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2347 self.assertEqual(t1, t2)
2348
2349 # But if they're not identical, it isn't ignored.
2350 t2 = t2.replace(tzinfo=Varies())
2351 self.failUnless(t1 < t2) # t1's offset counter still going up
2352
Tim Petersa98924a2003-05-17 05:55:19 +00002353 def test_subclass_timetz(self):
2354
2355 class C(self.theclass):
2356 theAnswer = 42
2357
2358 def __new__(cls, *args, **kws):
2359 temp = kws.copy()
2360 extra = temp.pop('extra')
2361 result = self.theclass.__new__(cls, *args, **temp)
2362 result.extra = extra
2363 return result
2364
2365 def newmeth(self, start):
2366 return start + self.hour + self.second
2367
2368 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2369
2370 dt1 = self.theclass(*args)
2371 dt2 = C(*args, **{'extra': 7})
2372
2373 self.assertEqual(dt2.__class__, C)
2374 self.assertEqual(dt2.theAnswer, 42)
2375 self.assertEqual(dt2.extra, 7)
2376 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2377 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2378
Tim Peters4c0db782002-12-26 05:01:19 +00002379
Tim Peters0bf60bd2003-01-08 20:40:01 +00002380# Testing datetime objects with a non-None tzinfo.
2381
Guido van Rossumd8faa362007-04-27 19:54:29 +00002382class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002383 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002384
2385 def test_trivial(self):
2386 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2387 self.assertEqual(dt.year, 1)
2388 self.assertEqual(dt.month, 2)
2389 self.assertEqual(dt.day, 3)
2390 self.assertEqual(dt.hour, 4)
2391 self.assertEqual(dt.minute, 5)
2392 self.assertEqual(dt.second, 6)
2393 self.assertEqual(dt.microsecond, 7)
2394 self.assertEqual(dt.tzinfo, None)
2395
2396 def test_even_more_compare(self):
2397 # The test_compare() and test_more_compare() inherited from TestDate
2398 # and TestDateTime covered non-tzinfo cases.
2399
2400 # Smallest possible after UTC adjustment.
2401 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2402 # Largest possible after UTC adjustment.
2403 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2404 tzinfo=FixedOffset(-1439, ""))
2405
2406 # Make sure those compare correctly, and w/o overflow.
2407 self.failUnless(t1 < t2)
2408 self.failUnless(t1 != t2)
2409 self.failUnless(t2 > t1)
2410
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002411 self.assertEqual(t1, t1)
2412 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002413
2414 # Equal afer adjustment.
2415 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2416 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2417 self.assertEqual(t1, t2)
2418
2419 # Change t1 not to subtract a minute, and t1 should be larger.
2420 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2421 self.failUnless(t1 > t2)
2422
2423 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2424 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2425 self.failUnless(t1 < t2)
2426
2427 # Back to the original t1, but make seconds resolve it.
2428 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2429 second=1)
2430 self.failUnless(t1 > t2)
2431
2432 # Likewise, but make microseconds resolve it.
2433 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2434 microsecond=1)
2435 self.failUnless(t1 > t2)
2436
2437 # Make t2 naive and it should fail.
2438 t2 = self.theclass.min
2439 self.assertRaises(TypeError, lambda: t1 == t2)
2440 self.assertEqual(t2, t2)
2441
2442 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2443 class Naive(tzinfo):
2444 def utcoffset(self, dt): return None
2445 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2446 self.assertRaises(TypeError, lambda: t1 == t2)
2447 self.assertEqual(t2, t2)
2448
2449 # OTOH, it's OK to compare two of these mixing the two ways of being
2450 # naive.
2451 t1 = self.theclass(5, 6, 7)
2452 self.assertEqual(t1, t2)
2453
2454 # Try a bogus uctoffset.
2455 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002456 def utcoffset(self, dt):
2457 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002458 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2459 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002460 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002461
Tim Peters2a799bf2002-12-16 20:18:38 +00002462 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002463 # Try one without a tzinfo.
2464 args = 6, 7, 23, 20, 59, 1, 64**2
2465 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002466 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002467 green = pickler.dumps(orig, proto)
2468 derived = unpickler.loads(green)
2469 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002470
2471 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002472 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002473 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002474 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002475 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002476 green = pickler.dumps(orig, proto)
2477 derived = unpickler.loads(green)
2478 self.assertEqual(orig, derived)
2479 self.failUnless(isinstance(derived.tzinfo,
2480 PicklableFixedOffset))
2481 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2482 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002483
2484 def test_extreme_hashes(self):
2485 # If an attempt is made to hash these via subtracting the offset
2486 # then hashing a datetime object, OverflowError results. The
2487 # Python implementation used to blow up here.
2488 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2489 hash(t)
2490 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2491 tzinfo=FixedOffset(-1439, ""))
2492 hash(t)
2493
2494 # OTOH, an OOB offset should blow up.
2495 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2496 self.assertRaises(ValueError, hash, t)
2497
2498 def test_zones(self):
2499 est = FixedOffset(-300, "EST")
2500 utc = FixedOffset(0, "UTC")
2501 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002502 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2503 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2504 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002505 self.assertEqual(t1.tzinfo, est)
2506 self.assertEqual(t2.tzinfo, utc)
2507 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002508 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2509 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2510 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002511 self.assertEqual(t1.tzname(), "EST")
2512 self.assertEqual(t2.tzname(), "UTC")
2513 self.assertEqual(t3.tzname(), "MET")
2514 self.assertEqual(hash(t1), hash(t2))
2515 self.assertEqual(hash(t1), hash(t3))
2516 self.assertEqual(hash(t2), hash(t3))
2517 self.assertEqual(t1, t2)
2518 self.assertEqual(t1, t3)
2519 self.assertEqual(t2, t3)
2520 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2521 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2522 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002523 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002524 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2525 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2526 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2527
2528 def test_combine(self):
2529 met = FixedOffset(60, "MET")
2530 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002531 tz = time(18, 45, 3, 1234, tzinfo=met)
2532 dt = datetime.combine(d, tz)
2533 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002534 tzinfo=met))
2535
2536 def test_extract(self):
2537 met = FixedOffset(60, "MET")
2538 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2539 self.assertEqual(dt.date(), date(2002, 3, 4))
2540 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002541 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002542
2543 def test_tz_aware_arithmetic(self):
2544 import random
2545
2546 now = self.theclass.now()
2547 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002548 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002549 nowaware = self.theclass.combine(now.date(), timeaware)
2550 self.failUnless(nowaware.tzinfo is tz55)
2551 self.assertEqual(nowaware.timetz(), timeaware)
2552
2553 # Can't mix aware and non-aware.
2554 self.assertRaises(TypeError, lambda: now - nowaware)
2555 self.assertRaises(TypeError, lambda: nowaware - now)
2556
Tim Peters0bf60bd2003-01-08 20:40:01 +00002557 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002558 self.assertRaises(TypeError, lambda: now + nowaware)
2559 self.assertRaises(TypeError, lambda: nowaware + now)
2560 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2561
2562 # Subtracting should yield 0.
2563 self.assertEqual(now - now, timedelta(0))
2564 self.assertEqual(nowaware - nowaware, timedelta(0))
2565
2566 # Adding a delta should preserve tzinfo.
2567 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2568 nowawareplus = nowaware + delta
2569 self.failUnless(nowaware.tzinfo is tz55)
2570 nowawareplus2 = delta + nowaware
2571 self.failUnless(nowawareplus2.tzinfo is tz55)
2572 self.assertEqual(nowawareplus, nowawareplus2)
2573
2574 # that - delta should be what we started with, and that - what we
2575 # started with should be delta.
2576 diff = nowawareplus - delta
2577 self.failUnless(diff.tzinfo is tz55)
2578 self.assertEqual(nowaware, diff)
2579 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2580 self.assertEqual(nowawareplus - nowaware, delta)
2581
2582 # Make up a random timezone.
2583 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002584 # Attach it to nowawareplus.
2585 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002586 self.failUnless(nowawareplus.tzinfo is tzr)
2587 # Make sure the difference takes the timezone adjustments into account.
2588 got = nowaware - nowawareplus
2589 # Expected: (nowaware base - nowaware offset) -
2590 # (nowawareplus base - nowawareplus offset) =
2591 # (nowaware base - nowawareplus base) +
2592 # (nowawareplus offset - nowaware offset) =
2593 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002594 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002595 self.assertEqual(got, expected)
2596
2597 # Try max possible difference.
2598 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2599 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2600 tzinfo=FixedOffset(-1439, "max"))
2601 maxdiff = max - min
2602 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2603 timedelta(minutes=2*1439))
2604
2605 def test_tzinfo_now(self):
2606 meth = self.theclass.now
2607 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2608 base = meth()
2609 # Try with and without naming the keyword.
2610 off42 = FixedOffset(42, "42")
2611 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002612 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002613 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002614 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002615 # Bad argument with and w/o naming the keyword.
2616 self.assertRaises(TypeError, meth, 16)
2617 self.assertRaises(TypeError, meth, tzinfo=16)
2618 # Bad keyword name.
2619 self.assertRaises(TypeError, meth, tinfo=off42)
2620 # Too many args.
2621 self.assertRaises(TypeError, meth, off42, off42)
2622
Tim Peters10cadce2003-01-23 19:58:02 +00002623 # We don't know which time zone we're in, and don't have a tzinfo
2624 # class to represent it, so seeing whether a tz argument actually
2625 # does a conversion is tricky.
2626 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2627 utc = FixedOffset(0, "utc", 0)
2628 for dummy in range(3):
2629 now = datetime.now(weirdtz)
2630 self.failUnless(now.tzinfo is weirdtz)
2631 utcnow = datetime.utcnow().replace(tzinfo=utc)
2632 now2 = utcnow.astimezone(weirdtz)
2633 if abs(now - now2) < timedelta(seconds=30):
2634 break
2635 # Else the code is broken, or more than 30 seconds passed between
2636 # calls; assuming the latter, just try again.
2637 else:
2638 # Three strikes and we're out.
2639 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2640
Tim Peters2a799bf2002-12-16 20:18:38 +00002641 def test_tzinfo_fromtimestamp(self):
2642 import time
2643 meth = self.theclass.fromtimestamp
2644 ts = time.time()
2645 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2646 base = meth(ts)
2647 # Try with and without naming the keyword.
2648 off42 = FixedOffset(42, "42")
2649 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002650 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002651 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002652 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002653 # Bad argument with and w/o naming the keyword.
2654 self.assertRaises(TypeError, meth, ts, 16)
2655 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2656 # Bad keyword name.
2657 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2658 # Too many args.
2659 self.assertRaises(TypeError, meth, ts, off42, off42)
2660 # Too few args.
2661 self.assertRaises(TypeError, meth)
2662
Tim Peters2a44a8d2003-01-23 20:53:10 +00002663 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002664 timestamp = 1000000000
2665 utcdatetime = datetime.utcfromtimestamp(timestamp)
2666 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2667 # But on some flavor of Mac, it's nowhere near that. So we can't have
2668 # any idea here what time that actually is, we can only test that
2669 # relative changes match.
2670 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2671 tz = FixedOffset(utcoffset, "tz", 0)
2672 expected = utcdatetime + utcoffset
2673 got = datetime.fromtimestamp(timestamp, tz)
2674 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002675
Tim Peters2a799bf2002-12-16 20:18:38 +00002676 def test_tzinfo_utcnow(self):
2677 meth = self.theclass.utcnow
2678 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2679 base = meth()
2680 # Try with and without naming the keyword; for whatever reason,
2681 # utcnow() doesn't accept a tzinfo argument.
2682 off42 = FixedOffset(42, "42")
2683 self.assertRaises(TypeError, meth, off42)
2684 self.assertRaises(TypeError, meth, tzinfo=off42)
2685
2686 def test_tzinfo_utcfromtimestamp(self):
2687 import time
2688 meth = self.theclass.utcfromtimestamp
2689 ts = time.time()
2690 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2691 base = meth(ts)
2692 # Try with and without naming the keyword; for whatever reason,
2693 # utcfromtimestamp() doesn't accept a tzinfo argument.
2694 off42 = FixedOffset(42, "42")
2695 self.assertRaises(TypeError, meth, ts, off42)
2696 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2697
2698 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002699 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002700 # DST flag.
2701 class DST(tzinfo):
2702 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002703 if isinstance(dstvalue, int):
2704 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002705 self.dstvalue = dstvalue
2706 def dst(self, dt):
2707 return self.dstvalue
2708
2709 cls = self.theclass
2710 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2711 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2712 t = d.timetuple()
2713 self.assertEqual(1, t.tm_year)
2714 self.assertEqual(1, t.tm_mon)
2715 self.assertEqual(1, t.tm_mday)
2716 self.assertEqual(10, t.tm_hour)
2717 self.assertEqual(20, t.tm_min)
2718 self.assertEqual(30, t.tm_sec)
2719 self.assertEqual(0, t.tm_wday)
2720 self.assertEqual(1, t.tm_yday)
2721 self.assertEqual(flag, t.tm_isdst)
2722
2723 # dst() returns wrong type.
2724 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2725
2726 # dst() at the edge.
2727 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2728 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2729
2730 # dst() out of range.
2731 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2732 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2733
2734 def test_utctimetuple(self):
2735 class DST(tzinfo):
2736 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002737 if isinstance(dstvalue, int):
2738 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002739 self.dstvalue = dstvalue
2740 def dst(self, dt):
2741 return self.dstvalue
2742
2743 cls = self.theclass
2744 # This can't work: DST didn't implement utcoffset.
2745 self.assertRaises(NotImplementedError,
2746 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2747
2748 class UOFS(DST):
2749 def __init__(self, uofs, dofs=None):
2750 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002751 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002752 def utcoffset(self, dt):
2753 return self.uofs
2754
2755 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2756 # in effect for a UTC time.
2757 for dstvalue in -33, 33, 0, None:
2758 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2759 t = d.utctimetuple()
2760 self.assertEqual(d.year, t.tm_year)
2761 self.assertEqual(d.month, t.tm_mon)
2762 self.assertEqual(d.day, t.tm_mday)
2763 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2764 self.assertEqual(13, t.tm_min)
2765 self.assertEqual(d.second, t.tm_sec)
2766 self.assertEqual(d.weekday(), t.tm_wday)
2767 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2768 t.tm_yday)
2769 self.assertEqual(0, t.tm_isdst)
2770
2771 # At the edges, UTC adjustment can normalize into years out-of-range
2772 # for a datetime object. Ensure that a correct timetuple is
2773 # created anyway.
2774 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2775 # That goes back 1 minute less than a full day.
2776 t = tiny.utctimetuple()
2777 self.assertEqual(t.tm_year, MINYEAR-1)
2778 self.assertEqual(t.tm_mon, 12)
2779 self.assertEqual(t.tm_mday, 31)
2780 self.assertEqual(t.tm_hour, 0)
2781 self.assertEqual(t.tm_min, 1)
2782 self.assertEqual(t.tm_sec, 37)
2783 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2784 self.assertEqual(t.tm_isdst, 0)
2785
2786 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2787 # That goes forward 1 minute less than a full day.
2788 t = huge.utctimetuple()
2789 self.assertEqual(t.tm_year, MAXYEAR+1)
2790 self.assertEqual(t.tm_mon, 1)
2791 self.assertEqual(t.tm_mday, 1)
2792 self.assertEqual(t.tm_hour, 23)
2793 self.assertEqual(t.tm_min, 58)
2794 self.assertEqual(t.tm_sec, 37)
2795 self.assertEqual(t.tm_yday, 1)
2796 self.assertEqual(t.tm_isdst, 0)
2797
2798 def test_tzinfo_isoformat(self):
2799 zero = FixedOffset(0, "+00:00")
2800 plus = FixedOffset(220, "+03:40")
2801 minus = FixedOffset(-231, "-03:51")
2802 unknown = FixedOffset(None, "")
2803
2804 cls = self.theclass
2805 datestr = '0001-02-03'
2806 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002807 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002808 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2809 timestr = '04:05:59' + (us and '.987001' or '')
2810 ofsstr = ofs is not None and d.tzname() or ''
2811 tailstr = timestr + ofsstr
2812 iso = d.isoformat()
2813 self.assertEqual(iso, datestr + 'T' + tailstr)
2814 self.assertEqual(iso, d.isoformat('T'))
2815 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002816 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002817 self.assertEqual(str(d), datestr + ' ' + tailstr)
2818
Tim Peters12bf3392002-12-24 05:41:27 +00002819 def test_replace(self):
2820 cls = self.theclass
2821 z100 = FixedOffset(100, "+100")
2822 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2823 args = [1, 2, 3, 4, 5, 6, 7, z100]
2824 base = cls(*args)
2825 self.assertEqual(base, base.replace())
2826
2827 i = 0
2828 for name, newval in (("year", 2),
2829 ("month", 3),
2830 ("day", 4),
2831 ("hour", 5),
2832 ("minute", 6),
2833 ("second", 7),
2834 ("microsecond", 8),
2835 ("tzinfo", zm200)):
2836 newargs = args[:]
2837 newargs[i] = newval
2838 expected = cls(*newargs)
2839 got = base.replace(**{name: newval})
2840 self.assertEqual(expected, got)
2841 i += 1
2842
2843 # Ensure we can get rid of a tzinfo.
2844 self.assertEqual(base.tzname(), "+100")
2845 base2 = base.replace(tzinfo=None)
2846 self.failUnless(base2.tzinfo is None)
2847 self.failUnless(base2.tzname() is None)
2848
2849 # Ensure we can add one.
2850 base3 = base2.replace(tzinfo=z100)
2851 self.assertEqual(base, base3)
2852 self.failUnless(base.tzinfo is base3.tzinfo)
2853
2854 # Out of bounds.
2855 base = cls(2000, 2, 29)
2856 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002857
Tim Peters80475bb2002-12-25 07:40:55 +00002858 def test_more_astimezone(self):
2859 # The inherited test_astimezone covered some trivial and error cases.
2860 fnone = FixedOffset(None, "None")
2861 f44m = FixedOffset(44, "44")
2862 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2863
Tim Peters10cadce2003-01-23 19:58:02 +00002864 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002865 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002866 # Replacing with degenerate tzinfo raises an exception.
2867 self.assertRaises(ValueError, dt.astimezone, fnone)
2868 # Ditto with None tz.
2869 self.assertRaises(TypeError, dt.astimezone, None)
2870 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002871 x = dt.astimezone(dt.tzinfo)
2872 self.failUnless(x.tzinfo is f44m)
2873 self.assertEqual(x.date(), dt.date())
2874 self.assertEqual(x.time(), dt.time())
2875
2876 # Replacing with different tzinfo does adjust.
2877 got = dt.astimezone(fm5h)
2878 self.failUnless(got.tzinfo is fm5h)
2879 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2880 expected = dt - dt.utcoffset() # in effect, convert to UTC
2881 expected += fm5h.utcoffset(dt) # and from there to local time
2882 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2883 self.assertEqual(got.date(), expected.date())
2884 self.assertEqual(got.time(), expected.time())
2885 self.assertEqual(got.timetz(), expected.timetz())
2886 self.failUnless(got.tzinfo is expected.tzinfo)
2887 self.assertEqual(got, expected)
2888
Tim Peters4c0db782002-12-26 05:01:19 +00002889 def test_aware_subtract(self):
2890 cls = self.theclass
2891
Tim Peters60c76e42002-12-27 00:41:11 +00002892 # Ensure that utcoffset() is ignored when the operands have the
2893 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002894 class OperandDependentOffset(tzinfo):
2895 def utcoffset(self, t):
2896 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002897 # d0 and d1 equal after adjustment
2898 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002899 else:
Tim Peters397301e2003-01-02 21:28:08 +00002900 # d2 off in the weeds
2901 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002902
2903 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2904 d0 = base.replace(minute=3)
2905 d1 = base.replace(minute=9)
2906 d2 = base.replace(minute=11)
2907 for x in d0, d1, d2:
2908 for y in d0, d1, d2:
2909 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002910 expected = timedelta(minutes=x.minute - y.minute)
2911 self.assertEqual(got, expected)
2912
2913 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2914 # ignored.
2915 base = cls(8, 9, 10, 11, 12, 13, 14)
2916 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2917 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2918 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2919 for x in d0, d1, d2:
2920 for y in d0, d1, d2:
2921 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002922 if (x is d0 or x is d1) and (y is d0 or y is d1):
2923 expected = timedelta(0)
2924 elif x is y is d2:
2925 expected = timedelta(0)
2926 elif x is d2:
2927 expected = timedelta(minutes=(11-59)-0)
2928 else:
2929 assert y is d2
2930 expected = timedelta(minutes=0-(11-59))
2931 self.assertEqual(got, expected)
2932
Tim Peters60c76e42002-12-27 00:41:11 +00002933 def test_mixed_compare(self):
2934 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002935 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002936 self.assertEqual(t1, t2)
2937 t2 = t2.replace(tzinfo=None)
2938 self.assertEqual(t1, t2)
2939 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2940 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002941 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2942 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002943
Tim Peters0bf60bd2003-01-08 20:40:01 +00002944 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002945 class Varies(tzinfo):
2946 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002947 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002948 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002949 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002950 return self.offset
2951
2952 v = Varies()
2953 t1 = t2.replace(tzinfo=v)
2954 t2 = t2.replace(tzinfo=v)
2955 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2956 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2957 self.assertEqual(t1, t2)
2958
2959 # But if they're not identical, it isn't ignored.
2960 t2 = t2.replace(tzinfo=Varies())
2961 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002962
Tim Petersa98924a2003-05-17 05:55:19 +00002963 def test_subclass_datetimetz(self):
2964
2965 class C(self.theclass):
2966 theAnswer = 42
2967
2968 def __new__(cls, *args, **kws):
2969 temp = kws.copy()
2970 extra = temp.pop('extra')
2971 result = self.theclass.__new__(cls, *args, **temp)
2972 result.extra = extra
2973 return result
2974
2975 def newmeth(self, start):
2976 return start + self.hour + self.year
2977
2978 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2979
2980 dt1 = self.theclass(*args)
2981 dt2 = C(*args, **{'extra': 7})
2982
2983 self.assertEqual(dt2.__class__, C)
2984 self.assertEqual(dt2.theAnswer, 42)
2985 self.assertEqual(dt2.extra, 7)
2986 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2987 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2988
Tim Peters621818b2002-12-29 23:44:49 +00002989# Pain to set up DST-aware tzinfo classes.
2990
2991def first_sunday_on_or_after(dt):
2992 days_to_go = 6 - dt.weekday()
2993 if days_to_go:
2994 dt += timedelta(days_to_go)
2995 return dt
2996
2997ZERO = timedelta(0)
2998HOUR = timedelta(hours=1)
2999DAY = timedelta(days=1)
3000# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3001DSTSTART = datetime(1, 4, 1, 2)
3002# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003003# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3004# being standard time on that day, there is no spelling in local time of
3005# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3006DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003007
3008class USTimeZone(tzinfo):
3009
3010 def __init__(self, hours, reprname, stdname, dstname):
3011 self.stdoffset = timedelta(hours=hours)
3012 self.reprname = reprname
3013 self.stdname = stdname
3014 self.dstname = dstname
3015
3016 def __repr__(self):
3017 return self.reprname
3018
3019 def tzname(self, dt):
3020 if self.dst(dt):
3021 return self.dstname
3022 else:
3023 return self.stdname
3024
3025 def utcoffset(self, dt):
3026 return self.stdoffset + self.dst(dt)
3027
3028 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003029 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003030 # An exception instead may be sensible here, in one or more of
3031 # the cases.
3032 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003033 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003034
3035 # Find first Sunday in April.
3036 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3037 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3038
3039 # Find last Sunday in October.
3040 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3041 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3042
Tim Peters621818b2002-12-29 23:44:49 +00003043 # Can't compare naive to aware objects, so strip the timezone from
3044 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003045 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003046 return HOUR
3047 else:
3048 return ZERO
3049
Tim Peters521fc152002-12-31 17:36:56 +00003050Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3051Central = USTimeZone(-6, "Central", "CST", "CDT")
3052Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3053Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003054utc_real = FixedOffset(0, "UTC", 0)
3055# For better test coverage, we want another flavor of UTC that's west of
3056# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003057utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003058
3059class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003060 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003061 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003062 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003063
Tim Peters0bf60bd2003-01-08 20:40:01 +00003064 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003065
Tim Peters521fc152002-12-31 17:36:56 +00003066 # Check a time that's inside DST.
3067 def checkinside(self, dt, tz, utc, dston, dstoff):
3068 self.assertEqual(dt.dst(), HOUR)
3069
3070 # Conversion to our own timezone is always an identity.
3071 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003072
3073 asutc = dt.astimezone(utc)
3074 there_and_back = asutc.astimezone(tz)
3075
3076 # Conversion to UTC and back isn't always an identity here,
3077 # because there are redundant spellings (in local time) of
3078 # UTC time when DST begins: the clock jumps from 1:59:59
3079 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3080 # make sense then. The classes above treat 2:MM:SS as
3081 # daylight time then (it's "after 2am"), really an alias
3082 # for 1:MM:SS standard time. The latter form is what
3083 # conversion back from UTC produces.
3084 if dt.date() == dston.date() and dt.hour == 2:
3085 # We're in the redundant hour, and coming back from
3086 # UTC gives the 1:MM:SS standard-time spelling.
3087 self.assertEqual(there_and_back + HOUR, dt)
3088 # Although during was considered to be in daylight
3089 # time, there_and_back is not.
3090 self.assertEqual(there_and_back.dst(), ZERO)
3091 # They're the same times in UTC.
3092 self.assertEqual(there_and_back.astimezone(utc),
3093 dt.astimezone(utc))
3094 else:
3095 # We're not in the redundant hour.
3096 self.assertEqual(dt, there_and_back)
3097
Tim Peters327098a2003-01-20 22:54:38 +00003098 # Because we have a redundant spelling when DST begins, there is
3099 # (unforunately) an hour when DST ends that can't be spelled at all in
3100 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3101 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3102 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3103 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3104 # expressed in local time. Nevertheless, we want conversion back
3105 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003106 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003107 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003108 if dt.date() == dstoff.date() and dt.hour == 0:
3109 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003110 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003111 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3112 nexthour_utc += HOUR
3113 nexthour_tz = nexthour_utc.astimezone(tz)
3114 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003115 else:
Tim Peters327098a2003-01-20 22:54:38 +00003116 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003117
3118 # Check a time that's outside DST.
3119 def checkoutside(self, dt, tz, utc):
3120 self.assertEqual(dt.dst(), ZERO)
3121
3122 # Conversion to our own timezone is always an identity.
3123 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003124
3125 # Converting to UTC and back is an identity too.
3126 asutc = dt.astimezone(utc)
3127 there_and_back = asutc.astimezone(tz)
3128 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003129
Tim Peters1024bf82002-12-30 17:09:40 +00003130 def convert_between_tz_and_utc(self, tz, utc):
3131 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003132 # Because 1:MM on the day DST ends is taken as being standard time,
3133 # there is no spelling in tz for the last hour of daylight time.
3134 # For purposes of the test, the last hour of DST is 0:MM, which is
3135 # taken as being daylight time (and 1:MM is taken as being standard
3136 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003137 dstoff = self.dstoff.replace(tzinfo=tz)
3138 for delta in (timedelta(weeks=13),
3139 DAY,
3140 HOUR,
3141 timedelta(minutes=1),
3142 timedelta(microseconds=1)):
3143
Tim Peters521fc152002-12-31 17:36:56 +00003144 self.checkinside(dston, tz, utc, dston, dstoff)
3145 for during in dston + delta, dstoff - delta:
3146 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003147
Tim Peters521fc152002-12-31 17:36:56 +00003148 self.checkoutside(dstoff, tz, utc)
3149 for outside in dston - delta, dstoff + delta:
3150 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003151
Tim Peters621818b2002-12-29 23:44:49 +00003152 def test_easy(self):
3153 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003154 self.convert_between_tz_and_utc(Eastern, utc_real)
3155 self.convert_between_tz_and_utc(Pacific, utc_real)
3156 self.convert_between_tz_and_utc(Eastern, utc_fake)
3157 self.convert_between_tz_and_utc(Pacific, utc_fake)
3158 # The next is really dancing near the edge. It works because
3159 # Pacific and Eastern are far enough apart that their "problem
3160 # hours" don't overlap.
3161 self.convert_between_tz_and_utc(Eastern, Pacific)
3162 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003163 # OTOH, these fail! Don't enable them. The difficulty is that
3164 # the edge case tests assume that every hour is representable in
3165 # the "utc" class. This is always true for a fixed-offset tzinfo
3166 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3167 # For these adjacent DST-aware time zones, the range of time offsets
3168 # tested ends up creating hours in the one that aren't representable
3169 # in the other. For the same reason, we would see failures in the
3170 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3171 # offset deltas in convert_between_tz_and_utc().
3172 #
3173 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3174 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003175
Tim Petersf3615152003-01-01 21:51:37 +00003176 def test_tricky(self):
3177 # 22:00 on day before daylight starts.
3178 fourback = self.dston - timedelta(hours=4)
3179 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003180 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003181 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3182 # 2", we should get the 3 spelling.
3183 # If we plug 22:00 the day before into Eastern, it "looks like std
3184 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3185 # to 22:00 lands on 2:00, which makes no sense in local time (the
3186 # local clock jumps from 1 to 3). The point here is to make sure we
3187 # get the 3 spelling.
3188 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003189 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003190 self.assertEqual(expected, got)
3191
3192 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3193 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003194 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003195 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3196 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3197 # spelling.
3198 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003199 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003200 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003201
Tim Petersadf64202003-01-04 06:03:15 +00003202 # Now on the day DST ends, we want "repeat an hour" behavior.
3203 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3204 # EST 23:MM 0:MM 1:MM 2:MM
3205 # EDT 0:MM 1:MM 2:MM 3:MM
3206 # wall 0:MM 1:MM 1:MM 2:MM against these
3207 for utc in utc_real, utc_fake:
3208 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003209 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003210 # Convert that to UTC.
3211 first_std_hour -= tz.utcoffset(None)
3212 # Adjust for possibly fake UTC.
3213 asutc = first_std_hour + utc.utcoffset(None)
3214 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3215 # tz=Eastern.
3216 asutcbase = asutc.replace(tzinfo=utc)
3217 for tzhour in (0, 1, 1, 2):
3218 expectedbase = self.dstoff.replace(hour=tzhour)
3219 for minute in 0, 30, 59:
3220 expected = expectedbase.replace(minute=minute)
3221 asutc = asutcbase.replace(minute=minute)
3222 astz = asutc.astimezone(tz)
3223 self.assertEqual(astz.replace(tzinfo=None), expected)
3224 asutcbase += HOUR
3225
3226
Tim Peters710fb152003-01-02 19:35:54 +00003227 def test_bogus_dst(self):
3228 class ok(tzinfo):
3229 def utcoffset(self, dt): return HOUR
3230 def dst(self, dt): return HOUR
3231
3232 now = self.theclass.now().replace(tzinfo=utc_real)
3233 # Doesn't blow up.
3234 now.astimezone(ok())
3235
3236 # Does blow up.
3237 class notok(ok):
3238 def dst(self, dt): return None
3239 self.assertRaises(ValueError, now.astimezone, notok())
3240
Tim Peters52dcce22003-01-23 16:36:11 +00003241 def test_fromutc(self):
3242 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3243 now = datetime.utcnow().replace(tzinfo=utc_real)
3244 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3245 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3246 enow = Eastern.fromutc(now) # doesn't blow up
3247 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3248 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3249 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3250
3251 # Always converts UTC to standard time.
3252 class FauxUSTimeZone(USTimeZone):
3253 def fromutc(self, dt):
3254 return dt + self.stdoffset
3255 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3256
3257 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3258 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3259 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3260
3261 # Check around DST start.
3262 start = self.dston.replace(hour=4, tzinfo=Eastern)
3263 fstart = start.replace(tzinfo=FEastern)
3264 for wall in 23, 0, 1, 3, 4, 5:
3265 expected = start.replace(hour=wall)
3266 if wall == 23:
3267 expected -= timedelta(days=1)
3268 got = Eastern.fromutc(start)
3269 self.assertEqual(expected, got)
3270
3271 expected = fstart + FEastern.stdoffset
3272 got = FEastern.fromutc(fstart)
3273 self.assertEqual(expected, got)
3274
3275 # Ensure astimezone() calls fromutc() too.
3276 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3277 self.assertEqual(expected, got)
3278
3279 start += HOUR
3280 fstart += HOUR
3281
3282 # Check around DST end.
3283 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3284 fstart = start.replace(tzinfo=FEastern)
3285 for wall in 0, 1, 1, 2, 3, 4:
3286 expected = start.replace(hour=wall)
3287 got = Eastern.fromutc(start)
3288 self.assertEqual(expected, got)
3289
3290 expected = fstart + FEastern.stdoffset
3291 got = FEastern.fromutc(fstart)
3292 self.assertEqual(expected, got)
3293
3294 # Ensure astimezone() calls fromutc() too.
3295 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3296 self.assertEqual(expected, got)
3297
3298 start += HOUR
3299 fstart += HOUR
3300
Tim Peters710fb152003-01-02 19:35:54 +00003301
Tim Peters528ca532004-09-16 01:30:50 +00003302#############################################################################
3303# oddballs
3304
3305class Oddballs(unittest.TestCase):
3306
3307 def test_bug_1028306(self):
3308 # Trying to compare a date to a datetime should act like a mixed-
3309 # type comparison, despite that datetime is a subclass of date.
3310 as_date = date.today()
3311 as_datetime = datetime.combine(as_date, time())
3312 self.assert_(as_date != as_datetime)
3313 self.assert_(as_datetime != as_date)
3314 self.assert_(not as_date == as_datetime)
3315 self.assert_(not 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 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3323 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3324
3325 # Neverthelss, comparison should work with the base-class (date)
3326 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003327 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003328 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003329 as_different = as_datetime.replace(day= different_day)
3330 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003331
3332 # And date should compare with other subclasses of date. If a
3333 # subclass wants to stop this, it's up to the subclass to do so.
3334 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3335 self.assertEqual(as_date, date_sc)
3336 self.assertEqual(date_sc, as_date)
3337
3338 # Ditto for datetimes.
3339 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3340 as_date.day, 0, 0, 0)
3341 self.assertEqual(as_datetime, datetime_sc)
3342 self.assertEqual(datetime_sc, as_datetime)
3343
Tim Peters2a799bf2002-12-16 20:18:38 +00003344def test_main():
Guido van Rossumd8faa362007-04-27 19:54:29 +00003345 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003346
3347if __name__ == "__main__":
3348 test_main()