blob: 89fa5c8e697b36254d6e41ee2d62bdd7cc07e6d5 [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 Rossumf1200f82007-03-07 15:16:29 +00006import os
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
8import cPickle
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
Tim Peters35ad6412003-02-05 04:08:07 +000019pickle_choices = [(pickler, unpickler, proto)
20 for pickler in pickle, cPickle
21 for unpickler in pickle, cPickle
22 for proto in range(3)]
23assert len(pickle_choices) == 2*2*3
Guido van Rossum177e41a2003-01-30 22:06:23 +000024
Tim Peters68124bb2003-02-08 03:46:31 +000025# An arbitrary collection of objects of non-datetime types, for testing
26# mixed-type comparisons.
27OTHERSTUFF = (10, 10L, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000028
Tim Peters2a799bf2002-12-16 20:18:38 +000029
30#############################################################################
31# module tests
32
33class TestModule(unittest.TestCase):
34
35 def test_constants(self):
36 import datetime
37 self.assertEqual(datetime.MINYEAR, 1)
38 self.assertEqual(datetime.MAXYEAR, 9999)
39
40#############################################################################
41# tzinfo tests
42
43class FixedOffset(tzinfo):
44 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000045 if isinstance(offset, int):
46 offset = timedelta(minutes=offset)
47 if isinstance(dstoffset, int):
48 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000049 self.__offset = offset
50 self.__name = name
51 self.__dstoffset = dstoffset
52 def __repr__(self):
53 return self.__name.lower()
54 def utcoffset(self, dt):
55 return self.__offset
56 def tzname(self, dt):
57 return self.__name
58 def dst(self, dt):
59 return self.__dstoffset
60
Tim Petersfb8472c2002-12-21 05:04:42 +000061class PicklableFixedOffset(FixedOffset):
62 def __init__(self, offset=None, name=None, dstoffset=None):
63 FixedOffset.__init__(self, offset, name, dstoffset)
64
Tim Peters2a799bf2002-12-16 20:18:38 +000065class TestTZInfo(unittest.TestCase):
66
67 def test_non_abstractness(self):
68 # In order to allow subclasses to get pickled, the C implementation
69 # wasn't able to get away with having __init__ raise
70 # NotImplementedError.
71 useless = tzinfo()
72 dt = datetime.max
73 self.assertRaises(NotImplementedError, useless.tzname, dt)
74 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
75 self.assertRaises(NotImplementedError, useless.dst, dt)
76
77 def test_subclass_must_override(self):
78 class NotEnough(tzinfo):
79 def __init__(self, offset, name):
80 self.__offset = offset
81 self.__name = name
82 self.failUnless(issubclass(NotEnough, tzinfo))
83 ne = NotEnough(3, "NotByALongShot")
84 self.failUnless(isinstance(ne, tzinfo))
85
86 dt = datetime.now()
87 self.assertRaises(NotImplementedError, ne.tzname, dt)
88 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
89 self.assertRaises(NotImplementedError, ne.dst, dt)
90
91 def test_normal(self):
92 fo = FixedOffset(3, "Three")
93 self.failUnless(isinstance(fo, tzinfo))
94 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +000095 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +000096 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +000097 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +000098
99 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000100 # There's no point to pickling tzinfo objects on their own (they
101 # carry no data), but they need to be picklable anyway else
102 # concrete subclasses can't be pickled.
103 orig = tzinfo.__new__(tzinfo)
104 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000105 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000106 green = pickler.dumps(orig, proto)
107 derived = unpickler.loads(green)
108 self.failUnless(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000109
110 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000111 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000112 offset = timedelta(minutes=-300)
113 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000114 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000115 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000116 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000117 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000118 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000119 green = pickler.dumps(orig, proto)
120 derived = unpickler.loads(green)
121 self.failUnless(isinstance(derived, tzinfo))
122 self.failUnless(type(derived) is PicklableFixedOffset)
123 self.assertEqual(derived.utcoffset(None), offset)
124 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000125
126#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000127# Base clase for testing a particular aspect of timedelta, time, date and
128# datetime comparisons.
129
Collin Winterc2898c52007-04-25 17:29:52 +0000130class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000131 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
132
133 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
134 # legit constructor.
135
136 def test_harmless_mixed_comparison(self):
137 me = self.theclass(1, 1, 1)
138
139 self.failIf(me == ())
140 self.failUnless(me != ())
141 self.failIf(() == me)
142 self.failUnless(() != me)
143
144 self.failUnless(me in [1, 20L, [], me])
145 self.failIf(me not in [1, 20L, [], me])
146
147 self.failUnless([] in [me, 1, 20L, []])
148 self.failIf([] not in [me, 1, 20L, []])
149
150 def test_harmful_mixed_comparison(self):
151 me = self.theclass(1, 1, 1)
152
153 self.assertRaises(TypeError, lambda: me < ())
154 self.assertRaises(TypeError, lambda: me <= ())
155 self.assertRaises(TypeError, lambda: me > ())
156 self.assertRaises(TypeError, lambda: me >= ())
157
158 self.assertRaises(TypeError, lambda: () < me)
159 self.assertRaises(TypeError, lambda: () <= me)
160 self.assertRaises(TypeError, lambda: () > me)
161 self.assertRaises(TypeError, lambda: () >= me)
162
163 self.assertRaises(TypeError, cmp, (), me)
164 self.assertRaises(TypeError, cmp, me, ())
165
166#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000167# timedelta tests
168
Collin Winterc2898c52007-04-25 17:29:52 +0000169class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000170
171 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000172
173 def test_constructor(self):
174 eq = self.assertEqual
175 td = timedelta
176
177 # Check keyword args to constructor
178 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
179 milliseconds=0, microseconds=0))
180 eq(td(1), td(days=1))
181 eq(td(0, 1), td(seconds=1))
182 eq(td(0, 0, 1), td(microseconds=1))
183 eq(td(weeks=1), td(days=7))
184 eq(td(days=1), td(hours=24))
185 eq(td(hours=1), td(minutes=60))
186 eq(td(minutes=1), td(seconds=60))
187 eq(td(seconds=1), td(milliseconds=1000))
188 eq(td(milliseconds=1), td(microseconds=1000))
189
190 # Check float args to constructor
191 eq(td(weeks=1.0/7), td(days=1))
192 eq(td(days=1.0/24), td(hours=1))
193 eq(td(hours=1.0/60), td(minutes=1))
194 eq(td(minutes=1.0/60), td(seconds=1))
195 eq(td(seconds=0.001), td(milliseconds=1))
196 eq(td(milliseconds=0.001), td(microseconds=1))
197
198 def test_computations(self):
199 eq = self.assertEqual
200 td = timedelta
201
202 a = td(7) # One week
203 b = td(0, 60) # One minute
204 c = td(0, 0, 1000) # One millisecond
205 eq(a+b+c, td(7, 60, 1000))
206 eq(a-b, td(6, 24*3600 - 60))
207 eq(-a, td(-7))
208 eq(+a, td(7))
209 eq(-b, td(-1, 24*3600 - 60))
210 eq(-c, td(-1, 24*3600 - 1, 999000))
211 eq(abs(a), a)
212 eq(abs(-a), a)
213 eq(td(6, 24*3600), a)
214 eq(td(0, 0, 60*1000000), b)
215 eq(a*10, td(70))
216 eq(a*10, 10*a)
217 eq(a*10L, 10*a)
218 eq(b*10, td(0, 600))
219 eq(10*b, td(0, 600))
220 eq(b*10L, td(0, 600))
221 eq(c*10, td(0, 0, 10000))
222 eq(10*c, td(0, 0, 10000))
223 eq(c*10L, td(0, 0, 10000))
224 eq(a*-1, -a)
225 eq(b*-2, -b-b)
226 eq(c*-2, -c+-c)
227 eq(b*(60*24), (b*60)*24)
228 eq(b*(60*24), (60*b)*24)
229 eq(c*1000, td(0, 1))
230 eq(1000*c, td(0, 1))
231 eq(a//7, td(1))
232 eq(b//10, td(0, 6))
233 eq(c//1000, td(0, 0, 1))
234 eq(a//10, td(0, 7*24*360))
235 eq(a//3600000, td(0, 0, 7*24*1000))
236
237 def test_disallowed_computations(self):
238 a = timedelta(42)
239
240 # Add/sub ints, longs, floats should be illegal
241 for i in 1, 1L, 1.0:
242 self.assertRaises(TypeError, lambda: a+i)
243 self.assertRaises(TypeError, lambda: a-i)
244 self.assertRaises(TypeError, lambda: i+a)
245 self.assertRaises(TypeError, lambda: i-a)
246
247 # Mul/div by float isn't supported.
248 x = 2.3
249 self.assertRaises(TypeError, lambda: a*x)
250 self.assertRaises(TypeError, lambda: x*a)
251 self.assertRaises(TypeError, lambda: a/x)
252 self.assertRaises(TypeError, lambda: x/a)
253 self.assertRaises(TypeError, lambda: a // x)
254 self.assertRaises(TypeError, lambda: x // a)
255
Andrew M. Kuchling081bb452008-10-03 16:42:52 +0000256 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000257 # Division by zero doesn't make sense.
258 for zero in 0, 0L:
259 self.assertRaises(TypeError, lambda: zero // a)
260 self.assertRaises(ZeroDivisionError, lambda: a // zero)
261
262 def test_basic_attributes(self):
263 days, seconds, us = 1, 7, 31
264 td = timedelta(days, seconds, us)
265 self.assertEqual(td.days, days)
266 self.assertEqual(td.seconds, seconds)
267 self.assertEqual(td.microseconds, us)
268
269 def test_carries(self):
270 t1 = timedelta(days=100,
271 weeks=-7,
272 hours=-24*(100-49),
273 minutes=-3,
274 seconds=12,
275 microseconds=(3*60 - 12) * 1e6 + 1)
276 t2 = timedelta(microseconds=1)
277 self.assertEqual(t1, t2)
278
279 def test_hash_equality(self):
280 t1 = timedelta(days=100,
281 weeks=-7,
282 hours=-24*(100-49),
283 minutes=-3,
284 seconds=12,
285 microseconds=(3*60 - 12) * 1000000)
286 t2 = timedelta()
287 self.assertEqual(hash(t1), hash(t2))
288
289 t1 += timedelta(weeks=7)
290 t2 += timedelta(days=7*7)
291 self.assertEqual(t1, t2)
292 self.assertEqual(hash(t1), hash(t2))
293
294 d = {t1: 1}
295 d[t2] = 2
296 self.assertEqual(len(d), 1)
297 self.assertEqual(d[t1], 2)
298
299 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000300 args = 12, 34, 56
301 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000302 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000303 green = pickler.dumps(orig, proto)
304 derived = unpickler.loads(green)
305 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000306
307 def test_compare(self):
308 t1 = timedelta(2, 3, 4)
309 t2 = timedelta(2, 3, 4)
310 self.failUnless(t1 == t2)
311 self.failUnless(t1 <= t2)
312 self.failUnless(t1 >= t2)
313 self.failUnless(not t1 != t2)
314 self.failUnless(not t1 < t2)
315 self.failUnless(not t1 > t2)
316 self.assertEqual(cmp(t1, t2), 0)
317 self.assertEqual(cmp(t2, t1), 0)
318
319 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
320 t2 = timedelta(*args) # this is larger than t1
321 self.failUnless(t1 < t2)
322 self.failUnless(t2 > t1)
323 self.failUnless(t1 <= t2)
324 self.failUnless(t2 >= t1)
325 self.failUnless(t1 != t2)
326 self.failUnless(t2 != t1)
327 self.failUnless(not t1 == t2)
328 self.failUnless(not t2 == t1)
329 self.failUnless(not t1 > t2)
330 self.failUnless(not t2 < t1)
331 self.failUnless(not t1 >= t2)
332 self.failUnless(not t2 <= t1)
333 self.assertEqual(cmp(t1, t2), -1)
334 self.assertEqual(cmp(t2, t1), 1)
335
Tim Peters68124bb2003-02-08 03:46:31 +0000336 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000337 self.assertEqual(t1 == badarg, False)
338 self.assertEqual(t1 != badarg, True)
339 self.assertEqual(badarg == t1, False)
340 self.assertEqual(badarg != t1, True)
341
Tim Peters2a799bf2002-12-16 20:18:38 +0000342 self.assertRaises(TypeError, lambda: t1 <= badarg)
343 self.assertRaises(TypeError, lambda: t1 < badarg)
344 self.assertRaises(TypeError, lambda: t1 > badarg)
345 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000346 self.assertRaises(TypeError, lambda: badarg <= t1)
347 self.assertRaises(TypeError, lambda: badarg < t1)
348 self.assertRaises(TypeError, lambda: badarg > t1)
349 self.assertRaises(TypeError, lambda: badarg >= t1)
350
351 def test_str(self):
352 td = timedelta
353 eq = self.assertEqual
354
355 eq(str(td(1)), "1 day, 0:00:00")
356 eq(str(td(-1)), "-1 day, 0:00:00")
357 eq(str(td(2)), "2 days, 0:00:00")
358 eq(str(td(-2)), "-2 days, 0:00:00")
359
360 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
361 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
362 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
363 "-210 days, 23:12:34")
364
365 eq(str(td(milliseconds=1)), "0:00:00.001000")
366 eq(str(td(microseconds=3)), "0:00:00.000003")
367
368 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
369 microseconds=999999)),
370 "999999999 days, 23:59:59.999999")
371
372 def test_roundtrip(self):
373 for td in (timedelta(days=999999999, hours=23, minutes=59,
374 seconds=59, microseconds=999999),
375 timedelta(days=-999999999),
376 timedelta(days=1, seconds=2, microseconds=3)):
377
378 # Verify td -> string -> td identity.
379 s = repr(td)
380 self.failUnless(s.startswith('datetime.'))
381 s = s[9:]
382 td2 = eval(s)
383 self.assertEqual(td, td2)
384
385 # Verify identity via reconstructing from pieces.
386 td2 = timedelta(td.days, td.seconds, td.microseconds)
387 self.assertEqual(td, td2)
388
389 def test_resolution_info(self):
390 self.assert_(isinstance(timedelta.min, timedelta))
391 self.assert_(isinstance(timedelta.max, timedelta))
392 self.assert_(isinstance(timedelta.resolution, timedelta))
393 self.assert_(timedelta.max > timedelta.min)
394 self.assertEqual(timedelta.min, timedelta(-999999999))
395 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
396 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
397
398 def test_overflow(self):
399 tiny = timedelta.resolution
400
401 td = timedelta.min + tiny
402 td -= tiny # no problem
403 self.assertRaises(OverflowError, td.__sub__, tiny)
404 self.assertRaises(OverflowError, td.__add__, -tiny)
405
406 td = timedelta.max - tiny
407 td += tiny # no problem
408 self.assertRaises(OverflowError, td.__add__, tiny)
409 self.assertRaises(OverflowError, td.__sub__, -tiny)
410
411 self.assertRaises(OverflowError, lambda: -timedelta.max)
412
413 def test_microsecond_rounding(self):
414 td = timedelta
415 eq = self.assertEqual
416
417 # Single-field rounding.
418 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
419 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
420 eq(td(milliseconds=0.6/1000), td(microseconds=1))
421 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
422
423 # Rounding due to contributions from more than one field.
424 us_per_hour = 3600e6
425 us_per_day = us_per_hour * 24
426 eq(td(days=.4/us_per_day), td(0))
427 eq(td(hours=.2/us_per_hour), td(0))
428 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
429
430 eq(td(days=-.4/us_per_day), td(0))
431 eq(td(hours=-.2/us_per_hour), td(0))
432 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
433
434 def test_massive_normalization(self):
435 td = timedelta(microseconds=-1)
436 self.assertEqual((td.days, td.seconds, td.microseconds),
437 (-1, 24*3600-1, 999999))
438
439 def test_bool(self):
440 self.failUnless(timedelta(1))
441 self.failUnless(timedelta(0, 1))
442 self.failUnless(timedelta(0, 0, 1))
443 self.failUnless(timedelta(microseconds=1))
444 self.failUnless(not timedelta(0))
445
Tim Petersb0c854d2003-05-17 15:57:00 +0000446 def test_subclass_timedelta(self):
447
448 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000449 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000450 def from_td(td):
451 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000452
453 def as_hours(self):
454 sum = (self.days * 24 +
455 self.seconds / 3600.0 +
456 self.microseconds / 3600e6)
457 return round(sum)
458
459 t1 = T(days=1)
460 self.assert_(type(t1) is T)
461 self.assertEqual(t1.as_hours(), 24)
462
463 t2 = T(days=-1, seconds=-3600)
464 self.assert_(type(t2) is T)
465 self.assertEqual(t2.as_hours(), -25)
466
467 t3 = t1 + t2
468 self.assert_(type(t3) is timedelta)
469 t4 = T.from_td(t3)
470 self.assert_(type(t4) is T)
471 self.assertEqual(t3.days, t4.days)
472 self.assertEqual(t3.seconds, t4.seconds)
473 self.assertEqual(t3.microseconds, t4.microseconds)
474 self.assertEqual(str(t3), str(t4))
475 self.assertEqual(t4.as_hours(), -1)
476
Tim Peters2a799bf2002-12-16 20:18:38 +0000477#############################################################################
478# date tests
479
480class TestDateOnly(unittest.TestCase):
481 # Tests here won't pass if also run on datetime objects, so don't
482 # subclass this to test datetimes too.
483
484 def test_delta_non_days_ignored(self):
485 dt = date(2000, 1, 2)
486 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
487 microseconds=5)
488 days = timedelta(delta.days)
489 self.assertEqual(days, timedelta(1))
490
491 dt2 = dt + delta
492 self.assertEqual(dt2, dt + days)
493
494 dt2 = delta + dt
495 self.assertEqual(dt2, dt + days)
496
497 dt2 = dt - delta
498 self.assertEqual(dt2, dt - days)
499
500 delta = -delta
501 days = timedelta(delta.days)
502 self.assertEqual(days, timedelta(-2))
503
504 dt2 = dt + delta
505 self.assertEqual(dt2, dt + days)
506
507 dt2 = delta + dt
508 self.assertEqual(dt2, dt + days)
509
510 dt2 = dt - delta
511 self.assertEqual(dt2, dt - days)
512
Tim Peters604c0132004-06-07 23:04:33 +0000513class SubclassDate(date):
514 sub_var = 1
515
Collin Winterc2898c52007-04-25 17:29:52 +0000516class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000517 # Tests here should pass for both dates and datetimes, except for a
518 # few tests that TestDateTime overrides.
519
520 theclass = date
521
522 def test_basic_attributes(self):
523 dt = self.theclass(2002, 3, 1)
524 self.assertEqual(dt.year, 2002)
525 self.assertEqual(dt.month, 3)
526 self.assertEqual(dt.day, 1)
527
528 def test_roundtrip(self):
529 for dt in (self.theclass(1, 2, 3),
530 self.theclass.today()):
531 # Verify dt -> string -> date identity.
532 s = repr(dt)
533 self.failUnless(s.startswith('datetime.'))
534 s = s[9:]
535 dt2 = eval(s)
536 self.assertEqual(dt, dt2)
537
538 # Verify identity via reconstructing from pieces.
539 dt2 = self.theclass(dt.year, dt.month, dt.day)
540 self.assertEqual(dt, dt2)
541
542 def test_ordinal_conversions(self):
543 # Check some fixed values.
544 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
545 (1, 12, 31, 365),
546 (2, 1, 1, 366),
547 # first example from "Calendrical Calculations"
548 (1945, 11, 12, 710347)]:
549 d = self.theclass(y, m, d)
550 self.assertEqual(n, d.toordinal())
551 fromord = self.theclass.fromordinal(n)
552 self.assertEqual(d, fromord)
553 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000554 # if we're checking something fancier than a date, verify
555 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000556 self.assertEqual(fromord.hour, 0)
557 self.assertEqual(fromord.minute, 0)
558 self.assertEqual(fromord.second, 0)
559 self.assertEqual(fromord.microsecond, 0)
560
Tim Peters0bf60bd2003-01-08 20:40:01 +0000561 # Check first and last days of year spottily across the whole
562 # range of years supported.
563 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000564 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
565 d = self.theclass(year, 1, 1)
566 n = d.toordinal()
567 d2 = self.theclass.fromordinal(n)
568 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000569 # Verify that moving back a day gets to the end of year-1.
570 if year > 1:
571 d = self.theclass.fromordinal(n-1)
572 d2 = self.theclass(year-1, 12, 31)
573 self.assertEqual(d, d2)
574 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000575
576 # Test every day in a leap-year and a non-leap year.
577 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
578 for year, isleap in (2000, True), (2002, False):
579 n = self.theclass(year, 1, 1).toordinal()
580 for month, maxday in zip(range(1, 13), dim):
581 if month == 2 and isleap:
582 maxday += 1
583 for day in range(1, maxday+1):
584 d = self.theclass(year, month, day)
585 self.assertEqual(d.toordinal(), n)
586 self.assertEqual(d, self.theclass.fromordinal(n))
587 n += 1
588
589 def test_extreme_ordinals(self):
590 a = self.theclass.min
591 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
592 aord = a.toordinal()
593 b = a.fromordinal(aord)
594 self.assertEqual(a, b)
595
596 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
597
598 b = a + timedelta(days=1)
599 self.assertEqual(b.toordinal(), aord + 1)
600 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
601
602 a = self.theclass.max
603 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
604 aord = a.toordinal()
605 b = a.fromordinal(aord)
606 self.assertEqual(a, b)
607
608 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
609
610 b = a - timedelta(days=1)
611 self.assertEqual(b.toordinal(), aord - 1)
612 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
613
614 def test_bad_constructor_arguments(self):
615 # bad years
616 self.theclass(MINYEAR, 1, 1) # no exception
617 self.theclass(MAXYEAR, 1, 1) # no exception
618 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
619 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
620 # bad months
621 self.theclass(2000, 1, 1) # no exception
622 self.theclass(2000, 12, 1) # no exception
623 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
624 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
625 # bad days
626 self.theclass(2000, 2, 29) # no exception
627 self.theclass(2004, 2, 29) # no exception
628 self.theclass(2400, 2, 29) # no exception
629 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
630 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
631 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
632 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
633 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
634 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
635
636 def test_hash_equality(self):
637 d = self.theclass(2000, 12, 31)
638 # same thing
639 e = self.theclass(2000, 12, 31)
640 self.assertEqual(d, e)
641 self.assertEqual(hash(d), hash(e))
642
643 dic = {d: 1}
644 dic[e] = 2
645 self.assertEqual(len(dic), 1)
646 self.assertEqual(dic[d], 2)
647 self.assertEqual(dic[e], 2)
648
649 d = self.theclass(2001, 1, 1)
650 # same thing
651 e = self.theclass(2001, 1, 1)
652 self.assertEqual(d, e)
653 self.assertEqual(hash(d), hash(e))
654
655 dic = {d: 1}
656 dic[e] = 2
657 self.assertEqual(len(dic), 1)
658 self.assertEqual(dic[d], 2)
659 self.assertEqual(dic[e], 2)
660
661 def test_computations(self):
662 a = self.theclass(2002, 1, 31)
663 b = self.theclass(1956, 1, 31)
664
665 diff = a-b
666 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
667 self.assertEqual(diff.seconds, 0)
668 self.assertEqual(diff.microseconds, 0)
669
670 day = timedelta(1)
671 week = timedelta(7)
672 a = self.theclass(2002, 3, 2)
673 self.assertEqual(a + day, self.theclass(2002, 3, 3))
674 self.assertEqual(day + a, self.theclass(2002, 3, 3))
675 self.assertEqual(a - day, self.theclass(2002, 3, 1))
676 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
677 self.assertEqual(a + week, self.theclass(2002, 3, 9))
678 self.assertEqual(a - week, self.theclass(2002, 2, 23))
679 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
680 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
681 self.assertEqual((a + week) - a, week)
682 self.assertEqual((a + day) - a, day)
683 self.assertEqual((a - week) - a, -week)
684 self.assertEqual((a - day) - a, -day)
685 self.assertEqual(a - (a + week), -week)
686 self.assertEqual(a - (a + day), -day)
687 self.assertEqual(a - (a - week), week)
688 self.assertEqual(a - (a - day), day)
689
690 # Add/sub ints, longs, floats should be illegal
691 for i in 1, 1L, 1.0:
692 self.assertRaises(TypeError, lambda: a+i)
693 self.assertRaises(TypeError, lambda: a-i)
694 self.assertRaises(TypeError, lambda: i+a)
695 self.assertRaises(TypeError, lambda: i-a)
696
697 # delta - date is senseless.
698 self.assertRaises(TypeError, lambda: day - a)
699 # mixing date and (delta or date) via * or // is senseless
700 self.assertRaises(TypeError, lambda: day * a)
701 self.assertRaises(TypeError, lambda: a * day)
702 self.assertRaises(TypeError, lambda: day // a)
703 self.assertRaises(TypeError, lambda: a // day)
704 self.assertRaises(TypeError, lambda: a * a)
705 self.assertRaises(TypeError, lambda: a // a)
706 # date + date is senseless
707 self.assertRaises(TypeError, lambda: a + a)
708
709 def test_overflow(self):
710 tiny = self.theclass.resolution
711
712 dt = self.theclass.min + tiny
713 dt -= tiny # no problem
714 self.assertRaises(OverflowError, dt.__sub__, tiny)
715 self.assertRaises(OverflowError, dt.__add__, -tiny)
716
717 dt = self.theclass.max - tiny
718 dt += tiny # no problem
719 self.assertRaises(OverflowError, dt.__add__, tiny)
720 self.assertRaises(OverflowError, dt.__sub__, -tiny)
721
722 def test_fromtimestamp(self):
723 import time
724
725 # Try an arbitrary fixed value.
726 year, month, day = 1999, 9, 19
727 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
728 d = self.theclass.fromtimestamp(ts)
729 self.assertEqual(d.year, year)
730 self.assertEqual(d.month, month)
731 self.assertEqual(d.day, day)
732
Tim Peters1b6f7a92004-06-20 02:50:16 +0000733 def test_insane_fromtimestamp(self):
734 # It's possible that some platform maps time_t to double,
735 # and that this test will fail there. This test should
736 # exempt such platforms (provided they return reasonable
737 # results!).
738 for insane in -1e200, 1e200:
739 self.assertRaises(ValueError, self.theclass.fromtimestamp,
740 insane)
741
Tim Peters2a799bf2002-12-16 20:18:38 +0000742 def test_today(self):
743 import time
744
745 # We claim that today() is like fromtimestamp(time.time()), so
746 # prove it.
747 for dummy in range(3):
748 today = self.theclass.today()
749 ts = time.time()
750 todayagain = self.theclass.fromtimestamp(ts)
751 if today == todayagain:
752 break
753 # There are several legit reasons that could fail:
754 # 1. It recently became midnight, between the today() and the
755 # time() calls.
756 # 2. The platform time() has such fine resolution that we'll
757 # never get the same value twice.
758 # 3. The platform time() has poor resolution, and we just
759 # happened to call today() right before a resolution quantum
760 # boundary.
761 # 4. The system clock got fiddled between calls.
762 # In any case, wait a little while and try again.
763 time.sleep(0.1)
764
765 # It worked or it didn't. If it didn't, assume it's reason #2, and
766 # let the test pass if they're within half a second of each other.
767 self.failUnless(today == todayagain or
768 abs(todayagain - today) < timedelta(seconds=0.5))
769
770 def test_weekday(self):
771 for i in range(7):
772 # March 4, 2002 is a Monday
773 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
774 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
775 # January 2, 1956 is a Monday
776 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
777 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
778
779 def test_isocalendar(self):
780 # Check examples from
781 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
782 for i in range(7):
783 d = self.theclass(2003, 12, 22+i)
784 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
785 d = self.theclass(2003, 12, 29) + timedelta(i)
786 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
787 d = self.theclass(2004, 1, 5+i)
788 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
789 d = self.theclass(2009, 12, 21+i)
790 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
791 d = self.theclass(2009, 12, 28) + timedelta(i)
792 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
793 d = self.theclass(2010, 1, 4+i)
794 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
795
796 def test_iso_long_years(self):
797 # Calculate long ISO years and compare to table from
798 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
799 ISO_LONG_YEARS_TABLE = """
800 4 32 60 88
801 9 37 65 93
802 15 43 71 99
803 20 48 76
804 26 54 82
805
806 105 133 161 189
807 111 139 167 195
808 116 144 172
809 122 150 178
810 128 156 184
811
812 201 229 257 285
813 207 235 263 291
814 212 240 268 296
815 218 246 274
816 224 252 280
817
818 303 331 359 387
819 308 336 364 392
820 314 342 370 398
821 320 348 376
822 325 353 381
823 """
824 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
825 iso_long_years.sort()
826 L = []
827 for i in range(400):
828 d = self.theclass(2000+i, 12, 31)
829 d1 = self.theclass(1600+i, 12, 31)
830 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
831 if d.isocalendar()[1] == 53:
832 L.append(i)
833 self.assertEqual(L, iso_long_years)
834
835 def test_isoformat(self):
836 t = self.theclass(2, 3, 2)
837 self.assertEqual(t.isoformat(), "0002-03-02")
838
839 def test_ctime(self):
840 t = self.theclass(2002, 3, 2)
841 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
842
843 def test_strftime(self):
844 t = self.theclass(2005, 3, 2)
845 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000846 self.assertEqual(t.strftime(""), "") # SF bug #761337
Georg Brandl4ddfcd32006-09-30 11:17:34 +0000847 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000848
849 self.assertRaises(TypeError, t.strftime) # needs an arg
850 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
851 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
852
Gregory P. Smith137d8242008-06-02 04:05:52 +0000853 # test that unicode input is allowed (issue 2782)
854 self.assertEqual(t.strftime(u"%m"), "03")
855
Tim Peters2a799bf2002-12-16 20:18:38 +0000856 # A naive object replaces %z and %Z w/ empty strings.
857 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
858
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000859 #make sure that invalid format specifiers are handled correctly
860 self.assertRaises(ValueError, t.strftime, "%e")
861 self.assertRaises(ValueError, t.strftime, "%")
862 self.assertRaises(ValueError, t.strftime, "%#")
863
864 #check that this standard extension works
865 t.strftime("%f")
866
Gregory P. Smith137d8242008-06-02 04:05:52 +0000867
Eric Smitha9f7d622008-02-17 19:46:49 +0000868 def test_format(self):
869 dt = self.theclass(2007, 9, 10)
870 self.assertEqual(dt.__format__(''), str(dt))
871
872 # check that a derived class's __str__() gets called
873 class A(self.theclass):
874 def __str__(self):
875 return 'A'
876 a = A(2007, 9, 10)
877 self.assertEqual(a.__format__(''), 'A')
878
879 # check that a derived class's strftime gets called
880 class B(self.theclass):
881 def strftime(self, format_spec):
882 return 'B'
883 b = B(2007, 9, 10)
884 self.assertEqual(b.__format__(''), str(dt))
885
886 for fmt in ["m:%m d:%d y:%y",
887 "m:%m d:%d y:%y H:%H M:%M S:%S",
888 "%z %Z",
889 ]:
890 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
891 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
892 self.assertEqual(b.__format__(fmt), 'B')
893
Tim Peters2a799bf2002-12-16 20:18:38 +0000894 def test_resolution_info(self):
895 self.assert_(isinstance(self.theclass.min, self.theclass))
896 self.assert_(isinstance(self.theclass.max, self.theclass))
897 self.assert_(isinstance(self.theclass.resolution, timedelta))
898 self.assert_(self.theclass.max > self.theclass.min)
899
900 def test_extreme_timedelta(self):
901 big = self.theclass.max - self.theclass.min
902 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
903 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
904 # n == 315537897599999999 ~= 2**58.13
905 justasbig = timedelta(0, 0, n)
906 self.assertEqual(big, justasbig)
907 self.assertEqual(self.theclass.min + big, self.theclass.max)
908 self.assertEqual(self.theclass.max - big, self.theclass.min)
909
910 def test_timetuple(self):
911 for i in range(7):
912 # January 2, 1956 is a Monday (0)
913 d = self.theclass(1956, 1, 2+i)
914 t = d.timetuple()
915 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
916 # February 1, 1956 is a Wednesday (2)
917 d = self.theclass(1956, 2, 1+i)
918 t = d.timetuple()
919 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
920 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
921 # of the year.
922 d = self.theclass(1956, 3, 1+i)
923 t = d.timetuple()
924 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
925 self.assertEqual(t.tm_year, 1956)
926 self.assertEqual(t.tm_mon, 3)
927 self.assertEqual(t.tm_mday, 1+i)
928 self.assertEqual(t.tm_hour, 0)
929 self.assertEqual(t.tm_min, 0)
930 self.assertEqual(t.tm_sec, 0)
931 self.assertEqual(t.tm_wday, (3+i)%7)
932 self.assertEqual(t.tm_yday, 61+i)
933 self.assertEqual(t.tm_isdst, -1)
934
935 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000936 args = 6, 7, 23
937 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000938 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000939 green = pickler.dumps(orig, proto)
940 derived = unpickler.loads(green)
941 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000942
943 def test_compare(self):
944 t1 = self.theclass(2, 3, 4)
945 t2 = self.theclass(2, 3, 4)
946 self.failUnless(t1 == t2)
947 self.failUnless(t1 <= t2)
948 self.failUnless(t1 >= t2)
949 self.failUnless(not t1 != t2)
950 self.failUnless(not t1 < t2)
951 self.failUnless(not t1 > t2)
952 self.assertEqual(cmp(t1, t2), 0)
953 self.assertEqual(cmp(t2, t1), 0)
954
955 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
956 t2 = self.theclass(*args) # this is larger than t1
957 self.failUnless(t1 < t2)
958 self.failUnless(t2 > t1)
959 self.failUnless(t1 <= t2)
960 self.failUnless(t2 >= t1)
961 self.failUnless(t1 != t2)
962 self.failUnless(t2 != t1)
963 self.failUnless(not t1 == t2)
964 self.failUnless(not t2 == t1)
965 self.failUnless(not t1 > t2)
966 self.failUnless(not t2 < t1)
967 self.failUnless(not t1 >= t2)
968 self.failUnless(not t2 <= t1)
969 self.assertEqual(cmp(t1, t2), -1)
970 self.assertEqual(cmp(t2, t1), 1)
971
Tim Peters68124bb2003-02-08 03:46:31 +0000972 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000973 self.assertEqual(t1 == badarg, False)
974 self.assertEqual(t1 != badarg, True)
975 self.assertEqual(badarg == t1, False)
976 self.assertEqual(badarg != t1, True)
977
Tim Peters2a799bf2002-12-16 20:18:38 +0000978 self.assertRaises(TypeError, lambda: t1 < badarg)
979 self.assertRaises(TypeError, lambda: t1 > badarg)
980 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000981 self.assertRaises(TypeError, lambda: badarg <= t1)
982 self.assertRaises(TypeError, lambda: badarg < t1)
983 self.assertRaises(TypeError, lambda: badarg > t1)
984 self.assertRaises(TypeError, lambda: badarg >= t1)
985
Tim Peters8d81a012003-01-24 22:36:34 +0000986 def test_mixed_compare(self):
987 our = self.theclass(2000, 4, 5)
988 self.assertRaises(TypeError, cmp, our, 1)
989 self.assertRaises(TypeError, cmp, 1, our)
990
991 class AnotherDateTimeClass(object):
992 def __cmp__(self, other):
993 # Return "equal" so calling this can't be confused with
994 # compare-by-address (which never says "equal" for distinct
995 # objects).
996 return 0
Nick Coghlan48361f52008-08-11 15:45:58 +0000997 __hash__ = None # Silence Py3k warning
Tim Peters8d81a012003-01-24 22:36:34 +0000998
999 # This still errors, because date and datetime comparison raise
1000 # TypeError instead of NotImplemented when they don't know what to
1001 # do, in order to stop comparison from falling back to the default
1002 # compare-by-address.
1003 their = AnotherDateTimeClass()
1004 self.assertRaises(TypeError, cmp, our, their)
1005 # Oops: The next stab raises TypeError in the C implementation,
1006 # but not in the Python implementation of datetime. The difference
1007 # is due to that the Python implementation defines __cmp__ but
1008 # the C implementation defines tp_richcompare. This is more pain
1009 # to fix than it's worth, so commenting out the test.
1010 # self.assertEqual(cmp(their, our), 0)
1011
1012 # But date and datetime comparison return NotImplemented instead if the
1013 # other object has a timetuple attr. This gives the other object a
1014 # chance to do the comparison.
1015 class Comparable(AnotherDateTimeClass):
1016 def timetuple(self):
1017 return ()
1018
1019 their = Comparable()
1020 self.assertEqual(cmp(our, their), 0)
1021 self.assertEqual(cmp(their, our), 0)
1022 self.failUnless(our == their)
1023 self.failUnless(their == our)
1024
Tim Peters2a799bf2002-12-16 20:18:38 +00001025 def test_bool(self):
1026 # All dates are considered true.
1027 self.failUnless(self.theclass.min)
1028 self.failUnless(self.theclass.max)
1029
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001030 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001031 # For nasty technical reasons, we can't handle years before 1900.
1032 cls = self.theclass
1033 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1034 for y in 1, 49, 51, 99, 100, 1000, 1899:
1035 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001036
1037 def test_replace(self):
1038 cls = self.theclass
1039 args = [1, 2, 3]
1040 base = cls(*args)
1041 self.assertEqual(base, base.replace())
1042
1043 i = 0
1044 for name, newval in (("year", 2),
1045 ("month", 3),
1046 ("day", 4)):
1047 newargs = args[:]
1048 newargs[i] = newval
1049 expected = cls(*newargs)
1050 got = base.replace(**{name: newval})
1051 self.assertEqual(expected, got)
1052 i += 1
1053
1054 # Out of bounds.
1055 base = cls(2000, 2, 29)
1056 self.assertRaises(ValueError, base.replace, year=2001)
1057
Tim Petersa98924a2003-05-17 05:55:19 +00001058 def test_subclass_date(self):
1059
1060 class C(self.theclass):
1061 theAnswer = 42
1062
1063 def __new__(cls, *args, **kws):
1064 temp = kws.copy()
1065 extra = temp.pop('extra')
1066 result = self.theclass.__new__(cls, *args, **temp)
1067 result.extra = extra
1068 return result
1069
1070 def newmeth(self, start):
1071 return start + self.year + self.month
1072
1073 args = 2003, 4, 14
1074
1075 dt1 = self.theclass(*args)
1076 dt2 = C(*args, **{'extra': 7})
1077
1078 self.assertEqual(dt2.__class__, C)
1079 self.assertEqual(dt2.theAnswer, 42)
1080 self.assertEqual(dt2.extra, 7)
1081 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1082 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1083
Tim Peters604c0132004-06-07 23:04:33 +00001084 def test_pickling_subclass_date(self):
1085
1086 args = 6, 7, 23
1087 orig = SubclassDate(*args)
1088 for pickler, unpickler, proto in pickle_choices:
1089 green = pickler.dumps(orig, proto)
1090 derived = unpickler.loads(green)
1091 self.assertEqual(orig, derived)
1092
Tim Peters3f606292004-03-21 23:38:41 +00001093 def test_backdoor_resistance(self):
1094 # For fast unpickling, the constructor accepts a pickle string.
1095 # This is a low-overhead backdoor. A user can (by intent or
1096 # mistake) pass a string directly, which (if it's the right length)
1097 # will get treated like a pickle, and bypass the normal sanity
1098 # checks in the constructor. This can create insane objects.
1099 # The constructor doesn't want to burn the time to validate all
1100 # fields, but does check the month field. This stops, e.g.,
1101 # datetime.datetime('1995-03-25') from yielding an insane object.
1102 base = '1995-03-25'
1103 if not issubclass(self.theclass, datetime):
1104 base = base[:4]
1105 for month_byte in '9', chr(0), chr(13), '\xff':
1106 self.assertRaises(TypeError, self.theclass,
1107 base[:2] + month_byte + base[3:])
1108 for ord_byte in range(1, 13):
1109 # This shouldn't blow up because of the month byte alone. If
1110 # the implementation changes to do more-careful checking, it may
1111 # blow up because other fields are insane.
1112 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001113
Tim Peters2a799bf2002-12-16 20:18:38 +00001114#############################################################################
1115# datetime tests
1116
Tim Peters604c0132004-06-07 23:04:33 +00001117class SubclassDatetime(datetime):
1118 sub_var = 1
1119
Tim Peters2a799bf2002-12-16 20:18:38 +00001120class TestDateTime(TestDate):
1121
1122 theclass = datetime
1123
1124 def test_basic_attributes(self):
1125 dt = self.theclass(2002, 3, 1, 12, 0)
1126 self.assertEqual(dt.year, 2002)
1127 self.assertEqual(dt.month, 3)
1128 self.assertEqual(dt.day, 1)
1129 self.assertEqual(dt.hour, 12)
1130 self.assertEqual(dt.minute, 0)
1131 self.assertEqual(dt.second, 0)
1132 self.assertEqual(dt.microsecond, 0)
1133
1134 def test_basic_attributes_nonzero(self):
1135 # Make sure all attributes are non-zero so bugs in
1136 # bit-shifting access show up.
1137 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1138 self.assertEqual(dt.year, 2002)
1139 self.assertEqual(dt.month, 3)
1140 self.assertEqual(dt.day, 1)
1141 self.assertEqual(dt.hour, 12)
1142 self.assertEqual(dt.minute, 59)
1143 self.assertEqual(dt.second, 59)
1144 self.assertEqual(dt.microsecond, 8000)
1145
1146 def test_roundtrip(self):
1147 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1148 self.theclass.now()):
1149 # Verify dt -> string -> datetime identity.
1150 s = repr(dt)
1151 self.failUnless(s.startswith('datetime.'))
1152 s = s[9:]
1153 dt2 = eval(s)
1154 self.assertEqual(dt, dt2)
1155
1156 # Verify identity via reconstructing from pieces.
1157 dt2 = self.theclass(dt.year, dt.month, dt.day,
1158 dt.hour, dt.minute, dt.second,
1159 dt.microsecond)
1160 self.assertEqual(dt, dt2)
1161
1162 def test_isoformat(self):
1163 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1164 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1165 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1166 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1167 # str is ISO format with the separator forced to a blank.
1168 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1169
1170 t = self.theclass(2, 3, 2)
1171 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1172 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1173 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1174 # str is ISO format with the separator forced to a blank.
1175 self.assertEqual(str(t), "0002-03-02 00:00:00")
1176
Eric Smitha9f7d622008-02-17 19:46:49 +00001177 def test_format(self):
1178 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1179 self.assertEqual(dt.__format__(''), str(dt))
1180
1181 # check that a derived class's __str__() gets called
1182 class A(self.theclass):
1183 def __str__(self):
1184 return 'A'
1185 a = A(2007, 9, 10, 4, 5, 1, 123)
1186 self.assertEqual(a.__format__(''), 'A')
1187
1188 # check that a derived class's strftime gets called
1189 class B(self.theclass):
1190 def strftime(self, format_spec):
1191 return 'B'
1192 b = B(2007, 9, 10, 4, 5, 1, 123)
1193 self.assertEqual(b.__format__(''), str(dt))
1194
1195 for fmt in ["m:%m d:%d y:%y",
1196 "m:%m d:%d y:%y H:%H M:%M S:%S",
1197 "%z %Z",
1198 ]:
1199 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1200 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1201 self.assertEqual(b.__format__(fmt), 'B')
1202
Tim Peters2a799bf2002-12-16 20:18:38 +00001203 def test_more_ctime(self):
1204 # Test fields that TestDate doesn't touch.
1205 import time
1206
1207 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1208 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1209 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1210 # out. The difference is that t.ctime() produces " 2" for the day,
1211 # but platform ctime() produces "02" for the day. According to
1212 # C99, t.ctime() is correct here.
1213 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1214
1215 # So test a case where that difference doesn't matter.
1216 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1217 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1218
1219 def test_tz_independent_comparing(self):
1220 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1221 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1222 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1223 self.assertEqual(dt1, dt3)
1224 self.assert_(dt2 > dt3)
1225
1226 # Make sure comparison doesn't forget microseconds, and isn't done
1227 # via comparing a float timestamp (an IEEE double doesn't have enough
1228 # precision to span microsecond resolution across years 1 thru 9999,
1229 # so comparing via timestamp necessarily calls some distinct values
1230 # equal).
1231 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1232 us = timedelta(microseconds=1)
1233 dt2 = dt1 + us
1234 self.assertEqual(dt2 - dt1, us)
1235 self.assert_(dt1 < dt2)
1236
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001237 def test_strftime_with_bad_tzname_replace(self):
1238 # verify ok if tzinfo.tzname().replace() returns a non-string
1239 class MyTzInfo(FixedOffset):
1240 def tzname(self, dt):
1241 class MyStr(str):
1242 def replace(self, *args):
1243 return None
1244 return MyStr('name')
1245 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1246 self.assertRaises(TypeError, t.strftime, '%Z')
1247
Tim Peters2a799bf2002-12-16 20:18:38 +00001248 def test_bad_constructor_arguments(self):
1249 # bad years
1250 self.theclass(MINYEAR, 1, 1) # no exception
1251 self.theclass(MAXYEAR, 1, 1) # no exception
1252 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1253 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1254 # bad months
1255 self.theclass(2000, 1, 1) # no exception
1256 self.theclass(2000, 12, 1) # no exception
1257 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1258 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1259 # bad days
1260 self.theclass(2000, 2, 29) # no exception
1261 self.theclass(2004, 2, 29) # no exception
1262 self.theclass(2400, 2, 29) # no exception
1263 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1264 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1265 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1266 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1267 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1268 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1269 # bad hours
1270 self.theclass(2000, 1, 31, 0) # no exception
1271 self.theclass(2000, 1, 31, 23) # no exception
1272 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1273 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1274 # bad minutes
1275 self.theclass(2000, 1, 31, 23, 0) # no exception
1276 self.theclass(2000, 1, 31, 23, 59) # no exception
1277 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1278 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1279 # bad seconds
1280 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1281 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1282 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1283 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1284 # bad microseconds
1285 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1286 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1287 self.assertRaises(ValueError, self.theclass,
1288 2000, 1, 31, 23, 59, 59, -1)
1289 self.assertRaises(ValueError, self.theclass,
1290 2000, 1, 31, 23, 59, 59,
1291 1000000)
1292
1293 def test_hash_equality(self):
1294 d = self.theclass(2000, 12, 31, 23, 30, 17)
1295 e = self.theclass(2000, 12, 31, 23, 30, 17)
1296 self.assertEqual(d, e)
1297 self.assertEqual(hash(d), hash(e))
1298
1299 dic = {d: 1}
1300 dic[e] = 2
1301 self.assertEqual(len(dic), 1)
1302 self.assertEqual(dic[d], 2)
1303 self.assertEqual(dic[e], 2)
1304
1305 d = self.theclass(2001, 1, 1, 0, 5, 17)
1306 e = self.theclass(2001, 1, 1, 0, 5, 17)
1307 self.assertEqual(d, e)
1308 self.assertEqual(hash(d), hash(e))
1309
1310 dic = {d: 1}
1311 dic[e] = 2
1312 self.assertEqual(len(dic), 1)
1313 self.assertEqual(dic[d], 2)
1314 self.assertEqual(dic[e], 2)
1315
1316 def test_computations(self):
1317 a = self.theclass(2002, 1, 31)
1318 b = self.theclass(1956, 1, 31)
1319 diff = a-b
1320 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1321 self.assertEqual(diff.seconds, 0)
1322 self.assertEqual(diff.microseconds, 0)
1323 a = self.theclass(2002, 3, 2, 17, 6)
1324 millisec = timedelta(0, 0, 1000)
1325 hour = timedelta(0, 3600)
1326 day = timedelta(1)
1327 week = timedelta(7)
1328 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1329 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1330 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1331 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1332 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1333 self.assertEqual(a - hour, a + -hour)
1334 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1335 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1336 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1337 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1338 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1339 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1340 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1341 self.assertEqual((a + week) - a, week)
1342 self.assertEqual((a + day) - a, day)
1343 self.assertEqual((a + hour) - a, hour)
1344 self.assertEqual((a + millisec) - a, millisec)
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 - (a + week), -week)
1350 self.assertEqual(a - (a + day), -day)
1351 self.assertEqual(a - (a + hour), -hour)
1352 self.assertEqual(a - (a + millisec), -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 + (week + day + hour + millisec),
1358 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1359 self.assertEqual(a + (week + day + hour + millisec),
1360 (((a + week) + day) + hour) + millisec)
1361 self.assertEqual(a - (week + day + hour + millisec),
1362 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1363 self.assertEqual(a - (week + day + hour + millisec),
1364 (((a - week) - day) - hour) - millisec)
1365 # Add/sub ints, longs, floats should be illegal
1366 for i in 1, 1L, 1.0:
1367 self.assertRaises(TypeError, lambda: a+i)
1368 self.assertRaises(TypeError, lambda: a-i)
1369 self.assertRaises(TypeError, lambda: i+a)
1370 self.assertRaises(TypeError, lambda: i-a)
1371
1372 # delta - datetime is senseless.
1373 self.assertRaises(TypeError, lambda: day - a)
1374 # mixing datetime and (delta or datetime) via * or // is senseless
1375 self.assertRaises(TypeError, lambda: day * a)
1376 self.assertRaises(TypeError, lambda: a * day)
1377 self.assertRaises(TypeError, lambda: day // a)
1378 self.assertRaises(TypeError, lambda: a // day)
1379 self.assertRaises(TypeError, lambda: a * a)
1380 self.assertRaises(TypeError, lambda: a // a)
1381 # datetime + datetime is senseless
1382 self.assertRaises(TypeError, lambda: a + a)
1383
1384 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001385 args = 6, 7, 23, 20, 59, 1, 64**2
1386 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001387 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001388 green = pickler.dumps(orig, proto)
1389 derived = unpickler.loads(green)
1390 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001391
Guido van Rossum275666f2003-02-07 21:49:01 +00001392 def test_more_pickling(self):
1393 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1394 s = pickle.dumps(a)
1395 b = pickle.loads(s)
1396 self.assertEqual(b.year, 2003)
1397 self.assertEqual(b.month, 2)
1398 self.assertEqual(b.day, 7)
1399
Tim Peters604c0132004-06-07 23:04:33 +00001400 def test_pickling_subclass_datetime(self):
1401 args = 6, 7, 23, 20, 59, 1, 64**2
1402 orig = SubclassDatetime(*args)
1403 for pickler, unpickler, proto in pickle_choices:
1404 green = pickler.dumps(orig, proto)
1405 derived = unpickler.loads(green)
1406 self.assertEqual(orig, derived)
1407
Tim Peters2a799bf2002-12-16 20:18:38 +00001408 def test_more_compare(self):
1409 # The test_compare() inherited from TestDate covers the error cases.
1410 # We just want to test lexicographic ordering on the members datetime
1411 # has that date lacks.
1412 args = [2000, 11, 29, 20, 58, 16, 999998]
1413 t1 = self.theclass(*args)
1414 t2 = self.theclass(*args)
1415 self.failUnless(t1 == t2)
1416 self.failUnless(t1 <= t2)
1417 self.failUnless(t1 >= t2)
1418 self.failUnless(not t1 != t2)
1419 self.failUnless(not t1 < t2)
1420 self.failUnless(not t1 > t2)
1421 self.assertEqual(cmp(t1, t2), 0)
1422 self.assertEqual(cmp(t2, t1), 0)
1423
1424 for i in range(len(args)):
1425 newargs = args[:]
1426 newargs[i] = args[i] + 1
1427 t2 = self.theclass(*newargs) # this is larger than t1
1428 self.failUnless(t1 < t2)
1429 self.failUnless(t2 > t1)
1430 self.failUnless(t1 <= t2)
1431 self.failUnless(t2 >= t1)
1432 self.failUnless(t1 != t2)
1433 self.failUnless(t2 != t1)
1434 self.failUnless(not t1 == t2)
1435 self.failUnless(not t2 == t1)
1436 self.failUnless(not t1 > t2)
1437 self.failUnless(not t2 < t1)
1438 self.failUnless(not t1 >= t2)
1439 self.failUnless(not t2 <= t1)
1440 self.assertEqual(cmp(t1, t2), -1)
1441 self.assertEqual(cmp(t2, t1), 1)
1442
1443
1444 # A helper for timestamp constructor tests.
1445 def verify_field_equality(self, expected, got):
1446 self.assertEqual(expected.tm_year, got.year)
1447 self.assertEqual(expected.tm_mon, got.month)
1448 self.assertEqual(expected.tm_mday, got.day)
1449 self.assertEqual(expected.tm_hour, got.hour)
1450 self.assertEqual(expected.tm_min, got.minute)
1451 self.assertEqual(expected.tm_sec, got.second)
1452
1453 def test_fromtimestamp(self):
1454 import time
1455
1456 ts = time.time()
1457 expected = time.localtime(ts)
1458 got = self.theclass.fromtimestamp(ts)
1459 self.verify_field_equality(expected, got)
1460
1461 def test_utcfromtimestamp(self):
1462 import time
1463
1464 ts = time.time()
1465 expected = time.gmtime(ts)
1466 got = self.theclass.utcfromtimestamp(ts)
1467 self.verify_field_equality(expected, got)
1468
Georg Brandl6d78a582006-04-28 19:09:24 +00001469 def test_microsecond_rounding(self):
1470 # Test whether fromtimestamp "rounds up" floats that are less
1471 # than one microsecond smaller than an integer.
1472 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1473 self.theclass.fromtimestamp(1))
1474
Tim Peters1b6f7a92004-06-20 02:50:16 +00001475 def test_insane_fromtimestamp(self):
1476 # It's possible that some platform maps time_t to double,
1477 # and that this test will fail there. This test should
1478 # exempt such platforms (provided they return reasonable
1479 # results!).
1480 for insane in -1e200, 1e200:
1481 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1482 insane)
1483
1484 def test_insane_utcfromtimestamp(self):
1485 # It's possible that some platform maps time_t to double,
1486 # and that this test will fail there. This test should
1487 # exempt such platforms (provided they return reasonable
1488 # results!).
1489 for insane in -1e200, 1e200:
1490 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1491 insane)
1492
Guido van Rossum2054ee92007-03-06 15:50:01 +00001493 def test_negative_float_fromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001494 # Windows doesn't accept negative timestamps
1495 if os.name == "nt":
1496 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001497 # The result is tz-dependent; at least test that this doesn't
1498 # fail (like it did before bug 1646728 was fixed).
1499 self.theclass.fromtimestamp(-1.05)
1500
1501 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001502 # Windows doesn't accept negative timestamps
1503 if os.name == "nt":
1504 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001505 d = self.theclass.utcfromtimestamp(-1.05)
1506 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1507
Tim Peters2a799bf2002-12-16 20:18:38 +00001508 def test_utcnow(self):
1509 import time
1510
1511 # Call it a success if utcnow() and utcfromtimestamp() are within
1512 # a second of each other.
1513 tolerance = timedelta(seconds=1)
1514 for dummy in range(3):
1515 from_now = self.theclass.utcnow()
1516 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1517 if abs(from_timestamp - from_now) <= tolerance:
1518 break
1519 # Else try again a few times.
1520 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1521
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001522 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001523 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001524
Skip Montanarofc070d22008-03-15 16:04:45 +00001525 string = '2004-12-01 13:02:47.197'
1526 format = '%Y-%m-%d %H:%M:%S.%f'
1527 result, frac = _strptime._strptime(string, format)
1528 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001529 got = self.theclass.strptime(string, format)
1530 self.assertEqual(expected, got)
1531
Tim Peters2a799bf2002-12-16 20:18:38 +00001532 def test_more_timetuple(self):
1533 # This tests fields beyond those tested by the TestDate.test_timetuple.
1534 t = self.theclass(2004, 12, 31, 6, 22, 33)
1535 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1536 self.assertEqual(t.timetuple(),
1537 (t.year, t.month, t.day,
1538 t.hour, t.minute, t.second,
1539 t.weekday(),
1540 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1541 -1))
1542 tt = t.timetuple()
1543 self.assertEqual(tt.tm_year, t.year)
1544 self.assertEqual(tt.tm_mon, t.month)
1545 self.assertEqual(tt.tm_mday, t.day)
1546 self.assertEqual(tt.tm_hour, t.hour)
1547 self.assertEqual(tt.tm_min, t.minute)
1548 self.assertEqual(tt.tm_sec, t.second)
1549 self.assertEqual(tt.tm_wday, t.weekday())
1550 self.assertEqual(tt.tm_yday, t.toordinal() -
1551 date(t.year, 1, 1).toordinal() + 1)
1552 self.assertEqual(tt.tm_isdst, -1)
1553
1554 def test_more_strftime(self):
1555 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001556 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1557 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1558 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001559
1560 def test_extract(self):
1561 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1562 self.assertEqual(dt.date(), date(2002, 3, 4))
1563 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1564
1565 def test_combine(self):
1566 d = date(2002, 3, 4)
1567 t = time(18, 45, 3, 1234)
1568 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1569 combine = self.theclass.combine
1570 dt = combine(d, t)
1571 self.assertEqual(dt, expected)
1572
1573 dt = combine(time=t, date=d)
1574 self.assertEqual(dt, expected)
1575
1576 self.assertEqual(d, dt.date())
1577 self.assertEqual(t, dt.time())
1578 self.assertEqual(dt, combine(dt.date(), dt.time()))
1579
1580 self.assertRaises(TypeError, combine) # need an arg
1581 self.assertRaises(TypeError, combine, d) # need two args
1582 self.assertRaises(TypeError, combine, t, d) # args reversed
1583 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1584 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1585
Tim Peters12bf3392002-12-24 05:41:27 +00001586 def test_replace(self):
1587 cls = self.theclass
1588 args = [1, 2, 3, 4, 5, 6, 7]
1589 base = cls(*args)
1590 self.assertEqual(base, base.replace())
1591
1592 i = 0
1593 for name, newval in (("year", 2),
1594 ("month", 3),
1595 ("day", 4),
1596 ("hour", 5),
1597 ("minute", 6),
1598 ("second", 7),
1599 ("microsecond", 8)):
1600 newargs = args[:]
1601 newargs[i] = newval
1602 expected = cls(*newargs)
1603 got = base.replace(**{name: newval})
1604 self.assertEqual(expected, got)
1605 i += 1
1606
1607 # Out of bounds.
1608 base = cls(2000, 2, 29)
1609 self.assertRaises(ValueError, base.replace, year=2001)
1610
Tim Peters80475bb2002-12-25 07:40:55 +00001611 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001612 # Pretty boring! The TZ test is more interesting here. astimezone()
1613 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001614 dt = self.theclass.now()
1615 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001616 self.assertRaises(TypeError, dt.astimezone) # not enough args
1617 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1618 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001619 self.assertRaises(ValueError, dt.astimezone, f) # naive
1620 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001621
Tim Peters52dcce22003-01-23 16:36:11 +00001622 class Bogus(tzinfo):
1623 def utcoffset(self, dt): return None
1624 def dst(self, dt): return timedelta(0)
1625 bog = Bogus()
1626 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1627
1628 class AlsoBogus(tzinfo):
1629 def utcoffset(self, dt): return timedelta(0)
1630 def dst(self, dt): return None
1631 alsobog = AlsoBogus()
1632 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001633
Tim Petersa98924a2003-05-17 05:55:19 +00001634 def test_subclass_datetime(self):
1635
1636 class C(self.theclass):
1637 theAnswer = 42
1638
1639 def __new__(cls, *args, **kws):
1640 temp = kws.copy()
1641 extra = temp.pop('extra')
1642 result = self.theclass.__new__(cls, *args, **temp)
1643 result.extra = extra
1644 return result
1645
1646 def newmeth(self, start):
1647 return start + self.year + self.month + self.second
1648
1649 args = 2003, 4, 14, 12, 13, 41
1650
1651 dt1 = self.theclass(*args)
1652 dt2 = C(*args, **{'extra': 7})
1653
1654 self.assertEqual(dt2.__class__, C)
1655 self.assertEqual(dt2.theAnswer, 42)
1656 self.assertEqual(dt2.extra, 7)
1657 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1658 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1659 dt1.second - 7)
1660
Tim Peters604c0132004-06-07 23:04:33 +00001661class SubclassTime(time):
1662 sub_var = 1
1663
Collin Winterc2898c52007-04-25 17:29:52 +00001664class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001665
1666 theclass = time
1667
1668 def test_basic_attributes(self):
1669 t = self.theclass(12, 0)
1670 self.assertEqual(t.hour, 12)
1671 self.assertEqual(t.minute, 0)
1672 self.assertEqual(t.second, 0)
1673 self.assertEqual(t.microsecond, 0)
1674
1675 def test_basic_attributes_nonzero(self):
1676 # Make sure all attributes are non-zero so bugs in
1677 # bit-shifting access show up.
1678 t = self.theclass(12, 59, 59, 8000)
1679 self.assertEqual(t.hour, 12)
1680 self.assertEqual(t.minute, 59)
1681 self.assertEqual(t.second, 59)
1682 self.assertEqual(t.microsecond, 8000)
1683
1684 def test_roundtrip(self):
1685 t = self.theclass(1, 2, 3, 4)
1686
1687 # Verify t -> string -> time identity.
1688 s = repr(t)
1689 self.failUnless(s.startswith('datetime.'))
1690 s = s[9:]
1691 t2 = eval(s)
1692 self.assertEqual(t, t2)
1693
1694 # Verify identity via reconstructing from pieces.
1695 t2 = self.theclass(t.hour, t.minute, t.second,
1696 t.microsecond)
1697 self.assertEqual(t, t2)
1698
1699 def test_comparing(self):
1700 args = [1, 2, 3, 4]
1701 t1 = self.theclass(*args)
1702 t2 = self.theclass(*args)
1703 self.failUnless(t1 == t2)
1704 self.failUnless(t1 <= t2)
1705 self.failUnless(t1 >= t2)
1706 self.failUnless(not t1 != t2)
1707 self.failUnless(not t1 < t2)
1708 self.failUnless(not t1 > t2)
1709 self.assertEqual(cmp(t1, t2), 0)
1710 self.assertEqual(cmp(t2, t1), 0)
1711
1712 for i in range(len(args)):
1713 newargs = args[:]
1714 newargs[i] = args[i] + 1
1715 t2 = self.theclass(*newargs) # this is larger than t1
1716 self.failUnless(t1 < t2)
1717 self.failUnless(t2 > t1)
1718 self.failUnless(t1 <= t2)
1719 self.failUnless(t2 >= t1)
1720 self.failUnless(t1 != t2)
1721 self.failUnless(t2 != t1)
1722 self.failUnless(not t1 == t2)
1723 self.failUnless(not t2 == t1)
1724 self.failUnless(not t1 > t2)
1725 self.failUnless(not t2 < t1)
1726 self.failUnless(not t1 >= t2)
1727 self.failUnless(not t2 <= t1)
1728 self.assertEqual(cmp(t1, t2), -1)
1729 self.assertEqual(cmp(t2, t1), 1)
1730
Tim Peters68124bb2003-02-08 03:46:31 +00001731 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001732 self.assertEqual(t1 == badarg, False)
1733 self.assertEqual(t1 != badarg, True)
1734 self.assertEqual(badarg == t1, False)
1735 self.assertEqual(badarg != t1, True)
1736
Tim Peters2a799bf2002-12-16 20:18:38 +00001737 self.assertRaises(TypeError, lambda: t1 <= badarg)
1738 self.assertRaises(TypeError, lambda: t1 < badarg)
1739 self.assertRaises(TypeError, lambda: t1 > badarg)
1740 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001741 self.assertRaises(TypeError, lambda: badarg <= t1)
1742 self.assertRaises(TypeError, lambda: badarg < t1)
1743 self.assertRaises(TypeError, lambda: badarg > t1)
1744 self.assertRaises(TypeError, lambda: badarg >= t1)
1745
1746 def test_bad_constructor_arguments(self):
1747 # bad hours
1748 self.theclass(0, 0) # no exception
1749 self.theclass(23, 0) # no exception
1750 self.assertRaises(ValueError, self.theclass, -1, 0)
1751 self.assertRaises(ValueError, self.theclass, 24, 0)
1752 # bad minutes
1753 self.theclass(23, 0) # no exception
1754 self.theclass(23, 59) # no exception
1755 self.assertRaises(ValueError, self.theclass, 23, -1)
1756 self.assertRaises(ValueError, self.theclass, 23, 60)
1757 # bad seconds
1758 self.theclass(23, 59, 0) # no exception
1759 self.theclass(23, 59, 59) # no exception
1760 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1761 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1762 # bad microseconds
1763 self.theclass(23, 59, 59, 0) # no exception
1764 self.theclass(23, 59, 59, 999999) # no exception
1765 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1766 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1767
1768 def test_hash_equality(self):
1769 d = self.theclass(23, 30, 17)
1770 e = self.theclass(23, 30, 17)
1771 self.assertEqual(d, e)
1772 self.assertEqual(hash(d), hash(e))
1773
1774 dic = {d: 1}
1775 dic[e] = 2
1776 self.assertEqual(len(dic), 1)
1777 self.assertEqual(dic[d], 2)
1778 self.assertEqual(dic[e], 2)
1779
1780 d = self.theclass(0, 5, 17)
1781 e = self.theclass(0, 5, 17)
1782 self.assertEqual(d, e)
1783 self.assertEqual(hash(d), hash(e))
1784
1785 dic = {d: 1}
1786 dic[e] = 2
1787 self.assertEqual(len(dic), 1)
1788 self.assertEqual(dic[d], 2)
1789 self.assertEqual(dic[e], 2)
1790
1791 def test_isoformat(self):
1792 t = self.theclass(4, 5, 1, 123)
1793 self.assertEqual(t.isoformat(), "04:05:01.000123")
1794 self.assertEqual(t.isoformat(), str(t))
1795
1796 t = self.theclass()
1797 self.assertEqual(t.isoformat(), "00:00:00")
1798 self.assertEqual(t.isoformat(), str(t))
1799
1800 t = self.theclass(microsecond=1)
1801 self.assertEqual(t.isoformat(), "00:00:00.000001")
1802 self.assertEqual(t.isoformat(), str(t))
1803
1804 t = self.theclass(microsecond=10)
1805 self.assertEqual(t.isoformat(), "00:00:00.000010")
1806 self.assertEqual(t.isoformat(), str(t))
1807
1808 t = self.theclass(microsecond=100)
1809 self.assertEqual(t.isoformat(), "00:00:00.000100")
1810 self.assertEqual(t.isoformat(), str(t))
1811
1812 t = self.theclass(microsecond=1000)
1813 self.assertEqual(t.isoformat(), "00:00:00.001000")
1814 self.assertEqual(t.isoformat(), str(t))
1815
1816 t = self.theclass(microsecond=10000)
1817 self.assertEqual(t.isoformat(), "00:00:00.010000")
1818 self.assertEqual(t.isoformat(), str(t))
1819
1820 t = self.theclass(microsecond=100000)
1821 self.assertEqual(t.isoformat(), "00:00:00.100000")
1822 self.assertEqual(t.isoformat(), str(t))
1823
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001824 def test_1653736(self):
1825 # verify it doesn't accept extra keyword arguments
1826 t = self.theclass(second=1)
1827 self.assertRaises(TypeError, t.isoformat, foo=3)
1828
Tim Peters2a799bf2002-12-16 20:18:38 +00001829 def test_strftime(self):
1830 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001831 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001832 # A naive object replaces %z and %Z with empty strings.
1833 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1834
Eric Smitha9f7d622008-02-17 19:46:49 +00001835 def test_format(self):
1836 t = self.theclass(1, 2, 3, 4)
1837 self.assertEqual(t.__format__(''), str(t))
1838
1839 # check that a derived class's __str__() gets called
1840 class A(self.theclass):
1841 def __str__(self):
1842 return 'A'
1843 a = A(1, 2, 3, 4)
1844 self.assertEqual(a.__format__(''), 'A')
1845
1846 # check that a derived class's strftime gets called
1847 class B(self.theclass):
1848 def strftime(self, format_spec):
1849 return 'B'
1850 b = B(1, 2, 3, 4)
1851 self.assertEqual(b.__format__(''), str(t))
1852
1853 for fmt in ['%H %M %S',
1854 ]:
1855 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1856 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1857 self.assertEqual(b.__format__(fmt), 'B')
1858
Tim Peters2a799bf2002-12-16 20:18:38 +00001859 def test_str(self):
1860 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1861 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1862 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1863 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1864 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1865
1866 def test_repr(self):
1867 name = 'datetime.' + self.theclass.__name__
1868 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1869 "%s(1, 2, 3, 4)" % name)
1870 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1871 "%s(10, 2, 3, 4000)" % name)
1872 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1873 "%s(0, 2, 3, 400000)" % name)
1874 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1875 "%s(12, 2, 3)" % name)
1876 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1877 "%s(23, 15)" % name)
1878
1879 def test_resolution_info(self):
1880 self.assert_(isinstance(self.theclass.min, self.theclass))
1881 self.assert_(isinstance(self.theclass.max, self.theclass))
1882 self.assert_(isinstance(self.theclass.resolution, timedelta))
1883 self.assert_(self.theclass.max > self.theclass.min)
1884
1885 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001886 args = 20, 59, 16, 64**2
1887 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001888 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001889 green = pickler.dumps(orig, proto)
1890 derived = unpickler.loads(green)
1891 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001892
Tim Peters604c0132004-06-07 23:04:33 +00001893 def test_pickling_subclass_time(self):
1894 args = 20, 59, 16, 64**2
1895 orig = SubclassTime(*args)
1896 for pickler, unpickler, proto in pickle_choices:
1897 green = pickler.dumps(orig, proto)
1898 derived = unpickler.loads(green)
1899 self.assertEqual(orig, derived)
1900
Tim Peters2a799bf2002-12-16 20:18:38 +00001901 def test_bool(self):
1902 cls = self.theclass
1903 self.failUnless(cls(1))
1904 self.failUnless(cls(0, 1))
1905 self.failUnless(cls(0, 0, 1))
1906 self.failUnless(cls(0, 0, 0, 1))
1907 self.failUnless(not cls(0))
1908 self.failUnless(not cls())
1909
Tim Peters12bf3392002-12-24 05:41:27 +00001910 def test_replace(self):
1911 cls = self.theclass
1912 args = [1, 2, 3, 4]
1913 base = cls(*args)
1914 self.assertEqual(base, base.replace())
1915
1916 i = 0
1917 for name, newval in (("hour", 5),
1918 ("minute", 6),
1919 ("second", 7),
1920 ("microsecond", 8)):
1921 newargs = args[:]
1922 newargs[i] = newval
1923 expected = cls(*newargs)
1924 got = base.replace(**{name: newval})
1925 self.assertEqual(expected, got)
1926 i += 1
1927
1928 # Out of bounds.
1929 base = cls(1)
1930 self.assertRaises(ValueError, base.replace, hour=24)
1931 self.assertRaises(ValueError, base.replace, minute=-1)
1932 self.assertRaises(ValueError, base.replace, second=100)
1933 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1934
Tim Petersa98924a2003-05-17 05:55:19 +00001935 def test_subclass_time(self):
1936
1937 class C(self.theclass):
1938 theAnswer = 42
1939
1940 def __new__(cls, *args, **kws):
1941 temp = kws.copy()
1942 extra = temp.pop('extra')
1943 result = self.theclass.__new__(cls, *args, **temp)
1944 result.extra = extra
1945 return result
1946
1947 def newmeth(self, start):
1948 return start + self.hour + self.second
1949
1950 args = 4, 5, 6
1951
1952 dt1 = self.theclass(*args)
1953 dt2 = C(*args, **{'extra': 7})
1954
1955 self.assertEqual(dt2.__class__, C)
1956 self.assertEqual(dt2.theAnswer, 42)
1957 self.assertEqual(dt2.extra, 7)
1958 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1959 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1960
Armin Rigof4afb212005-11-07 07:15:48 +00001961 def test_backdoor_resistance(self):
1962 # see TestDate.test_backdoor_resistance().
1963 base = '2:59.0'
1964 for hour_byte in ' ', '9', chr(24), '\xff':
1965 self.assertRaises(TypeError, self.theclass,
1966 hour_byte + base[1:])
1967
Tim Peters855fe882002-12-22 03:43:39 +00001968# A mixin for classes with a tzinfo= argument. Subclasses must define
1969# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001970# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001971class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001972
Tim Petersbad8ff02002-12-30 20:52:32 +00001973 def test_argument_passing(self):
1974 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001975 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001976 class introspective(tzinfo):
1977 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001978 def utcoffset(self, dt):
1979 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001980 dst = utcoffset
1981
1982 obj = cls(1, 2, 3, tzinfo=introspective())
1983
Tim Peters0bf60bd2003-01-08 20:40:01 +00001984 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001985 self.assertEqual(obj.tzname(), expected)
1986
Tim Peters0bf60bd2003-01-08 20:40:01 +00001987 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001988 self.assertEqual(obj.utcoffset(), expected)
1989 self.assertEqual(obj.dst(), expected)
1990
Tim Peters855fe882002-12-22 03:43:39 +00001991 def test_bad_tzinfo_classes(self):
1992 cls = self.theclass
1993 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001994
Tim Peters855fe882002-12-22 03:43:39 +00001995 class NiceTry(object):
1996 def __init__(self): pass
1997 def utcoffset(self, dt): pass
1998 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1999
2000 class BetterTry(tzinfo):
2001 def __init__(self): pass
2002 def utcoffset(self, dt): pass
2003 b = BetterTry()
2004 t = cls(1, 1, 1, tzinfo=b)
2005 self.failUnless(t.tzinfo is b)
2006
2007 def test_utc_offset_out_of_bounds(self):
2008 class Edgy(tzinfo):
2009 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002010 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002011 def utcoffset(self, dt):
2012 return self.offset
2013
2014 cls = self.theclass
2015 for offset, legit in ((-1440, False),
2016 (-1439, True),
2017 (1439, True),
2018 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002019 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002020 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002021 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002022 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002023 else:
2024 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002025 if legit:
2026 aofs = abs(offset)
2027 h, m = divmod(aofs, 60)
2028 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002029 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002030 t = t.timetz()
2031 self.assertEqual(str(t), "01:02:03" + tag)
2032 else:
2033 self.assertRaises(ValueError, str, t)
2034
2035 def test_tzinfo_classes(self):
2036 cls = self.theclass
2037 class C1(tzinfo):
2038 def utcoffset(self, dt): return None
2039 def dst(self, dt): return None
2040 def tzname(self, dt): return None
2041 for t in (cls(1, 1, 1),
2042 cls(1, 1, 1, tzinfo=None),
2043 cls(1, 1, 1, tzinfo=C1())):
2044 self.failUnless(t.utcoffset() is None)
2045 self.failUnless(t.dst() is None)
2046 self.failUnless(t.tzname() is None)
2047
Tim Peters855fe882002-12-22 03:43:39 +00002048 class C3(tzinfo):
2049 def utcoffset(self, dt): return timedelta(minutes=-1439)
2050 def dst(self, dt): return timedelta(minutes=1439)
2051 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002052 t = cls(1, 1, 1, tzinfo=C3())
2053 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2054 self.assertEqual(t.dst(), timedelta(minutes=1439))
2055 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002056
2057 # Wrong types.
2058 class C4(tzinfo):
2059 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002060 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002061 def tzname(self, dt): return 0
2062 t = cls(1, 1, 1, tzinfo=C4())
2063 self.assertRaises(TypeError, t.utcoffset)
2064 self.assertRaises(TypeError, t.dst)
2065 self.assertRaises(TypeError, t.tzname)
2066
2067 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002068 class C6(tzinfo):
2069 def utcoffset(self, dt): return timedelta(hours=-24)
2070 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002071 t = cls(1, 1, 1, tzinfo=C6())
2072 self.assertRaises(ValueError, t.utcoffset)
2073 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002074
2075 # Not a whole number of minutes.
2076 class C7(tzinfo):
2077 def utcoffset(self, dt): return timedelta(seconds=61)
2078 def dst(self, dt): return timedelta(microseconds=-81)
2079 t = cls(1, 1, 1, tzinfo=C7())
2080 self.assertRaises(ValueError, t.utcoffset)
2081 self.assertRaises(ValueError, t.dst)
2082
Tim Peters4c0db782002-12-26 05:01:19 +00002083 def test_aware_compare(self):
2084 cls = self.theclass
2085
Tim Peters60c76e42002-12-27 00:41:11 +00002086 # Ensure that utcoffset() gets ignored if the comparands have
2087 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002088 class OperandDependentOffset(tzinfo):
2089 def utcoffset(self, t):
2090 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002091 # d0 and d1 equal after adjustment
2092 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002093 else:
Tim Peters397301e2003-01-02 21:28:08 +00002094 # d2 off in the weeds
2095 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002096
2097 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2098 d0 = base.replace(minute=3)
2099 d1 = base.replace(minute=9)
2100 d2 = base.replace(minute=11)
2101 for x in d0, d1, d2:
2102 for y in d0, d1, d2:
2103 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002104 expected = cmp(x.minute, y.minute)
2105 self.assertEqual(got, expected)
2106
2107 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002108 # Note that a time can't actually have an operand-depedent offset,
2109 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2110 # so skip this test for time.
2111 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002112 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2113 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2114 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2115 for x in d0, d1, d2:
2116 for y in d0, d1, d2:
2117 got = cmp(x, y)
2118 if (x is d0 or x is d1) and (y is d0 or y is d1):
2119 expected = 0
2120 elif x is y is d2:
2121 expected = 0
2122 elif x is d2:
2123 expected = -1
2124 else:
2125 assert y is d2
2126 expected = 1
2127 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002128
Tim Peters855fe882002-12-22 03:43:39 +00002129
Tim Peters0bf60bd2003-01-08 20:40:01 +00002130# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002131class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002132 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002133
2134 def test_empty(self):
2135 t = self.theclass()
2136 self.assertEqual(t.hour, 0)
2137 self.assertEqual(t.minute, 0)
2138 self.assertEqual(t.second, 0)
2139 self.assertEqual(t.microsecond, 0)
2140 self.failUnless(t.tzinfo is None)
2141
Tim Peters2a799bf2002-12-16 20:18:38 +00002142 def test_zones(self):
2143 est = FixedOffset(-300, "EST", 1)
2144 utc = FixedOffset(0, "UTC", -2)
2145 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002146 t1 = time( 7, 47, tzinfo=est)
2147 t2 = time(12, 47, tzinfo=utc)
2148 t3 = time(13, 47, tzinfo=met)
2149 t4 = time(microsecond=40)
2150 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002151
2152 self.assertEqual(t1.tzinfo, est)
2153 self.assertEqual(t2.tzinfo, utc)
2154 self.assertEqual(t3.tzinfo, met)
2155 self.failUnless(t4.tzinfo is None)
2156 self.assertEqual(t5.tzinfo, utc)
2157
Tim Peters855fe882002-12-22 03:43:39 +00002158 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2159 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2160 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002161 self.failUnless(t4.utcoffset() is None)
2162 self.assertRaises(TypeError, t1.utcoffset, "no args")
2163
2164 self.assertEqual(t1.tzname(), "EST")
2165 self.assertEqual(t2.tzname(), "UTC")
2166 self.assertEqual(t3.tzname(), "MET")
2167 self.failUnless(t4.tzname() is None)
2168 self.assertRaises(TypeError, t1.tzname, "no args")
2169
Tim Peters855fe882002-12-22 03:43:39 +00002170 self.assertEqual(t1.dst(), timedelta(minutes=1))
2171 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2172 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002173 self.failUnless(t4.dst() is None)
2174 self.assertRaises(TypeError, t1.dst, "no args")
2175
2176 self.assertEqual(hash(t1), hash(t2))
2177 self.assertEqual(hash(t1), hash(t3))
2178 self.assertEqual(hash(t2), hash(t3))
2179
2180 self.assertEqual(t1, t2)
2181 self.assertEqual(t1, t3)
2182 self.assertEqual(t2, t3)
2183 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2184 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2185 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2186
2187 self.assertEqual(str(t1), "07:47:00-05:00")
2188 self.assertEqual(str(t2), "12:47:00+00:00")
2189 self.assertEqual(str(t3), "13:47:00+01:00")
2190 self.assertEqual(str(t4), "00:00:00.000040")
2191 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2192
2193 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2194 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2195 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2196 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2197 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2198
Tim Peters0bf60bd2003-01-08 20:40:01 +00002199 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002200 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2201 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2202 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2203 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2204 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2205
2206 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2207 "07:47:00 %Z=EST %z=-0500")
2208 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2209 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2210
2211 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002212 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002213 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2214 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2215
Tim Petersb92bb712002-12-21 17:44:07 +00002216 # Check that an invalid tzname result raises an exception.
2217 class Badtzname(tzinfo):
2218 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002219 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002220 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2221 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002222
2223 def test_hash_edge_cases(self):
2224 # Offsets that overflow a basic time.
2225 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2226 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2227 self.assertEqual(hash(t1), hash(t2))
2228
2229 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2230 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2231 self.assertEqual(hash(t1), hash(t2))
2232
Tim Peters2a799bf2002-12-16 20:18:38 +00002233 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002234 # Try one without a tzinfo.
2235 args = 20, 59, 16, 64**2
2236 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002237 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002238 green = pickler.dumps(orig, proto)
2239 derived = unpickler.loads(green)
2240 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002241
2242 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002243 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002244 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002245 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002246 green = pickler.dumps(orig, proto)
2247 derived = unpickler.loads(green)
2248 self.assertEqual(orig, derived)
2249 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2250 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2251 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002252
2253 def test_more_bool(self):
2254 # Test cases with non-None tzinfo.
2255 cls = self.theclass
2256
2257 t = cls(0, tzinfo=FixedOffset(-300, ""))
2258 self.failUnless(t)
2259
2260 t = cls(5, tzinfo=FixedOffset(-300, ""))
2261 self.failUnless(t)
2262
2263 t = cls(5, tzinfo=FixedOffset(300, ""))
2264 self.failUnless(not t)
2265
2266 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2267 self.failUnless(not t)
2268
2269 # Mostly ensuring this doesn't overflow internally.
2270 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2271 self.failUnless(t)
2272
2273 # But this should yield a value error -- the utcoffset is bogus.
2274 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2275 self.assertRaises(ValueError, lambda: bool(t))
2276
2277 # Likewise.
2278 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2279 self.assertRaises(ValueError, lambda: bool(t))
2280
Tim Peters12bf3392002-12-24 05:41:27 +00002281 def test_replace(self):
2282 cls = self.theclass
2283 z100 = FixedOffset(100, "+100")
2284 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2285 args = [1, 2, 3, 4, z100]
2286 base = cls(*args)
2287 self.assertEqual(base, base.replace())
2288
2289 i = 0
2290 for name, newval in (("hour", 5),
2291 ("minute", 6),
2292 ("second", 7),
2293 ("microsecond", 8),
2294 ("tzinfo", zm200)):
2295 newargs = args[:]
2296 newargs[i] = newval
2297 expected = cls(*newargs)
2298 got = base.replace(**{name: newval})
2299 self.assertEqual(expected, got)
2300 i += 1
2301
2302 # Ensure we can get rid of a tzinfo.
2303 self.assertEqual(base.tzname(), "+100")
2304 base2 = base.replace(tzinfo=None)
2305 self.failUnless(base2.tzinfo is None)
2306 self.failUnless(base2.tzname() is None)
2307
2308 # Ensure we can add one.
2309 base3 = base2.replace(tzinfo=z100)
2310 self.assertEqual(base, base3)
2311 self.failUnless(base.tzinfo is base3.tzinfo)
2312
2313 # Out of bounds.
2314 base = cls(1)
2315 self.assertRaises(ValueError, base.replace, hour=24)
2316 self.assertRaises(ValueError, base.replace, minute=-1)
2317 self.assertRaises(ValueError, base.replace, second=100)
2318 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2319
Tim Peters60c76e42002-12-27 00:41:11 +00002320 def test_mixed_compare(self):
2321 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002322 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002323 self.assertEqual(t1, t2)
2324 t2 = t2.replace(tzinfo=None)
2325 self.assertEqual(t1, t2)
2326 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2327 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002328 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2329 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002330
Tim Peters0bf60bd2003-01-08 20:40:01 +00002331 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002332 class Varies(tzinfo):
2333 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002334 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002335 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002336 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002337 return self.offset
2338
2339 v = Varies()
2340 t1 = t2.replace(tzinfo=v)
2341 t2 = t2.replace(tzinfo=v)
2342 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2343 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2344 self.assertEqual(t1, t2)
2345
2346 # But if they're not identical, it isn't ignored.
2347 t2 = t2.replace(tzinfo=Varies())
2348 self.failUnless(t1 < t2) # t1's offset counter still going up
2349
Tim Petersa98924a2003-05-17 05:55:19 +00002350 def test_subclass_timetz(self):
2351
2352 class C(self.theclass):
2353 theAnswer = 42
2354
2355 def __new__(cls, *args, **kws):
2356 temp = kws.copy()
2357 extra = temp.pop('extra')
2358 result = self.theclass.__new__(cls, *args, **temp)
2359 result.extra = extra
2360 return result
2361
2362 def newmeth(self, start):
2363 return start + self.hour + self.second
2364
2365 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2366
2367 dt1 = self.theclass(*args)
2368 dt2 = C(*args, **{'extra': 7})
2369
2370 self.assertEqual(dt2.__class__, C)
2371 self.assertEqual(dt2.theAnswer, 42)
2372 self.assertEqual(dt2.extra, 7)
2373 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2374 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2375
Tim Peters4c0db782002-12-26 05:01:19 +00002376
Tim Peters0bf60bd2003-01-08 20:40:01 +00002377# Testing datetime objects with a non-None tzinfo.
2378
Collin Winterc2898c52007-04-25 17:29:52 +00002379class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002380 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002381
2382 def test_trivial(self):
2383 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2384 self.assertEqual(dt.year, 1)
2385 self.assertEqual(dt.month, 2)
2386 self.assertEqual(dt.day, 3)
2387 self.assertEqual(dt.hour, 4)
2388 self.assertEqual(dt.minute, 5)
2389 self.assertEqual(dt.second, 6)
2390 self.assertEqual(dt.microsecond, 7)
2391 self.assertEqual(dt.tzinfo, None)
2392
2393 def test_even_more_compare(self):
2394 # The test_compare() and test_more_compare() inherited from TestDate
2395 # and TestDateTime covered non-tzinfo cases.
2396
2397 # Smallest possible after UTC adjustment.
2398 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2399 # Largest possible after UTC adjustment.
2400 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2401 tzinfo=FixedOffset(-1439, ""))
2402
2403 # Make sure those compare correctly, and w/o overflow.
2404 self.failUnless(t1 < t2)
2405 self.failUnless(t1 != t2)
2406 self.failUnless(t2 > t1)
2407
2408 self.failUnless(t1 == t1)
2409 self.failUnless(t2 == t2)
2410
2411 # Equal afer adjustment.
2412 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2413 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2414 self.assertEqual(t1, t2)
2415
2416 # Change t1 not to subtract a minute, and t1 should be larger.
2417 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2418 self.failUnless(t1 > t2)
2419
2420 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2421 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2422 self.failUnless(t1 < t2)
2423
2424 # Back to the original t1, but make seconds resolve it.
2425 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2426 second=1)
2427 self.failUnless(t1 > t2)
2428
2429 # Likewise, but make microseconds resolve it.
2430 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2431 microsecond=1)
2432 self.failUnless(t1 > t2)
2433
2434 # Make t2 naive and it should fail.
2435 t2 = self.theclass.min
2436 self.assertRaises(TypeError, lambda: t1 == t2)
2437 self.assertEqual(t2, t2)
2438
2439 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2440 class Naive(tzinfo):
2441 def utcoffset(self, dt): return None
2442 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2443 self.assertRaises(TypeError, lambda: t1 == t2)
2444 self.assertEqual(t2, t2)
2445
2446 # OTOH, it's OK to compare two of these mixing the two ways of being
2447 # naive.
2448 t1 = self.theclass(5, 6, 7)
2449 self.assertEqual(t1, t2)
2450
2451 # Try a bogus uctoffset.
2452 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002453 def utcoffset(self, dt):
2454 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002455 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2456 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002457 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002458
Tim Peters2a799bf2002-12-16 20:18:38 +00002459 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002460 # Try one without a tzinfo.
2461 args = 6, 7, 23, 20, 59, 1, 64**2
2462 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002463 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002464 green = pickler.dumps(orig, proto)
2465 derived = unpickler.loads(green)
2466 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002467
2468 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002469 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002470 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002471 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002472 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002473 green = pickler.dumps(orig, proto)
2474 derived = unpickler.loads(green)
2475 self.assertEqual(orig, derived)
2476 self.failUnless(isinstance(derived.tzinfo,
2477 PicklableFixedOffset))
2478 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2479 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002480
2481 def test_extreme_hashes(self):
2482 # If an attempt is made to hash these via subtracting the offset
2483 # then hashing a datetime object, OverflowError results. The
2484 # Python implementation used to blow up here.
2485 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2486 hash(t)
2487 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2488 tzinfo=FixedOffset(-1439, ""))
2489 hash(t)
2490
2491 # OTOH, an OOB offset should blow up.
2492 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2493 self.assertRaises(ValueError, hash, t)
2494
2495 def test_zones(self):
2496 est = FixedOffset(-300, "EST")
2497 utc = FixedOffset(0, "UTC")
2498 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002499 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2500 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2501 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002502 self.assertEqual(t1.tzinfo, est)
2503 self.assertEqual(t2.tzinfo, utc)
2504 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002505 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2506 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2507 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002508 self.assertEqual(t1.tzname(), "EST")
2509 self.assertEqual(t2.tzname(), "UTC")
2510 self.assertEqual(t3.tzname(), "MET")
2511 self.assertEqual(hash(t1), hash(t2))
2512 self.assertEqual(hash(t1), hash(t3))
2513 self.assertEqual(hash(t2), hash(t3))
2514 self.assertEqual(t1, t2)
2515 self.assertEqual(t1, t3)
2516 self.assertEqual(t2, t3)
2517 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2518 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2519 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002520 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002521 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2522 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2523 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2524
2525 def test_combine(self):
2526 met = FixedOffset(60, "MET")
2527 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002528 tz = time(18, 45, 3, 1234, tzinfo=met)
2529 dt = datetime.combine(d, tz)
2530 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002531 tzinfo=met))
2532
2533 def test_extract(self):
2534 met = FixedOffset(60, "MET")
2535 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2536 self.assertEqual(dt.date(), date(2002, 3, 4))
2537 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002538 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002539
2540 def test_tz_aware_arithmetic(self):
2541 import random
2542
2543 now = self.theclass.now()
2544 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002545 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002546 nowaware = self.theclass.combine(now.date(), timeaware)
2547 self.failUnless(nowaware.tzinfo is tz55)
2548 self.assertEqual(nowaware.timetz(), timeaware)
2549
2550 # Can't mix aware and non-aware.
2551 self.assertRaises(TypeError, lambda: now - nowaware)
2552 self.assertRaises(TypeError, lambda: nowaware - now)
2553
Tim Peters0bf60bd2003-01-08 20:40:01 +00002554 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002555 self.assertRaises(TypeError, lambda: now + nowaware)
2556 self.assertRaises(TypeError, lambda: nowaware + now)
2557 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2558
2559 # Subtracting should yield 0.
2560 self.assertEqual(now - now, timedelta(0))
2561 self.assertEqual(nowaware - nowaware, timedelta(0))
2562
2563 # Adding a delta should preserve tzinfo.
2564 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2565 nowawareplus = nowaware + delta
2566 self.failUnless(nowaware.tzinfo is tz55)
2567 nowawareplus2 = delta + nowaware
2568 self.failUnless(nowawareplus2.tzinfo is tz55)
2569 self.assertEqual(nowawareplus, nowawareplus2)
2570
2571 # that - delta should be what we started with, and that - what we
2572 # started with should be delta.
2573 diff = nowawareplus - delta
2574 self.failUnless(diff.tzinfo is tz55)
2575 self.assertEqual(nowaware, diff)
2576 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2577 self.assertEqual(nowawareplus - nowaware, delta)
2578
2579 # Make up a random timezone.
2580 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002581 # Attach it to nowawareplus.
2582 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002583 self.failUnless(nowawareplus.tzinfo is tzr)
2584 # Make sure the difference takes the timezone adjustments into account.
2585 got = nowaware - nowawareplus
2586 # Expected: (nowaware base - nowaware offset) -
2587 # (nowawareplus base - nowawareplus offset) =
2588 # (nowaware base - nowawareplus base) +
2589 # (nowawareplus offset - nowaware offset) =
2590 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002591 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002592 self.assertEqual(got, expected)
2593
2594 # Try max possible difference.
2595 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2596 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2597 tzinfo=FixedOffset(-1439, "max"))
2598 maxdiff = max - min
2599 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2600 timedelta(minutes=2*1439))
2601
2602 def test_tzinfo_now(self):
2603 meth = self.theclass.now
2604 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2605 base = meth()
2606 # Try with and without naming the keyword.
2607 off42 = FixedOffset(42, "42")
2608 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002609 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002610 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002611 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002612 # Bad argument with and w/o naming the keyword.
2613 self.assertRaises(TypeError, meth, 16)
2614 self.assertRaises(TypeError, meth, tzinfo=16)
2615 # Bad keyword name.
2616 self.assertRaises(TypeError, meth, tinfo=off42)
2617 # Too many args.
2618 self.assertRaises(TypeError, meth, off42, off42)
2619
Tim Peters10cadce2003-01-23 19:58:02 +00002620 # We don't know which time zone we're in, and don't have a tzinfo
2621 # class to represent it, so seeing whether a tz argument actually
2622 # does a conversion is tricky.
2623 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2624 utc = FixedOffset(0, "utc", 0)
2625 for dummy in range(3):
2626 now = datetime.now(weirdtz)
2627 self.failUnless(now.tzinfo is weirdtz)
2628 utcnow = datetime.utcnow().replace(tzinfo=utc)
2629 now2 = utcnow.astimezone(weirdtz)
2630 if abs(now - now2) < timedelta(seconds=30):
2631 break
2632 # Else the code is broken, or more than 30 seconds passed between
2633 # calls; assuming the latter, just try again.
2634 else:
2635 # Three strikes and we're out.
2636 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2637
Tim Peters2a799bf2002-12-16 20:18:38 +00002638 def test_tzinfo_fromtimestamp(self):
2639 import time
2640 meth = self.theclass.fromtimestamp
2641 ts = time.time()
2642 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2643 base = meth(ts)
2644 # Try with and without naming the keyword.
2645 off42 = FixedOffset(42, "42")
2646 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002647 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002648 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002649 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002650 # Bad argument with and w/o naming the keyword.
2651 self.assertRaises(TypeError, meth, ts, 16)
2652 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2653 # Bad keyword name.
2654 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2655 # Too many args.
2656 self.assertRaises(TypeError, meth, ts, off42, off42)
2657 # Too few args.
2658 self.assertRaises(TypeError, meth)
2659
Tim Peters2a44a8d2003-01-23 20:53:10 +00002660 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002661 timestamp = 1000000000
2662 utcdatetime = datetime.utcfromtimestamp(timestamp)
2663 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2664 # But on some flavor of Mac, it's nowhere near that. So we can't have
2665 # any idea here what time that actually is, we can only test that
2666 # relative changes match.
2667 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2668 tz = FixedOffset(utcoffset, "tz", 0)
2669 expected = utcdatetime + utcoffset
2670 got = datetime.fromtimestamp(timestamp, tz)
2671 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002672
Tim Peters2a799bf2002-12-16 20:18:38 +00002673 def test_tzinfo_utcnow(self):
2674 meth = self.theclass.utcnow
2675 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2676 base = meth()
2677 # Try with and without naming the keyword; for whatever reason,
2678 # utcnow() doesn't accept a tzinfo argument.
2679 off42 = FixedOffset(42, "42")
2680 self.assertRaises(TypeError, meth, off42)
2681 self.assertRaises(TypeError, meth, tzinfo=off42)
2682
2683 def test_tzinfo_utcfromtimestamp(self):
2684 import time
2685 meth = self.theclass.utcfromtimestamp
2686 ts = time.time()
2687 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2688 base = meth(ts)
2689 # Try with and without naming the keyword; for whatever reason,
2690 # utcfromtimestamp() doesn't accept a tzinfo argument.
2691 off42 = FixedOffset(42, "42")
2692 self.assertRaises(TypeError, meth, ts, off42)
2693 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2694
2695 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002696 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002697 # DST flag.
2698 class DST(tzinfo):
2699 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002700 if isinstance(dstvalue, int):
2701 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002702 self.dstvalue = dstvalue
2703 def dst(self, dt):
2704 return self.dstvalue
2705
2706 cls = self.theclass
2707 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2708 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2709 t = d.timetuple()
2710 self.assertEqual(1, t.tm_year)
2711 self.assertEqual(1, t.tm_mon)
2712 self.assertEqual(1, t.tm_mday)
2713 self.assertEqual(10, t.tm_hour)
2714 self.assertEqual(20, t.tm_min)
2715 self.assertEqual(30, t.tm_sec)
2716 self.assertEqual(0, t.tm_wday)
2717 self.assertEqual(1, t.tm_yday)
2718 self.assertEqual(flag, t.tm_isdst)
2719
2720 # dst() returns wrong type.
2721 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2722
2723 # dst() at the edge.
2724 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2725 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2726
2727 # dst() out of range.
2728 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2729 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2730
2731 def test_utctimetuple(self):
2732 class DST(tzinfo):
2733 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002734 if isinstance(dstvalue, int):
2735 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002736 self.dstvalue = dstvalue
2737 def dst(self, dt):
2738 return self.dstvalue
2739
2740 cls = self.theclass
2741 # This can't work: DST didn't implement utcoffset.
2742 self.assertRaises(NotImplementedError,
2743 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2744
2745 class UOFS(DST):
2746 def __init__(self, uofs, dofs=None):
2747 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002748 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002749 def utcoffset(self, dt):
2750 return self.uofs
2751
2752 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2753 # in effect for a UTC time.
2754 for dstvalue in -33, 33, 0, None:
2755 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2756 t = d.utctimetuple()
2757 self.assertEqual(d.year, t.tm_year)
2758 self.assertEqual(d.month, t.tm_mon)
2759 self.assertEqual(d.day, t.tm_mday)
2760 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2761 self.assertEqual(13, t.tm_min)
2762 self.assertEqual(d.second, t.tm_sec)
2763 self.assertEqual(d.weekday(), t.tm_wday)
2764 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2765 t.tm_yday)
2766 self.assertEqual(0, t.tm_isdst)
2767
2768 # At the edges, UTC adjustment can normalize into years out-of-range
2769 # for a datetime object. Ensure that a correct timetuple is
2770 # created anyway.
2771 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2772 # That goes back 1 minute less than a full day.
2773 t = tiny.utctimetuple()
2774 self.assertEqual(t.tm_year, MINYEAR-1)
2775 self.assertEqual(t.tm_mon, 12)
2776 self.assertEqual(t.tm_mday, 31)
2777 self.assertEqual(t.tm_hour, 0)
2778 self.assertEqual(t.tm_min, 1)
2779 self.assertEqual(t.tm_sec, 37)
2780 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2781 self.assertEqual(t.tm_isdst, 0)
2782
2783 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2784 # That goes forward 1 minute less than a full day.
2785 t = huge.utctimetuple()
2786 self.assertEqual(t.tm_year, MAXYEAR+1)
2787 self.assertEqual(t.tm_mon, 1)
2788 self.assertEqual(t.tm_mday, 1)
2789 self.assertEqual(t.tm_hour, 23)
2790 self.assertEqual(t.tm_min, 58)
2791 self.assertEqual(t.tm_sec, 37)
2792 self.assertEqual(t.tm_yday, 1)
2793 self.assertEqual(t.tm_isdst, 0)
2794
2795 def test_tzinfo_isoformat(self):
2796 zero = FixedOffset(0, "+00:00")
2797 plus = FixedOffset(220, "+03:40")
2798 minus = FixedOffset(-231, "-03:51")
2799 unknown = FixedOffset(None, "")
2800
2801 cls = self.theclass
2802 datestr = '0001-02-03'
2803 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002804 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002805 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2806 timestr = '04:05:59' + (us and '.987001' or '')
2807 ofsstr = ofs is not None and d.tzname() or ''
2808 tailstr = timestr + ofsstr
2809 iso = d.isoformat()
2810 self.assertEqual(iso, datestr + 'T' + tailstr)
2811 self.assertEqual(iso, d.isoformat('T'))
2812 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2813 self.assertEqual(str(d), datestr + ' ' + tailstr)
2814
Tim Peters12bf3392002-12-24 05:41:27 +00002815 def test_replace(self):
2816 cls = self.theclass
2817 z100 = FixedOffset(100, "+100")
2818 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2819 args = [1, 2, 3, 4, 5, 6, 7, z100]
2820 base = cls(*args)
2821 self.assertEqual(base, base.replace())
2822
2823 i = 0
2824 for name, newval in (("year", 2),
2825 ("month", 3),
2826 ("day", 4),
2827 ("hour", 5),
2828 ("minute", 6),
2829 ("second", 7),
2830 ("microsecond", 8),
2831 ("tzinfo", zm200)):
2832 newargs = args[:]
2833 newargs[i] = newval
2834 expected = cls(*newargs)
2835 got = base.replace(**{name: newval})
2836 self.assertEqual(expected, got)
2837 i += 1
2838
2839 # Ensure we can get rid of a tzinfo.
2840 self.assertEqual(base.tzname(), "+100")
2841 base2 = base.replace(tzinfo=None)
2842 self.failUnless(base2.tzinfo is None)
2843 self.failUnless(base2.tzname() is None)
2844
2845 # Ensure we can add one.
2846 base3 = base2.replace(tzinfo=z100)
2847 self.assertEqual(base, base3)
2848 self.failUnless(base.tzinfo is base3.tzinfo)
2849
2850 # Out of bounds.
2851 base = cls(2000, 2, 29)
2852 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002853
Tim Peters80475bb2002-12-25 07:40:55 +00002854 def test_more_astimezone(self):
2855 # The inherited test_astimezone covered some trivial and error cases.
2856 fnone = FixedOffset(None, "None")
2857 f44m = FixedOffset(44, "44")
2858 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2859
Tim Peters10cadce2003-01-23 19:58:02 +00002860 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002861 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002862 # Replacing with degenerate tzinfo raises an exception.
2863 self.assertRaises(ValueError, dt.astimezone, fnone)
2864 # Ditto with None tz.
2865 self.assertRaises(TypeError, dt.astimezone, None)
2866 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002867 x = dt.astimezone(dt.tzinfo)
2868 self.failUnless(x.tzinfo is f44m)
2869 self.assertEqual(x.date(), dt.date())
2870 self.assertEqual(x.time(), dt.time())
2871
2872 # Replacing with different tzinfo does adjust.
2873 got = dt.astimezone(fm5h)
2874 self.failUnless(got.tzinfo is fm5h)
2875 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2876 expected = dt - dt.utcoffset() # in effect, convert to UTC
2877 expected += fm5h.utcoffset(dt) # and from there to local time
2878 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2879 self.assertEqual(got.date(), expected.date())
2880 self.assertEqual(got.time(), expected.time())
2881 self.assertEqual(got.timetz(), expected.timetz())
2882 self.failUnless(got.tzinfo is expected.tzinfo)
2883 self.assertEqual(got, expected)
2884
Tim Peters4c0db782002-12-26 05:01:19 +00002885 def test_aware_subtract(self):
2886 cls = self.theclass
2887
Tim Peters60c76e42002-12-27 00:41:11 +00002888 # Ensure that utcoffset() is ignored when the operands have the
2889 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002890 class OperandDependentOffset(tzinfo):
2891 def utcoffset(self, t):
2892 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002893 # d0 and d1 equal after adjustment
2894 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002895 else:
Tim Peters397301e2003-01-02 21:28:08 +00002896 # d2 off in the weeds
2897 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002898
2899 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2900 d0 = base.replace(minute=3)
2901 d1 = base.replace(minute=9)
2902 d2 = base.replace(minute=11)
2903 for x in d0, d1, d2:
2904 for y in d0, d1, d2:
2905 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002906 expected = timedelta(minutes=x.minute - y.minute)
2907 self.assertEqual(got, expected)
2908
2909 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2910 # ignored.
2911 base = cls(8, 9, 10, 11, 12, 13, 14)
2912 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2913 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2914 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2915 for x in d0, d1, d2:
2916 for y in d0, d1, d2:
2917 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002918 if (x is d0 or x is d1) and (y is d0 or y is d1):
2919 expected = timedelta(0)
2920 elif x is y is d2:
2921 expected = timedelta(0)
2922 elif x is d2:
2923 expected = timedelta(minutes=(11-59)-0)
2924 else:
2925 assert y is d2
2926 expected = timedelta(minutes=0-(11-59))
2927 self.assertEqual(got, expected)
2928
Tim Peters60c76e42002-12-27 00:41:11 +00002929 def test_mixed_compare(self):
2930 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002931 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002932 self.assertEqual(t1, t2)
2933 t2 = t2.replace(tzinfo=None)
2934 self.assertEqual(t1, t2)
2935 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2936 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002937 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2938 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002939
Tim Peters0bf60bd2003-01-08 20:40:01 +00002940 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002941 class Varies(tzinfo):
2942 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002943 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002944 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002945 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002946 return self.offset
2947
2948 v = Varies()
2949 t1 = t2.replace(tzinfo=v)
2950 t2 = t2.replace(tzinfo=v)
2951 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2952 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2953 self.assertEqual(t1, t2)
2954
2955 # But if they're not identical, it isn't ignored.
2956 t2 = t2.replace(tzinfo=Varies())
2957 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002958
Tim Petersa98924a2003-05-17 05:55:19 +00002959 def test_subclass_datetimetz(self):
2960
2961 class C(self.theclass):
2962 theAnswer = 42
2963
2964 def __new__(cls, *args, **kws):
2965 temp = kws.copy()
2966 extra = temp.pop('extra')
2967 result = self.theclass.__new__(cls, *args, **temp)
2968 result.extra = extra
2969 return result
2970
2971 def newmeth(self, start):
2972 return start + self.hour + self.year
2973
2974 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2975
2976 dt1 = self.theclass(*args)
2977 dt2 = C(*args, **{'extra': 7})
2978
2979 self.assertEqual(dt2.__class__, C)
2980 self.assertEqual(dt2.theAnswer, 42)
2981 self.assertEqual(dt2.extra, 7)
2982 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2983 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2984
Tim Peters621818b2002-12-29 23:44:49 +00002985# Pain to set up DST-aware tzinfo classes.
2986
2987def first_sunday_on_or_after(dt):
2988 days_to_go = 6 - dt.weekday()
2989 if days_to_go:
2990 dt += timedelta(days_to_go)
2991 return dt
2992
2993ZERO = timedelta(0)
2994HOUR = timedelta(hours=1)
2995DAY = timedelta(days=1)
2996# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2997DSTSTART = datetime(1, 4, 1, 2)
2998# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002999# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3000# being standard time on that day, there is no spelling in local time of
3001# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3002DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003003
3004class USTimeZone(tzinfo):
3005
3006 def __init__(self, hours, reprname, stdname, dstname):
3007 self.stdoffset = timedelta(hours=hours)
3008 self.reprname = reprname
3009 self.stdname = stdname
3010 self.dstname = dstname
3011
3012 def __repr__(self):
3013 return self.reprname
3014
3015 def tzname(self, dt):
3016 if self.dst(dt):
3017 return self.dstname
3018 else:
3019 return self.stdname
3020
3021 def utcoffset(self, dt):
3022 return self.stdoffset + self.dst(dt)
3023
3024 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003025 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003026 # An exception instead may be sensible here, in one or more of
3027 # the cases.
3028 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003029 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003030
3031 # Find first Sunday in April.
3032 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3033 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3034
3035 # Find last Sunday in October.
3036 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3037 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3038
Tim Peters621818b2002-12-29 23:44:49 +00003039 # Can't compare naive to aware objects, so strip the timezone from
3040 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003041 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003042 return HOUR
3043 else:
3044 return ZERO
3045
Tim Peters521fc152002-12-31 17:36:56 +00003046Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3047Central = USTimeZone(-6, "Central", "CST", "CDT")
3048Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3049Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003050utc_real = FixedOffset(0, "UTC", 0)
3051# For better test coverage, we want another flavor of UTC that's west of
3052# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003053utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003054
3055class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003056 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003057 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003058 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003059
Tim Peters0bf60bd2003-01-08 20:40:01 +00003060 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003061
Tim Peters521fc152002-12-31 17:36:56 +00003062 # Check a time that's inside DST.
3063 def checkinside(self, dt, tz, utc, dston, dstoff):
3064 self.assertEqual(dt.dst(), HOUR)
3065
3066 # Conversion to our own timezone is always an identity.
3067 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003068
3069 asutc = dt.astimezone(utc)
3070 there_and_back = asutc.astimezone(tz)
3071
3072 # Conversion to UTC and back isn't always an identity here,
3073 # because there are redundant spellings (in local time) of
3074 # UTC time when DST begins: the clock jumps from 1:59:59
3075 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3076 # make sense then. The classes above treat 2:MM:SS as
3077 # daylight time then (it's "after 2am"), really an alias
3078 # for 1:MM:SS standard time. The latter form is what
3079 # conversion back from UTC produces.
3080 if dt.date() == dston.date() and dt.hour == 2:
3081 # We're in the redundant hour, and coming back from
3082 # UTC gives the 1:MM:SS standard-time spelling.
3083 self.assertEqual(there_and_back + HOUR, dt)
3084 # Although during was considered to be in daylight
3085 # time, there_and_back is not.
3086 self.assertEqual(there_and_back.dst(), ZERO)
3087 # They're the same times in UTC.
3088 self.assertEqual(there_and_back.astimezone(utc),
3089 dt.astimezone(utc))
3090 else:
3091 # We're not in the redundant hour.
3092 self.assertEqual(dt, there_and_back)
3093
Tim Peters327098a2003-01-20 22:54:38 +00003094 # Because we have a redundant spelling when DST begins, there is
3095 # (unforunately) an hour when DST ends that can't be spelled at all in
3096 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3097 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3098 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3099 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3100 # expressed in local time. Nevertheless, we want conversion back
3101 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003102 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003103 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003104 if dt.date() == dstoff.date() and dt.hour == 0:
3105 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003106 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003107 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3108 nexthour_utc += HOUR
3109 nexthour_tz = nexthour_utc.astimezone(tz)
3110 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003111 else:
Tim Peters327098a2003-01-20 22:54:38 +00003112 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003113
3114 # Check a time that's outside DST.
3115 def checkoutside(self, dt, tz, utc):
3116 self.assertEqual(dt.dst(), ZERO)
3117
3118 # Conversion to our own timezone is always an identity.
3119 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003120
3121 # Converting to UTC and back is an identity too.
3122 asutc = dt.astimezone(utc)
3123 there_and_back = asutc.astimezone(tz)
3124 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003125
Tim Peters1024bf82002-12-30 17:09:40 +00003126 def convert_between_tz_and_utc(self, tz, utc):
3127 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003128 # Because 1:MM on the day DST ends is taken as being standard time,
3129 # there is no spelling in tz for the last hour of daylight time.
3130 # For purposes of the test, the last hour of DST is 0:MM, which is
3131 # taken as being daylight time (and 1:MM is taken as being standard
3132 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003133 dstoff = self.dstoff.replace(tzinfo=tz)
3134 for delta in (timedelta(weeks=13),
3135 DAY,
3136 HOUR,
3137 timedelta(minutes=1),
3138 timedelta(microseconds=1)):
3139
Tim Peters521fc152002-12-31 17:36:56 +00003140 self.checkinside(dston, tz, utc, dston, dstoff)
3141 for during in dston + delta, dstoff - delta:
3142 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003143
Tim Peters521fc152002-12-31 17:36:56 +00003144 self.checkoutside(dstoff, tz, utc)
3145 for outside in dston - delta, dstoff + delta:
3146 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003147
Tim Peters621818b2002-12-29 23:44:49 +00003148 def test_easy(self):
3149 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003150 self.convert_between_tz_and_utc(Eastern, utc_real)
3151 self.convert_between_tz_and_utc(Pacific, utc_real)
3152 self.convert_between_tz_and_utc(Eastern, utc_fake)
3153 self.convert_between_tz_and_utc(Pacific, utc_fake)
3154 # The next is really dancing near the edge. It works because
3155 # Pacific and Eastern are far enough apart that their "problem
3156 # hours" don't overlap.
3157 self.convert_between_tz_and_utc(Eastern, Pacific)
3158 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003159 # OTOH, these fail! Don't enable them. The difficulty is that
3160 # the edge case tests assume that every hour is representable in
3161 # the "utc" class. This is always true for a fixed-offset tzinfo
3162 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3163 # For these adjacent DST-aware time zones, the range of time offsets
3164 # tested ends up creating hours in the one that aren't representable
3165 # in the other. For the same reason, we would see failures in the
3166 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3167 # offset deltas in convert_between_tz_and_utc().
3168 #
3169 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3170 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003171
Tim Petersf3615152003-01-01 21:51:37 +00003172 def test_tricky(self):
3173 # 22:00 on day before daylight starts.
3174 fourback = self.dston - timedelta(hours=4)
3175 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003176 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003177 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3178 # 2", we should get the 3 spelling.
3179 # If we plug 22:00 the day before into Eastern, it "looks like std
3180 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3181 # to 22:00 lands on 2:00, which makes no sense in local time (the
3182 # local clock jumps from 1 to 3). The point here is to make sure we
3183 # get the 3 spelling.
3184 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003185 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003186 self.assertEqual(expected, got)
3187
3188 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3189 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003190 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003191 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3192 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3193 # spelling.
3194 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003195 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003196 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003197
Tim Petersadf64202003-01-04 06:03:15 +00003198 # Now on the day DST ends, we want "repeat an hour" behavior.
3199 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3200 # EST 23:MM 0:MM 1:MM 2:MM
3201 # EDT 0:MM 1:MM 2:MM 3:MM
3202 # wall 0:MM 1:MM 1:MM 2:MM against these
3203 for utc in utc_real, utc_fake:
3204 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003205 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003206 # Convert that to UTC.
3207 first_std_hour -= tz.utcoffset(None)
3208 # Adjust for possibly fake UTC.
3209 asutc = first_std_hour + utc.utcoffset(None)
3210 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3211 # tz=Eastern.
3212 asutcbase = asutc.replace(tzinfo=utc)
3213 for tzhour in (0, 1, 1, 2):
3214 expectedbase = self.dstoff.replace(hour=tzhour)
3215 for minute in 0, 30, 59:
3216 expected = expectedbase.replace(minute=minute)
3217 asutc = asutcbase.replace(minute=minute)
3218 astz = asutc.astimezone(tz)
3219 self.assertEqual(astz.replace(tzinfo=None), expected)
3220 asutcbase += HOUR
3221
3222
Tim Peters710fb152003-01-02 19:35:54 +00003223 def test_bogus_dst(self):
3224 class ok(tzinfo):
3225 def utcoffset(self, dt): return HOUR
3226 def dst(self, dt): return HOUR
3227
3228 now = self.theclass.now().replace(tzinfo=utc_real)
3229 # Doesn't blow up.
3230 now.astimezone(ok())
3231
3232 # Does blow up.
3233 class notok(ok):
3234 def dst(self, dt): return None
3235 self.assertRaises(ValueError, now.astimezone, notok())
3236
Tim Peters52dcce22003-01-23 16:36:11 +00003237 def test_fromutc(self):
3238 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3239 now = datetime.utcnow().replace(tzinfo=utc_real)
3240 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3241 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3242 enow = Eastern.fromutc(now) # doesn't blow up
3243 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3244 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3245 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3246
3247 # Always converts UTC to standard time.
3248 class FauxUSTimeZone(USTimeZone):
3249 def fromutc(self, dt):
3250 return dt + self.stdoffset
3251 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3252
3253 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3254 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3255 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3256
3257 # Check around DST start.
3258 start = self.dston.replace(hour=4, tzinfo=Eastern)
3259 fstart = start.replace(tzinfo=FEastern)
3260 for wall in 23, 0, 1, 3, 4, 5:
3261 expected = start.replace(hour=wall)
3262 if wall == 23:
3263 expected -= timedelta(days=1)
3264 got = Eastern.fromutc(start)
3265 self.assertEqual(expected, got)
3266
3267 expected = fstart + FEastern.stdoffset
3268 got = FEastern.fromutc(fstart)
3269 self.assertEqual(expected, got)
3270
3271 # Ensure astimezone() calls fromutc() too.
3272 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3273 self.assertEqual(expected, got)
3274
3275 start += HOUR
3276 fstart += HOUR
3277
3278 # Check around DST end.
3279 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3280 fstart = start.replace(tzinfo=FEastern)
3281 for wall in 0, 1, 1, 2, 3, 4:
3282 expected = start.replace(hour=wall)
3283 got = Eastern.fromutc(start)
3284 self.assertEqual(expected, got)
3285
3286 expected = fstart + FEastern.stdoffset
3287 got = FEastern.fromutc(fstart)
3288 self.assertEqual(expected, got)
3289
3290 # Ensure astimezone() calls fromutc() too.
3291 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3292 self.assertEqual(expected, got)
3293
3294 start += HOUR
3295 fstart += HOUR
3296
Tim Peters710fb152003-01-02 19:35:54 +00003297
Tim Peters528ca532004-09-16 01:30:50 +00003298#############################################################################
3299# oddballs
3300
3301class Oddballs(unittest.TestCase):
3302
3303 def test_bug_1028306(self):
3304 # Trying to compare a date to a datetime should act like a mixed-
3305 # type comparison, despite that datetime is a subclass of date.
3306 as_date = date.today()
3307 as_datetime = datetime.combine(as_date, time())
3308 self.assert_(as_date != as_datetime)
3309 self.assert_(as_datetime != as_date)
3310 self.assert_(not as_date == as_datetime)
3311 self.assert_(not as_datetime == as_date)
3312 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3313 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3314 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3315 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3316 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3317 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3318 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3319 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3320
3321 # Neverthelss, comparison should work with the base-class (date)
3322 # projection if use of a date method is forced.
3323 self.assert_(as_date.__eq__(as_datetime))
3324 different_day = (as_date.day + 1) % 20 + 1
3325 self.assert_(not as_date.__eq__(as_datetime.replace(day=
3326 different_day)))
3327
3328 # And date should compare with other subclasses of date. If a
3329 # subclass wants to stop this, it's up to the subclass to do so.
3330 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3331 self.assertEqual(as_date, date_sc)
3332 self.assertEqual(date_sc, as_date)
3333
3334 # Ditto for datetimes.
3335 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3336 as_date.day, 0, 0, 0)
3337 self.assertEqual(as_datetime, datetime_sc)
3338 self.assertEqual(datetime_sc, as_datetime)
3339
Tim Peters2a799bf2002-12-16 20:18:38 +00003340def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003341 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003342
3343if __name__ == "__main__":
3344 test_main()