blob: ff00d8b0b0dadd2207841b780685bb6af5ca97e2 [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
Gregory P. Smith137d8242008-06-02 04:05:52 +0000859
Eric Smitha9f7d622008-02-17 19:46:49 +0000860 def test_format(self):
861 dt = self.theclass(2007, 9, 10)
862 self.assertEqual(dt.__format__(''), str(dt))
863
864 # check that a derived class's __str__() gets called
865 class A(self.theclass):
866 def __str__(self):
867 return 'A'
868 a = A(2007, 9, 10)
869 self.assertEqual(a.__format__(''), 'A')
870
871 # check that a derived class's strftime gets called
872 class B(self.theclass):
873 def strftime(self, format_spec):
874 return 'B'
875 b = B(2007, 9, 10)
876 self.assertEqual(b.__format__(''), str(dt))
877
878 for fmt in ["m:%m d:%d y:%y",
879 "m:%m d:%d y:%y H:%H M:%M S:%S",
880 "%z %Z",
881 ]:
882 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
883 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
884 self.assertEqual(b.__format__(fmt), 'B')
885
Tim Peters2a799bf2002-12-16 20:18:38 +0000886 def test_resolution_info(self):
887 self.assert_(isinstance(self.theclass.min, self.theclass))
888 self.assert_(isinstance(self.theclass.max, self.theclass))
889 self.assert_(isinstance(self.theclass.resolution, timedelta))
890 self.assert_(self.theclass.max > self.theclass.min)
891
892 def test_extreme_timedelta(self):
893 big = self.theclass.max - self.theclass.min
894 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
895 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
896 # n == 315537897599999999 ~= 2**58.13
897 justasbig = timedelta(0, 0, n)
898 self.assertEqual(big, justasbig)
899 self.assertEqual(self.theclass.min + big, self.theclass.max)
900 self.assertEqual(self.theclass.max - big, self.theclass.min)
901
902 def test_timetuple(self):
903 for i in range(7):
904 # January 2, 1956 is a Monday (0)
905 d = self.theclass(1956, 1, 2+i)
906 t = d.timetuple()
907 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
908 # February 1, 1956 is a Wednesday (2)
909 d = self.theclass(1956, 2, 1+i)
910 t = d.timetuple()
911 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
912 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
913 # of the year.
914 d = self.theclass(1956, 3, 1+i)
915 t = d.timetuple()
916 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
917 self.assertEqual(t.tm_year, 1956)
918 self.assertEqual(t.tm_mon, 3)
919 self.assertEqual(t.tm_mday, 1+i)
920 self.assertEqual(t.tm_hour, 0)
921 self.assertEqual(t.tm_min, 0)
922 self.assertEqual(t.tm_sec, 0)
923 self.assertEqual(t.tm_wday, (3+i)%7)
924 self.assertEqual(t.tm_yday, 61+i)
925 self.assertEqual(t.tm_isdst, -1)
926
927 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000928 args = 6, 7, 23
929 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000930 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000931 green = pickler.dumps(orig, proto)
932 derived = unpickler.loads(green)
933 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000934
935 def test_compare(self):
936 t1 = self.theclass(2, 3, 4)
937 t2 = self.theclass(2, 3, 4)
938 self.failUnless(t1 == t2)
939 self.failUnless(t1 <= t2)
940 self.failUnless(t1 >= t2)
941 self.failUnless(not t1 != t2)
942 self.failUnless(not t1 < t2)
943 self.failUnless(not t1 > t2)
944 self.assertEqual(cmp(t1, t2), 0)
945 self.assertEqual(cmp(t2, t1), 0)
946
947 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
948 t2 = self.theclass(*args) # this is larger than t1
949 self.failUnless(t1 < t2)
950 self.failUnless(t2 > t1)
951 self.failUnless(t1 <= t2)
952 self.failUnless(t2 >= t1)
953 self.failUnless(t1 != t2)
954 self.failUnless(t2 != t1)
955 self.failUnless(not t1 == t2)
956 self.failUnless(not t2 == t1)
957 self.failUnless(not t1 > t2)
958 self.failUnless(not t2 < t1)
959 self.failUnless(not t1 >= t2)
960 self.failUnless(not t2 <= t1)
961 self.assertEqual(cmp(t1, t2), -1)
962 self.assertEqual(cmp(t2, t1), 1)
963
Tim Peters68124bb2003-02-08 03:46:31 +0000964 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000965 self.assertEqual(t1 == badarg, False)
966 self.assertEqual(t1 != badarg, True)
967 self.assertEqual(badarg == t1, False)
968 self.assertEqual(badarg != t1, True)
969
Tim Peters2a799bf2002-12-16 20:18:38 +0000970 self.assertRaises(TypeError, lambda: t1 < badarg)
971 self.assertRaises(TypeError, lambda: t1 > badarg)
972 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000973 self.assertRaises(TypeError, lambda: badarg <= t1)
974 self.assertRaises(TypeError, lambda: badarg < t1)
975 self.assertRaises(TypeError, lambda: badarg > t1)
976 self.assertRaises(TypeError, lambda: badarg >= t1)
977
Tim Peters8d81a012003-01-24 22:36:34 +0000978 def test_mixed_compare(self):
979 our = self.theclass(2000, 4, 5)
980 self.assertRaises(TypeError, cmp, our, 1)
981 self.assertRaises(TypeError, cmp, 1, our)
982
983 class AnotherDateTimeClass(object):
984 def __cmp__(self, other):
985 # Return "equal" so calling this can't be confused with
986 # compare-by-address (which never says "equal" for distinct
987 # objects).
988 return 0
Nick Coghlan48361f52008-08-11 15:45:58 +0000989 __hash__ = None # Silence Py3k warning
Tim Peters8d81a012003-01-24 22:36:34 +0000990
991 # This still errors, because date and datetime comparison raise
992 # TypeError instead of NotImplemented when they don't know what to
993 # do, in order to stop comparison from falling back to the default
994 # compare-by-address.
995 their = AnotherDateTimeClass()
996 self.assertRaises(TypeError, cmp, our, their)
997 # Oops: The next stab raises TypeError in the C implementation,
998 # but not in the Python implementation of datetime. The difference
999 # is due to that the Python implementation defines __cmp__ but
1000 # the C implementation defines tp_richcompare. This is more pain
1001 # to fix than it's worth, so commenting out the test.
1002 # self.assertEqual(cmp(their, our), 0)
1003
1004 # But date and datetime comparison return NotImplemented instead if the
1005 # other object has a timetuple attr. This gives the other object a
1006 # chance to do the comparison.
1007 class Comparable(AnotherDateTimeClass):
1008 def timetuple(self):
1009 return ()
1010
1011 their = Comparable()
1012 self.assertEqual(cmp(our, their), 0)
1013 self.assertEqual(cmp(their, our), 0)
1014 self.failUnless(our == their)
1015 self.failUnless(their == our)
1016
Tim Peters2a799bf2002-12-16 20:18:38 +00001017 def test_bool(self):
1018 # All dates are considered true.
1019 self.failUnless(self.theclass.min)
1020 self.failUnless(self.theclass.max)
1021
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001022 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001023 # For nasty technical reasons, we can't handle years before 1900.
1024 cls = self.theclass
1025 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1026 for y in 1, 49, 51, 99, 100, 1000, 1899:
1027 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001028
1029 def test_replace(self):
1030 cls = self.theclass
1031 args = [1, 2, 3]
1032 base = cls(*args)
1033 self.assertEqual(base, base.replace())
1034
1035 i = 0
1036 for name, newval in (("year", 2),
1037 ("month", 3),
1038 ("day", 4)):
1039 newargs = args[:]
1040 newargs[i] = newval
1041 expected = cls(*newargs)
1042 got = base.replace(**{name: newval})
1043 self.assertEqual(expected, got)
1044 i += 1
1045
1046 # Out of bounds.
1047 base = cls(2000, 2, 29)
1048 self.assertRaises(ValueError, base.replace, year=2001)
1049
Tim Petersa98924a2003-05-17 05:55:19 +00001050 def test_subclass_date(self):
1051
1052 class C(self.theclass):
1053 theAnswer = 42
1054
1055 def __new__(cls, *args, **kws):
1056 temp = kws.copy()
1057 extra = temp.pop('extra')
1058 result = self.theclass.__new__(cls, *args, **temp)
1059 result.extra = extra
1060 return result
1061
1062 def newmeth(self, start):
1063 return start + self.year + self.month
1064
1065 args = 2003, 4, 14
1066
1067 dt1 = self.theclass(*args)
1068 dt2 = C(*args, **{'extra': 7})
1069
1070 self.assertEqual(dt2.__class__, C)
1071 self.assertEqual(dt2.theAnswer, 42)
1072 self.assertEqual(dt2.extra, 7)
1073 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1074 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1075
Tim Peters604c0132004-06-07 23:04:33 +00001076 def test_pickling_subclass_date(self):
1077
1078 args = 6, 7, 23
1079 orig = SubclassDate(*args)
1080 for pickler, unpickler, proto in pickle_choices:
1081 green = pickler.dumps(orig, proto)
1082 derived = unpickler.loads(green)
1083 self.assertEqual(orig, derived)
1084
Tim Peters3f606292004-03-21 23:38:41 +00001085 def test_backdoor_resistance(self):
1086 # For fast unpickling, the constructor accepts a pickle string.
1087 # This is a low-overhead backdoor. A user can (by intent or
1088 # mistake) pass a string directly, which (if it's the right length)
1089 # will get treated like a pickle, and bypass the normal sanity
1090 # checks in the constructor. This can create insane objects.
1091 # The constructor doesn't want to burn the time to validate all
1092 # fields, but does check the month field. This stops, e.g.,
1093 # datetime.datetime('1995-03-25') from yielding an insane object.
1094 base = '1995-03-25'
1095 if not issubclass(self.theclass, datetime):
1096 base = base[:4]
1097 for month_byte in '9', chr(0), chr(13), '\xff':
1098 self.assertRaises(TypeError, self.theclass,
1099 base[:2] + month_byte + base[3:])
1100 for ord_byte in range(1, 13):
1101 # This shouldn't blow up because of the month byte alone. If
1102 # the implementation changes to do more-careful checking, it may
1103 # blow up because other fields are insane.
1104 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001105
Tim Peters2a799bf2002-12-16 20:18:38 +00001106#############################################################################
1107# datetime tests
1108
Tim Peters604c0132004-06-07 23:04:33 +00001109class SubclassDatetime(datetime):
1110 sub_var = 1
1111
Tim Peters2a799bf2002-12-16 20:18:38 +00001112class TestDateTime(TestDate):
1113
1114 theclass = datetime
1115
1116 def test_basic_attributes(self):
1117 dt = self.theclass(2002, 3, 1, 12, 0)
1118 self.assertEqual(dt.year, 2002)
1119 self.assertEqual(dt.month, 3)
1120 self.assertEqual(dt.day, 1)
1121 self.assertEqual(dt.hour, 12)
1122 self.assertEqual(dt.minute, 0)
1123 self.assertEqual(dt.second, 0)
1124 self.assertEqual(dt.microsecond, 0)
1125
1126 def test_basic_attributes_nonzero(self):
1127 # Make sure all attributes are non-zero so bugs in
1128 # bit-shifting access show up.
1129 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1130 self.assertEqual(dt.year, 2002)
1131 self.assertEqual(dt.month, 3)
1132 self.assertEqual(dt.day, 1)
1133 self.assertEqual(dt.hour, 12)
1134 self.assertEqual(dt.minute, 59)
1135 self.assertEqual(dt.second, 59)
1136 self.assertEqual(dt.microsecond, 8000)
1137
1138 def test_roundtrip(self):
1139 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1140 self.theclass.now()):
1141 # Verify dt -> string -> datetime identity.
1142 s = repr(dt)
1143 self.failUnless(s.startswith('datetime.'))
1144 s = s[9:]
1145 dt2 = eval(s)
1146 self.assertEqual(dt, dt2)
1147
1148 # Verify identity via reconstructing from pieces.
1149 dt2 = self.theclass(dt.year, dt.month, dt.day,
1150 dt.hour, dt.minute, dt.second,
1151 dt.microsecond)
1152 self.assertEqual(dt, dt2)
1153
1154 def test_isoformat(self):
1155 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1156 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1157 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1158 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1159 # str is ISO format with the separator forced to a blank.
1160 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1161
1162 t = self.theclass(2, 3, 2)
1163 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1164 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1165 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1166 # str is ISO format with the separator forced to a blank.
1167 self.assertEqual(str(t), "0002-03-02 00:00:00")
1168
Eric Smitha9f7d622008-02-17 19:46:49 +00001169 def test_format(self):
1170 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1171 self.assertEqual(dt.__format__(''), str(dt))
1172
1173 # check that a derived class's __str__() gets called
1174 class A(self.theclass):
1175 def __str__(self):
1176 return 'A'
1177 a = A(2007, 9, 10, 4, 5, 1, 123)
1178 self.assertEqual(a.__format__(''), 'A')
1179
1180 # check that a derived class's strftime gets called
1181 class B(self.theclass):
1182 def strftime(self, format_spec):
1183 return 'B'
1184 b = B(2007, 9, 10, 4, 5, 1, 123)
1185 self.assertEqual(b.__format__(''), str(dt))
1186
1187 for fmt in ["m:%m d:%d y:%y",
1188 "m:%m d:%d y:%y H:%H M:%M S:%S",
1189 "%z %Z",
1190 ]:
1191 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1192 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1193 self.assertEqual(b.__format__(fmt), 'B')
1194
Tim Peters2a799bf2002-12-16 20:18:38 +00001195 def test_more_ctime(self):
1196 # Test fields that TestDate doesn't touch.
1197 import time
1198
1199 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1200 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1201 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1202 # out. The difference is that t.ctime() produces " 2" for the day,
1203 # but platform ctime() produces "02" for the day. According to
1204 # C99, t.ctime() is correct here.
1205 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1206
1207 # So test a case where that difference doesn't matter.
1208 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1209 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1210
1211 def test_tz_independent_comparing(self):
1212 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1213 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1214 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1215 self.assertEqual(dt1, dt3)
1216 self.assert_(dt2 > dt3)
1217
1218 # Make sure comparison doesn't forget microseconds, and isn't done
1219 # via comparing a float timestamp (an IEEE double doesn't have enough
1220 # precision to span microsecond resolution across years 1 thru 9999,
1221 # so comparing via timestamp necessarily calls some distinct values
1222 # equal).
1223 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1224 us = timedelta(microseconds=1)
1225 dt2 = dt1 + us
1226 self.assertEqual(dt2 - dt1, us)
1227 self.assert_(dt1 < dt2)
1228
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001229 def test_strftime_with_bad_tzname_replace(self):
1230 # verify ok if tzinfo.tzname().replace() returns a non-string
1231 class MyTzInfo(FixedOffset):
1232 def tzname(self, dt):
1233 class MyStr(str):
1234 def replace(self, *args):
1235 return None
1236 return MyStr('name')
1237 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1238 self.assertRaises(TypeError, t.strftime, '%Z')
1239
Tim Peters2a799bf2002-12-16 20:18:38 +00001240 def test_bad_constructor_arguments(self):
1241 # bad years
1242 self.theclass(MINYEAR, 1, 1) # no exception
1243 self.theclass(MAXYEAR, 1, 1) # no exception
1244 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1245 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1246 # bad months
1247 self.theclass(2000, 1, 1) # no exception
1248 self.theclass(2000, 12, 1) # no exception
1249 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1250 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1251 # bad days
1252 self.theclass(2000, 2, 29) # no exception
1253 self.theclass(2004, 2, 29) # no exception
1254 self.theclass(2400, 2, 29) # no exception
1255 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1256 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1257 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1258 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1259 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1260 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1261 # bad hours
1262 self.theclass(2000, 1, 31, 0) # no exception
1263 self.theclass(2000, 1, 31, 23) # no exception
1264 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1265 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1266 # bad minutes
1267 self.theclass(2000, 1, 31, 23, 0) # no exception
1268 self.theclass(2000, 1, 31, 23, 59) # no exception
1269 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1270 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1271 # bad seconds
1272 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1273 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1274 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1275 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1276 # bad microseconds
1277 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1278 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1279 self.assertRaises(ValueError, self.theclass,
1280 2000, 1, 31, 23, 59, 59, -1)
1281 self.assertRaises(ValueError, self.theclass,
1282 2000, 1, 31, 23, 59, 59,
1283 1000000)
1284
1285 def test_hash_equality(self):
1286 d = self.theclass(2000, 12, 31, 23, 30, 17)
1287 e = self.theclass(2000, 12, 31, 23, 30, 17)
1288 self.assertEqual(d, e)
1289 self.assertEqual(hash(d), hash(e))
1290
1291 dic = {d: 1}
1292 dic[e] = 2
1293 self.assertEqual(len(dic), 1)
1294 self.assertEqual(dic[d], 2)
1295 self.assertEqual(dic[e], 2)
1296
1297 d = self.theclass(2001, 1, 1, 0, 5, 17)
1298 e = self.theclass(2001, 1, 1, 0, 5, 17)
1299 self.assertEqual(d, e)
1300 self.assertEqual(hash(d), hash(e))
1301
1302 dic = {d: 1}
1303 dic[e] = 2
1304 self.assertEqual(len(dic), 1)
1305 self.assertEqual(dic[d], 2)
1306 self.assertEqual(dic[e], 2)
1307
1308 def test_computations(self):
1309 a = self.theclass(2002, 1, 31)
1310 b = self.theclass(1956, 1, 31)
1311 diff = a-b
1312 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1313 self.assertEqual(diff.seconds, 0)
1314 self.assertEqual(diff.microseconds, 0)
1315 a = self.theclass(2002, 3, 2, 17, 6)
1316 millisec = timedelta(0, 0, 1000)
1317 hour = timedelta(0, 3600)
1318 day = timedelta(1)
1319 week = timedelta(7)
1320 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1321 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1322 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1323 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1324 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1325 self.assertEqual(a - hour, a + -hour)
1326 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1327 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1328 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1329 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1330 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1331 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1332 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1333 self.assertEqual((a + week) - a, week)
1334 self.assertEqual((a + day) - a, day)
1335 self.assertEqual((a + hour) - a, hour)
1336 self.assertEqual((a + millisec) - a, millisec)
1337 self.assertEqual((a - week) - a, -week)
1338 self.assertEqual((a - day) - a, -day)
1339 self.assertEqual((a - hour) - a, -hour)
1340 self.assertEqual((a - millisec) - a, -millisec)
1341 self.assertEqual(a - (a + week), -week)
1342 self.assertEqual(a - (a + day), -day)
1343 self.assertEqual(a - (a + hour), -hour)
1344 self.assertEqual(a - (a + millisec), -millisec)
1345 self.assertEqual(a - (a - week), week)
1346 self.assertEqual(a - (a - day), day)
1347 self.assertEqual(a - (a - hour), hour)
1348 self.assertEqual(a - (a - millisec), millisec)
1349 self.assertEqual(a + (week + day + hour + millisec),
1350 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1351 self.assertEqual(a + (week + day + hour + millisec),
1352 (((a + week) + day) + hour) + millisec)
1353 self.assertEqual(a - (week + day + hour + millisec),
1354 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1355 self.assertEqual(a - (week + day + hour + millisec),
1356 (((a - week) - day) - hour) - millisec)
1357 # Add/sub ints, longs, floats should be illegal
1358 for i in 1, 1L, 1.0:
1359 self.assertRaises(TypeError, lambda: a+i)
1360 self.assertRaises(TypeError, lambda: a-i)
1361 self.assertRaises(TypeError, lambda: i+a)
1362 self.assertRaises(TypeError, lambda: i-a)
1363
1364 # delta - datetime is senseless.
1365 self.assertRaises(TypeError, lambda: day - a)
1366 # mixing datetime and (delta or datetime) via * or // is senseless
1367 self.assertRaises(TypeError, lambda: day * a)
1368 self.assertRaises(TypeError, lambda: a * day)
1369 self.assertRaises(TypeError, lambda: day // a)
1370 self.assertRaises(TypeError, lambda: a // day)
1371 self.assertRaises(TypeError, lambda: a * a)
1372 self.assertRaises(TypeError, lambda: a // a)
1373 # datetime + datetime is senseless
1374 self.assertRaises(TypeError, lambda: a + a)
1375
1376 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001377 args = 6, 7, 23, 20, 59, 1, 64**2
1378 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001379 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001380 green = pickler.dumps(orig, proto)
1381 derived = unpickler.loads(green)
1382 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001383
Guido van Rossum275666f2003-02-07 21:49:01 +00001384 def test_more_pickling(self):
1385 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1386 s = pickle.dumps(a)
1387 b = pickle.loads(s)
1388 self.assertEqual(b.year, 2003)
1389 self.assertEqual(b.month, 2)
1390 self.assertEqual(b.day, 7)
1391
Tim Peters604c0132004-06-07 23:04:33 +00001392 def test_pickling_subclass_datetime(self):
1393 args = 6, 7, 23, 20, 59, 1, 64**2
1394 orig = SubclassDatetime(*args)
1395 for pickler, unpickler, proto in pickle_choices:
1396 green = pickler.dumps(orig, proto)
1397 derived = unpickler.loads(green)
1398 self.assertEqual(orig, derived)
1399
Tim Peters2a799bf2002-12-16 20:18:38 +00001400 def test_more_compare(self):
1401 # The test_compare() inherited from TestDate covers the error cases.
1402 # We just want to test lexicographic ordering on the members datetime
1403 # has that date lacks.
1404 args = [2000, 11, 29, 20, 58, 16, 999998]
1405 t1 = self.theclass(*args)
1406 t2 = self.theclass(*args)
1407 self.failUnless(t1 == t2)
1408 self.failUnless(t1 <= t2)
1409 self.failUnless(t1 >= t2)
1410 self.failUnless(not t1 != t2)
1411 self.failUnless(not t1 < t2)
1412 self.failUnless(not t1 > t2)
1413 self.assertEqual(cmp(t1, t2), 0)
1414 self.assertEqual(cmp(t2, t1), 0)
1415
1416 for i in range(len(args)):
1417 newargs = args[:]
1418 newargs[i] = args[i] + 1
1419 t2 = self.theclass(*newargs) # this is larger than t1
1420 self.failUnless(t1 < t2)
1421 self.failUnless(t2 > t1)
1422 self.failUnless(t1 <= t2)
1423 self.failUnless(t2 >= t1)
1424 self.failUnless(t1 != t2)
1425 self.failUnless(t2 != t1)
1426 self.failUnless(not t1 == t2)
1427 self.failUnless(not t2 == t1)
1428 self.failUnless(not t1 > t2)
1429 self.failUnless(not t2 < t1)
1430 self.failUnless(not t1 >= t2)
1431 self.failUnless(not t2 <= t1)
1432 self.assertEqual(cmp(t1, t2), -1)
1433 self.assertEqual(cmp(t2, t1), 1)
1434
1435
1436 # A helper for timestamp constructor tests.
1437 def verify_field_equality(self, expected, got):
1438 self.assertEqual(expected.tm_year, got.year)
1439 self.assertEqual(expected.tm_mon, got.month)
1440 self.assertEqual(expected.tm_mday, got.day)
1441 self.assertEqual(expected.tm_hour, got.hour)
1442 self.assertEqual(expected.tm_min, got.minute)
1443 self.assertEqual(expected.tm_sec, got.second)
1444
1445 def test_fromtimestamp(self):
1446 import time
1447
1448 ts = time.time()
1449 expected = time.localtime(ts)
1450 got = self.theclass.fromtimestamp(ts)
1451 self.verify_field_equality(expected, got)
1452
1453 def test_utcfromtimestamp(self):
1454 import time
1455
1456 ts = time.time()
1457 expected = time.gmtime(ts)
1458 got = self.theclass.utcfromtimestamp(ts)
1459 self.verify_field_equality(expected, got)
1460
Georg Brandl6d78a582006-04-28 19:09:24 +00001461 def test_microsecond_rounding(self):
1462 # Test whether fromtimestamp "rounds up" floats that are less
1463 # than one microsecond smaller than an integer.
1464 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1465 self.theclass.fromtimestamp(1))
1466
Tim Peters1b6f7a92004-06-20 02:50:16 +00001467 def test_insane_fromtimestamp(self):
1468 # It's possible that some platform maps time_t to double,
1469 # and that this test will fail there. This test should
1470 # exempt such platforms (provided they return reasonable
1471 # results!).
1472 for insane in -1e200, 1e200:
1473 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1474 insane)
1475
1476 def test_insane_utcfromtimestamp(self):
1477 # It's possible that some platform maps time_t to double,
1478 # and that this test will fail there. This test should
1479 # exempt such platforms (provided they return reasonable
1480 # results!).
1481 for insane in -1e200, 1e200:
1482 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1483 insane)
1484
Guido van Rossum2054ee92007-03-06 15:50:01 +00001485 def test_negative_float_fromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001486 # Windows doesn't accept negative timestamps
1487 if os.name == "nt":
1488 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001489 # The result is tz-dependent; at least test that this doesn't
1490 # fail (like it did before bug 1646728 was fixed).
1491 self.theclass.fromtimestamp(-1.05)
1492
1493 def test_negative_float_utcfromtimestamp(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 d = self.theclass.utcfromtimestamp(-1.05)
1498 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1499
Tim Peters2a799bf2002-12-16 20:18:38 +00001500 def test_utcnow(self):
1501 import time
1502
1503 # Call it a success if utcnow() and utcfromtimestamp() are within
1504 # a second of each other.
1505 tolerance = timedelta(seconds=1)
1506 for dummy in range(3):
1507 from_now = self.theclass.utcnow()
1508 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1509 if abs(from_timestamp - from_now) <= tolerance:
1510 break
1511 # Else try again a few times.
1512 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1513
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001514 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001515 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001516
Skip Montanarofc070d22008-03-15 16:04:45 +00001517 string = '2004-12-01 13:02:47.197'
1518 format = '%Y-%m-%d %H:%M:%S.%f'
1519 result, frac = _strptime._strptime(string, format)
1520 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001521 got = self.theclass.strptime(string, format)
1522 self.assertEqual(expected, got)
1523
Tim Peters2a799bf2002-12-16 20:18:38 +00001524 def test_more_timetuple(self):
1525 # This tests fields beyond those tested by the TestDate.test_timetuple.
1526 t = self.theclass(2004, 12, 31, 6, 22, 33)
1527 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1528 self.assertEqual(t.timetuple(),
1529 (t.year, t.month, t.day,
1530 t.hour, t.minute, t.second,
1531 t.weekday(),
1532 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1533 -1))
1534 tt = t.timetuple()
1535 self.assertEqual(tt.tm_year, t.year)
1536 self.assertEqual(tt.tm_mon, t.month)
1537 self.assertEqual(tt.tm_mday, t.day)
1538 self.assertEqual(tt.tm_hour, t.hour)
1539 self.assertEqual(tt.tm_min, t.minute)
1540 self.assertEqual(tt.tm_sec, t.second)
1541 self.assertEqual(tt.tm_wday, t.weekday())
1542 self.assertEqual(tt.tm_yday, t.toordinal() -
1543 date(t.year, 1, 1).toordinal() + 1)
1544 self.assertEqual(tt.tm_isdst, -1)
1545
1546 def test_more_strftime(self):
1547 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001548 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1549 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1550 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001551
1552 def test_extract(self):
1553 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1554 self.assertEqual(dt.date(), date(2002, 3, 4))
1555 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1556
1557 def test_combine(self):
1558 d = date(2002, 3, 4)
1559 t = time(18, 45, 3, 1234)
1560 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1561 combine = self.theclass.combine
1562 dt = combine(d, t)
1563 self.assertEqual(dt, expected)
1564
1565 dt = combine(time=t, date=d)
1566 self.assertEqual(dt, expected)
1567
1568 self.assertEqual(d, dt.date())
1569 self.assertEqual(t, dt.time())
1570 self.assertEqual(dt, combine(dt.date(), dt.time()))
1571
1572 self.assertRaises(TypeError, combine) # need an arg
1573 self.assertRaises(TypeError, combine, d) # need two args
1574 self.assertRaises(TypeError, combine, t, d) # args reversed
1575 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1576 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1577
Tim Peters12bf3392002-12-24 05:41:27 +00001578 def test_replace(self):
1579 cls = self.theclass
1580 args = [1, 2, 3, 4, 5, 6, 7]
1581 base = cls(*args)
1582 self.assertEqual(base, base.replace())
1583
1584 i = 0
1585 for name, newval in (("year", 2),
1586 ("month", 3),
1587 ("day", 4),
1588 ("hour", 5),
1589 ("minute", 6),
1590 ("second", 7),
1591 ("microsecond", 8)):
1592 newargs = args[:]
1593 newargs[i] = newval
1594 expected = cls(*newargs)
1595 got = base.replace(**{name: newval})
1596 self.assertEqual(expected, got)
1597 i += 1
1598
1599 # Out of bounds.
1600 base = cls(2000, 2, 29)
1601 self.assertRaises(ValueError, base.replace, year=2001)
1602
Tim Peters80475bb2002-12-25 07:40:55 +00001603 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001604 # Pretty boring! The TZ test is more interesting here. astimezone()
1605 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001606 dt = self.theclass.now()
1607 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001608 self.assertRaises(TypeError, dt.astimezone) # not enough args
1609 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1610 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001611 self.assertRaises(ValueError, dt.astimezone, f) # naive
1612 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001613
Tim Peters52dcce22003-01-23 16:36:11 +00001614 class Bogus(tzinfo):
1615 def utcoffset(self, dt): return None
1616 def dst(self, dt): return timedelta(0)
1617 bog = Bogus()
1618 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1619
1620 class AlsoBogus(tzinfo):
1621 def utcoffset(self, dt): return timedelta(0)
1622 def dst(self, dt): return None
1623 alsobog = AlsoBogus()
1624 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001625
Tim Petersa98924a2003-05-17 05:55:19 +00001626 def test_subclass_datetime(self):
1627
1628 class C(self.theclass):
1629 theAnswer = 42
1630
1631 def __new__(cls, *args, **kws):
1632 temp = kws.copy()
1633 extra = temp.pop('extra')
1634 result = self.theclass.__new__(cls, *args, **temp)
1635 result.extra = extra
1636 return result
1637
1638 def newmeth(self, start):
1639 return start + self.year + self.month + self.second
1640
1641 args = 2003, 4, 14, 12, 13, 41
1642
1643 dt1 = self.theclass(*args)
1644 dt2 = C(*args, **{'extra': 7})
1645
1646 self.assertEqual(dt2.__class__, C)
1647 self.assertEqual(dt2.theAnswer, 42)
1648 self.assertEqual(dt2.extra, 7)
1649 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1650 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1651 dt1.second - 7)
1652
Tim Peters604c0132004-06-07 23:04:33 +00001653class SubclassTime(time):
1654 sub_var = 1
1655
Collin Winterc2898c52007-04-25 17:29:52 +00001656class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001657
1658 theclass = time
1659
1660 def test_basic_attributes(self):
1661 t = self.theclass(12, 0)
1662 self.assertEqual(t.hour, 12)
1663 self.assertEqual(t.minute, 0)
1664 self.assertEqual(t.second, 0)
1665 self.assertEqual(t.microsecond, 0)
1666
1667 def test_basic_attributes_nonzero(self):
1668 # Make sure all attributes are non-zero so bugs in
1669 # bit-shifting access show up.
1670 t = self.theclass(12, 59, 59, 8000)
1671 self.assertEqual(t.hour, 12)
1672 self.assertEqual(t.minute, 59)
1673 self.assertEqual(t.second, 59)
1674 self.assertEqual(t.microsecond, 8000)
1675
1676 def test_roundtrip(self):
1677 t = self.theclass(1, 2, 3, 4)
1678
1679 # Verify t -> string -> time identity.
1680 s = repr(t)
1681 self.failUnless(s.startswith('datetime.'))
1682 s = s[9:]
1683 t2 = eval(s)
1684 self.assertEqual(t, t2)
1685
1686 # Verify identity via reconstructing from pieces.
1687 t2 = self.theclass(t.hour, t.minute, t.second,
1688 t.microsecond)
1689 self.assertEqual(t, t2)
1690
1691 def test_comparing(self):
1692 args = [1, 2, 3, 4]
1693 t1 = self.theclass(*args)
1694 t2 = self.theclass(*args)
1695 self.failUnless(t1 == t2)
1696 self.failUnless(t1 <= t2)
1697 self.failUnless(t1 >= t2)
1698 self.failUnless(not t1 != t2)
1699 self.failUnless(not t1 < t2)
1700 self.failUnless(not t1 > t2)
1701 self.assertEqual(cmp(t1, t2), 0)
1702 self.assertEqual(cmp(t2, t1), 0)
1703
1704 for i in range(len(args)):
1705 newargs = args[:]
1706 newargs[i] = args[i] + 1
1707 t2 = self.theclass(*newargs) # this is larger than t1
1708 self.failUnless(t1 < t2)
1709 self.failUnless(t2 > t1)
1710 self.failUnless(t1 <= t2)
1711 self.failUnless(t2 >= t1)
1712 self.failUnless(t1 != t2)
1713 self.failUnless(t2 != t1)
1714 self.failUnless(not t1 == t2)
1715 self.failUnless(not t2 == t1)
1716 self.failUnless(not t1 > t2)
1717 self.failUnless(not t2 < t1)
1718 self.failUnless(not t1 >= t2)
1719 self.failUnless(not t2 <= t1)
1720 self.assertEqual(cmp(t1, t2), -1)
1721 self.assertEqual(cmp(t2, t1), 1)
1722
Tim Peters68124bb2003-02-08 03:46:31 +00001723 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001724 self.assertEqual(t1 == badarg, False)
1725 self.assertEqual(t1 != badarg, True)
1726 self.assertEqual(badarg == t1, False)
1727 self.assertEqual(badarg != t1, True)
1728
Tim Peters2a799bf2002-12-16 20:18:38 +00001729 self.assertRaises(TypeError, lambda: t1 <= badarg)
1730 self.assertRaises(TypeError, lambda: t1 < badarg)
1731 self.assertRaises(TypeError, lambda: t1 > badarg)
1732 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001733 self.assertRaises(TypeError, lambda: badarg <= t1)
1734 self.assertRaises(TypeError, lambda: badarg < t1)
1735 self.assertRaises(TypeError, lambda: badarg > t1)
1736 self.assertRaises(TypeError, lambda: badarg >= t1)
1737
1738 def test_bad_constructor_arguments(self):
1739 # bad hours
1740 self.theclass(0, 0) # no exception
1741 self.theclass(23, 0) # no exception
1742 self.assertRaises(ValueError, self.theclass, -1, 0)
1743 self.assertRaises(ValueError, self.theclass, 24, 0)
1744 # bad minutes
1745 self.theclass(23, 0) # no exception
1746 self.theclass(23, 59) # no exception
1747 self.assertRaises(ValueError, self.theclass, 23, -1)
1748 self.assertRaises(ValueError, self.theclass, 23, 60)
1749 # bad seconds
1750 self.theclass(23, 59, 0) # no exception
1751 self.theclass(23, 59, 59) # no exception
1752 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1753 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1754 # bad microseconds
1755 self.theclass(23, 59, 59, 0) # no exception
1756 self.theclass(23, 59, 59, 999999) # no exception
1757 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1758 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1759
1760 def test_hash_equality(self):
1761 d = self.theclass(23, 30, 17)
1762 e = self.theclass(23, 30, 17)
1763 self.assertEqual(d, e)
1764 self.assertEqual(hash(d), hash(e))
1765
1766 dic = {d: 1}
1767 dic[e] = 2
1768 self.assertEqual(len(dic), 1)
1769 self.assertEqual(dic[d], 2)
1770 self.assertEqual(dic[e], 2)
1771
1772 d = self.theclass(0, 5, 17)
1773 e = self.theclass(0, 5, 17)
1774 self.assertEqual(d, e)
1775 self.assertEqual(hash(d), hash(e))
1776
1777 dic = {d: 1}
1778 dic[e] = 2
1779 self.assertEqual(len(dic), 1)
1780 self.assertEqual(dic[d], 2)
1781 self.assertEqual(dic[e], 2)
1782
1783 def test_isoformat(self):
1784 t = self.theclass(4, 5, 1, 123)
1785 self.assertEqual(t.isoformat(), "04:05:01.000123")
1786 self.assertEqual(t.isoformat(), str(t))
1787
1788 t = self.theclass()
1789 self.assertEqual(t.isoformat(), "00:00:00")
1790 self.assertEqual(t.isoformat(), str(t))
1791
1792 t = self.theclass(microsecond=1)
1793 self.assertEqual(t.isoformat(), "00:00:00.000001")
1794 self.assertEqual(t.isoformat(), str(t))
1795
1796 t = self.theclass(microsecond=10)
1797 self.assertEqual(t.isoformat(), "00:00:00.000010")
1798 self.assertEqual(t.isoformat(), str(t))
1799
1800 t = self.theclass(microsecond=100)
1801 self.assertEqual(t.isoformat(), "00:00:00.000100")
1802 self.assertEqual(t.isoformat(), str(t))
1803
1804 t = self.theclass(microsecond=1000)
1805 self.assertEqual(t.isoformat(), "00:00:00.001000")
1806 self.assertEqual(t.isoformat(), str(t))
1807
1808 t = self.theclass(microsecond=10000)
1809 self.assertEqual(t.isoformat(), "00:00:00.010000")
1810 self.assertEqual(t.isoformat(), str(t))
1811
1812 t = self.theclass(microsecond=100000)
1813 self.assertEqual(t.isoformat(), "00:00:00.100000")
1814 self.assertEqual(t.isoformat(), str(t))
1815
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001816 def test_1653736(self):
1817 # verify it doesn't accept extra keyword arguments
1818 t = self.theclass(second=1)
1819 self.assertRaises(TypeError, t.isoformat, foo=3)
1820
Tim Peters2a799bf2002-12-16 20:18:38 +00001821 def test_strftime(self):
1822 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001823 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001824 # A naive object replaces %z and %Z with empty strings.
1825 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1826
Eric Smitha9f7d622008-02-17 19:46:49 +00001827 def test_format(self):
1828 t = self.theclass(1, 2, 3, 4)
1829 self.assertEqual(t.__format__(''), str(t))
1830
1831 # check that a derived class's __str__() gets called
1832 class A(self.theclass):
1833 def __str__(self):
1834 return 'A'
1835 a = A(1, 2, 3, 4)
1836 self.assertEqual(a.__format__(''), 'A')
1837
1838 # check that a derived class's strftime gets called
1839 class B(self.theclass):
1840 def strftime(self, format_spec):
1841 return 'B'
1842 b = B(1, 2, 3, 4)
1843 self.assertEqual(b.__format__(''), str(t))
1844
1845 for fmt in ['%H %M %S',
1846 ]:
1847 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1848 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1849 self.assertEqual(b.__format__(fmt), 'B')
1850
Tim Peters2a799bf2002-12-16 20:18:38 +00001851 def test_str(self):
1852 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1853 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1854 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1855 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1856 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1857
1858 def test_repr(self):
1859 name = 'datetime.' + self.theclass.__name__
1860 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1861 "%s(1, 2, 3, 4)" % name)
1862 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1863 "%s(10, 2, 3, 4000)" % name)
1864 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1865 "%s(0, 2, 3, 400000)" % name)
1866 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1867 "%s(12, 2, 3)" % name)
1868 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1869 "%s(23, 15)" % name)
1870
1871 def test_resolution_info(self):
1872 self.assert_(isinstance(self.theclass.min, self.theclass))
1873 self.assert_(isinstance(self.theclass.max, self.theclass))
1874 self.assert_(isinstance(self.theclass.resolution, timedelta))
1875 self.assert_(self.theclass.max > self.theclass.min)
1876
1877 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001878 args = 20, 59, 16, 64**2
1879 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001880 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001881 green = pickler.dumps(orig, proto)
1882 derived = unpickler.loads(green)
1883 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001884
Tim Peters604c0132004-06-07 23:04:33 +00001885 def test_pickling_subclass_time(self):
1886 args = 20, 59, 16, 64**2
1887 orig = SubclassTime(*args)
1888 for pickler, unpickler, proto in pickle_choices:
1889 green = pickler.dumps(orig, proto)
1890 derived = unpickler.loads(green)
1891 self.assertEqual(orig, derived)
1892
Tim Peters2a799bf2002-12-16 20:18:38 +00001893 def test_bool(self):
1894 cls = self.theclass
1895 self.failUnless(cls(1))
1896 self.failUnless(cls(0, 1))
1897 self.failUnless(cls(0, 0, 1))
1898 self.failUnless(cls(0, 0, 0, 1))
1899 self.failUnless(not cls(0))
1900 self.failUnless(not cls())
1901
Tim Peters12bf3392002-12-24 05:41:27 +00001902 def test_replace(self):
1903 cls = self.theclass
1904 args = [1, 2, 3, 4]
1905 base = cls(*args)
1906 self.assertEqual(base, base.replace())
1907
1908 i = 0
1909 for name, newval in (("hour", 5),
1910 ("minute", 6),
1911 ("second", 7),
1912 ("microsecond", 8)):
1913 newargs = args[:]
1914 newargs[i] = newval
1915 expected = cls(*newargs)
1916 got = base.replace(**{name: newval})
1917 self.assertEqual(expected, got)
1918 i += 1
1919
1920 # Out of bounds.
1921 base = cls(1)
1922 self.assertRaises(ValueError, base.replace, hour=24)
1923 self.assertRaises(ValueError, base.replace, minute=-1)
1924 self.assertRaises(ValueError, base.replace, second=100)
1925 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1926
Tim Petersa98924a2003-05-17 05:55:19 +00001927 def test_subclass_time(self):
1928
1929 class C(self.theclass):
1930 theAnswer = 42
1931
1932 def __new__(cls, *args, **kws):
1933 temp = kws.copy()
1934 extra = temp.pop('extra')
1935 result = self.theclass.__new__(cls, *args, **temp)
1936 result.extra = extra
1937 return result
1938
1939 def newmeth(self, start):
1940 return start + self.hour + self.second
1941
1942 args = 4, 5, 6
1943
1944 dt1 = self.theclass(*args)
1945 dt2 = C(*args, **{'extra': 7})
1946
1947 self.assertEqual(dt2.__class__, C)
1948 self.assertEqual(dt2.theAnswer, 42)
1949 self.assertEqual(dt2.extra, 7)
1950 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1951 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1952
Armin Rigof4afb212005-11-07 07:15:48 +00001953 def test_backdoor_resistance(self):
1954 # see TestDate.test_backdoor_resistance().
1955 base = '2:59.0'
1956 for hour_byte in ' ', '9', chr(24), '\xff':
1957 self.assertRaises(TypeError, self.theclass,
1958 hour_byte + base[1:])
1959
Tim Peters855fe882002-12-22 03:43:39 +00001960# A mixin for classes with a tzinfo= argument. Subclasses must define
1961# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001962# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001963class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001964
Tim Petersbad8ff02002-12-30 20:52:32 +00001965 def test_argument_passing(self):
1966 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001967 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001968 class introspective(tzinfo):
1969 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001970 def utcoffset(self, dt):
1971 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001972 dst = utcoffset
1973
1974 obj = cls(1, 2, 3, tzinfo=introspective())
1975
Tim Peters0bf60bd2003-01-08 20:40:01 +00001976 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001977 self.assertEqual(obj.tzname(), expected)
1978
Tim Peters0bf60bd2003-01-08 20:40:01 +00001979 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001980 self.assertEqual(obj.utcoffset(), expected)
1981 self.assertEqual(obj.dst(), expected)
1982
Tim Peters855fe882002-12-22 03:43:39 +00001983 def test_bad_tzinfo_classes(self):
1984 cls = self.theclass
1985 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001986
Tim Peters855fe882002-12-22 03:43:39 +00001987 class NiceTry(object):
1988 def __init__(self): pass
1989 def utcoffset(self, dt): pass
1990 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1991
1992 class BetterTry(tzinfo):
1993 def __init__(self): pass
1994 def utcoffset(self, dt): pass
1995 b = BetterTry()
1996 t = cls(1, 1, 1, tzinfo=b)
1997 self.failUnless(t.tzinfo is b)
1998
1999 def test_utc_offset_out_of_bounds(self):
2000 class Edgy(tzinfo):
2001 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002002 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002003 def utcoffset(self, dt):
2004 return self.offset
2005
2006 cls = self.theclass
2007 for offset, legit in ((-1440, False),
2008 (-1439, True),
2009 (1439, True),
2010 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002011 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002012 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002013 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002014 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002015 else:
2016 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002017 if legit:
2018 aofs = abs(offset)
2019 h, m = divmod(aofs, 60)
2020 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002021 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002022 t = t.timetz()
2023 self.assertEqual(str(t), "01:02:03" + tag)
2024 else:
2025 self.assertRaises(ValueError, str, t)
2026
2027 def test_tzinfo_classes(self):
2028 cls = self.theclass
2029 class C1(tzinfo):
2030 def utcoffset(self, dt): return None
2031 def dst(self, dt): return None
2032 def tzname(self, dt): return None
2033 for t in (cls(1, 1, 1),
2034 cls(1, 1, 1, tzinfo=None),
2035 cls(1, 1, 1, tzinfo=C1())):
2036 self.failUnless(t.utcoffset() is None)
2037 self.failUnless(t.dst() is None)
2038 self.failUnless(t.tzname() is None)
2039
Tim Peters855fe882002-12-22 03:43:39 +00002040 class C3(tzinfo):
2041 def utcoffset(self, dt): return timedelta(minutes=-1439)
2042 def dst(self, dt): return timedelta(minutes=1439)
2043 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002044 t = cls(1, 1, 1, tzinfo=C3())
2045 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2046 self.assertEqual(t.dst(), timedelta(minutes=1439))
2047 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002048
2049 # Wrong types.
2050 class C4(tzinfo):
2051 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002052 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002053 def tzname(self, dt): return 0
2054 t = cls(1, 1, 1, tzinfo=C4())
2055 self.assertRaises(TypeError, t.utcoffset)
2056 self.assertRaises(TypeError, t.dst)
2057 self.assertRaises(TypeError, t.tzname)
2058
2059 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002060 class C6(tzinfo):
2061 def utcoffset(self, dt): return timedelta(hours=-24)
2062 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002063 t = cls(1, 1, 1, tzinfo=C6())
2064 self.assertRaises(ValueError, t.utcoffset)
2065 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002066
2067 # Not a whole number of minutes.
2068 class C7(tzinfo):
2069 def utcoffset(self, dt): return timedelta(seconds=61)
2070 def dst(self, dt): return timedelta(microseconds=-81)
2071 t = cls(1, 1, 1, tzinfo=C7())
2072 self.assertRaises(ValueError, t.utcoffset)
2073 self.assertRaises(ValueError, t.dst)
2074
Tim Peters4c0db782002-12-26 05:01:19 +00002075 def test_aware_compare(self):
2076 cls = self.theclass
2077
Tim Peters60c76e42002-12-27 00:41:11 +00002078 # Ensure that utcoffset() gets ignored if the comparands have
2079 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002080 class OperandDependentOffset(tzinfo):
2081 def utcoffset(self, t):
2082 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002083 # d0 and d1 equal after adjustment
2084 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002085 else:
Tim Peters397301e2003-01-02 21:28:08 +00002086 # d2 off in the weeds
2087 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002088
2089 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2090 d0 = base.replace(minute=3)
2091 d1 = base.replace(minute=9)
2092 d2 = base.replace(minute=11)
2093 for x in d0, d1, d2:
2094 for y in d0, d1, d2:
2095 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002096 expected = cmp(x.minute, y.minute)
2097 self.assertEqual(got, expected)
2098
2099 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002100 # Note that a time can't actually have an operand-depedent offset,
2101 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2102 # so skip this test for time.
2103 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002104 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2105 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2106 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2107 for x in d0, d1, d2:
2108 for y in d0, d1, d2:
2109 got = cmp(x, y)
2110 if (x is d0 or x is d1) and (y is d0 or y is d1):
2111 expected = 0
2112 elif x is y is d2:
2113 expected = 0
2114 elif x is d2:
2115 expected = -1
2116 else:
2117 assert y is d2
2118 expected = 1
2119 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002120
Tim Peters855fe882002-12-22 03:43:39 +00002121
Tim Peters0bf60bd2003-01-08 20:40:01 +00002122# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002123class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002124 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002125
2126 def test_empty(self):
2127 t = self.theclass()
2128 self.assertEqual(t.hour, 0)
2129 self.assertEqual(t.minute, 0)
2130 self.assertEqual(t.second, 0)
2131 self.assertEqual(t.microsecond, 0)
2132 self.failUnless(t.tzinfo is None)
2133
Tim Peters2a799bf2002-12-16 20:18:38 +00002134 def test_zones(self):
2135 est = FixedOffset(-300, "EST", 1)
2136 utc = FixedOffset(0, "UTC", -2)
2137 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002138 t1 = time( 7, 47, tzinfo=est)
2139 t2 = time(12, 47, tzinfo=utc)
2140 t3 = time(13, 47, tzinfo=met)
2141 t4 = time(microsecond=40)
2142 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002143
2144 self.assertEqual(t1.tzinfo, est)
2145 self.assertEqual(t2.tzinfo, utc)
2146 self.assertEqual(t3.tzinfo, met)
2147 self.failUnless(t4.tzinfo is None)
2148 self.assertEqual(t5.tzinfo, utc)
2149
Tim Peters855fe882002-12-22 03:43:39 +00002150 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2151 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2152 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002153 self.failUnless(t4.utcoffset() is None)
2154 self.assertRaises(TypeError, t1.utcoffset, "no args")
2155
2156 self.assertEqual(t1.tzname(), "EST")
2157 self.assertEqual(t2.tzname(), "UTC")
2158 self.assertEqual(t3.tzname(), "MET")
2159 self.failUnless(t4.tzname() is None)
2160 self.assertRaises(TypeError, t1.tzname, "no args")
2161
Tim Peters855fe882002-12-22 03:43:39 +00002162 self.assertEqual(t1.dst(), timedelta(minutes=1))
2163 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2164 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002165 self.failUnless(t4.dst() is None)
2166 self.assertRaises(TypeError, t1.dst, "no args")
2167
2168 self.assertEqual(hash(t1), hash(t2))
2169 self.assertEqual(hash(t1), hash(t3))
2170 self.assertEqual(hash(t2), hash(t3))
2171
2172 self.assertEqual(t1, t2)
2173 self.assertEqual(t1, t3)
2174 self.assertEqual(t2, t3)
2175 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2176 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2177 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2178
2179 self.assertEqual(str(t1), "07:47:00-05:00")
2180 self.assertEqual(str(t2), "12:47:00+00:00")
2181 self.assertEqual(str(t3), "13:47:00+01:00")
2182 self.assertEqual(str(t4), "00:00:00.000040")
2183 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2184
2185 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2186 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2187 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2188 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2189 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2190
Tim Peters0bf60bd2003-01-08 20:40:01 +00002191 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002192 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2193 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2194 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2195 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2196 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2197
2198 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2199 "07:47:00 %Z=EST %z=-0500")
2200 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2201 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2202
2203 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002204 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002205 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2206 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2207
Tim Petersb92bb712002-12-21 17:44:07 +00002208 # Check that an invalid tzname result raises an exception.
2209 class Badtzname(tzinfo):
2210 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002211 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002212 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2213 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002214
2215 def test_hash_edge_cases(self):
2216 # Offsets that overflow a basic time.
2217 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2218 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2219 self.assertEqual(hash(t1), hash(t2))
2220
2221 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2222 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2223 self.assertEqual(hash(t1), hash(t2))
2224
Tim Peters2a799bf2002-12-16 20:18:38 +00002225 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002226 # Try one without a tzinfo.
2227 args = 20, 59, 16, 64**2
2228 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002229 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002230 green = pickler.dumps(orig, proto)
2231 derived = unpickler.loads(green)
2232 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002233
2234 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002235 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002236 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
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)
2241 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2242 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2243 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002244
2245 def test_more_bool(self):
2246 # Test cases with non-None tzinfo.
2247 cls = self.theclass
2248
2249 t = cls(0, tzinfo=FixedOffset(-300, ""))
2250 self.failUnless(t)
2251
2252 t = cls(5, tzinfo=FixedOffset(-300, ""))
2253 self.failUnless(t)
2254
2255 t = cls(5, tzinfo=FixedOffset(300, ""))
2256 self.failUnless(not t)
2257
2258 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2259 self.failUnless(not t)
2260
2261 # Mostly ensuring this doesn't overflow internally.
2262 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2263 self.failUnless(t)
2264
2265 # But this should yield a value error -- the utcoffset is bogus.
2266 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2267 self.assertRaises(ValueError, lambda: bool(t))
2268
2269 # Likewise.
2270 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2271 self.assertRaises(ValueError, lambda: bool(t))
2272
Tim Peters12bf3392002-12-24 05:41:27 +00002273 def test_replace(self):
2274 cls = self.theclass
2275 z100 = FixedOffset(100, "+100")
2276 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2277 args = [1, 2, 3, 4, z100]
2278 base = cls(*args)
2279 self.assertEqual(base, base.replace())
2280
2281 i = 0
2282 for name, newval in (("hour", 5),
2283 ("minute", 6),
2284 ("second", 7),
2285 ("microsecond", 8),
2286 ("tzinfo", zm200)):
2287 newargs = args[:]
2288 newargs[i] = newval
2289 expected = cls(*newargs)
2290 got = base.replace(**{name: newval})
2291 self.assertEqual(expected, got)
2292 i += 1
2293
2294 # Ensure we can get rid of a tzinfo.
2295 self.assertEqual(base.tzname(), "+100")
2296 base2 = base.replace(tzinfo=None)
2297 self.failUnless(base2.tzinfo is None)
2298 self.failUnless(base2.tzname() is None)
2299
2300 # Ensure we can add one.
2301 base3 = base2.replace(tzinfo=z100)
2302 self.assertEqual(base, base3)
2303 self.failUnless(base.tzinfo is base3.tzinfo)
2304
2305 # Out of bounds.
2306 base = cls(1)
2307 self.assertRaises(ValueError, base.replace, hour=24)
2308 self.assertRaises(ValueError, base.replace, minute=-1)
2309 self.assertRaises(ValueError, base.replace, second=100)
2310 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2311
Tim Peters60c76e42002-12-27 00:41:11 +00002312 def test_mixed_compare(self):
2313 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002314 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002315 self.assertEqual(t1, t2)
2316 t2 = t2.replace(tzinfo=None)
2317 self.assertEqual(t1, t2)
2318 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2319 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002320 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2321 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002322
Tim Peters0bf60bd2003-01-08 20:40:01 +00002323 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002324 class Varies(tzinfo):
2325 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002326 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002327 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002328 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002329 return self.offset
2330
2331 v = Varies()
2332 t1 = t2.replace(tzinfo=v)
2333 t2 = t2.replace(tzinfo=v)
2334 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2335 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2336 self.assertEqual(t1, t2)
2337
2338 # But if they're not identical, it isn't ignored.
2339 t2 = t2.replace(tzinfo=Varies())
2340 self.failUnless(t1 < t2) # t1's offset counter still going up
2341
Tim Petersa98924a2003-05-17 05:55:19 +00002342 def test_subclass_timetz(self):
2343
2344 class C(self.theclass):
2345 theAnswer = 42
2346
2347 def __new__(cls, *args, **kws):
2348 temp = kws.copy()
2349 extra = temp.pop('extra')
2350 result = self.theclass.__new__(cls, *args, **temp)
2351 result.extra = extra
2352 return result
2353
2354 def newmeth(self, start):
2355 return start + self.hour + self.second
2356
2357 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2358
2359 dt1 = self.theclass(*args)
2360 dt2 = C(*args, **{'extra': 7})
2361
2362 self.assertEqual(dt2.__class__, C)
2363 self.assertEqual(dt2.theAnswer, 42)
2364 self.assertEqual(dt2.extra, 7)
2365 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2366 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2367
Tim Peters4c0db782002-12-26 05:01:19 +00002368
Tim Peters0bf60bd2003-01-08 20:40:01 +00002369# Testing datetime objects with a non-None tzinfo.
2370
Collin Winterc2898c52007-04-25 17:29:52 +00002371class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002372 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002373
2374 def test_trivial(self):
2375 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2376 self.assertEqual(dt.year, 1)
2377 self.assertEqual(dt.month, 2)
2378 self.assertEqual(dt.day, 3)
2379 self.assertEqual(dt.hour, 4)
2380 self.assertEqual(dt.minute, 5)
2381 self.assertEqual(dt.second, 6)
2382 self.assertEqual(dt.microsecond, 7)
2383 self.assertEqual(dt.tzinfo, None)
2384
2385 def test_even_more_compare(self):
2386 # The test_compare() and test_more_compare() inherited from TestDate
2387 # and TestDateTime covered non-tzinfo cases.
2388
2389 # Smallest possible after UTC adjustment.
2390 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2391 # Largest possible after UTC adjustment.
2392 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2393 tzinfo=FixedOffset(-1439, ""))
2394
2395 # Make sure those compare correctly, and w/o overflow.
2396 self.failUnless(t1 < t2)
2397 self.failUnless(t1 != t2)
2398 self.failUnless(t2 > t1)
2399
2400 self.failUnless(t1 == t1)
2401 self.failUnless(t2 == t2)
2402
2403 # Equal afer adjustment.
2404 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2405 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2406 self.assertEqual(t1, t2)
2407
2408 # Change t1 not to subtract a minute, and t1 should be larger.
2409 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2410 self.failUnless(t1 > t2)
2411
2412 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2413 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2414 self.failUnless(t1 < t2)
2415
2416 # Back to the original t1, but make seconds resolve it.
2417 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2418 second=1)
2419 self.failUnless(t1 > t2)
2420
2421 # Likewise, but make microseconds resolve it.
2422 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2423 microsecond=1)
2424 self.failUnless(t1 > t2)
2425
2426 # Make t2 naive and it should fail.
2427 t2 = self.theclass.min
2428 self.assertRaises(TypeError, lambda: t1 == t2)
2429 self.assertEqual(t2, t2)
2430
2431 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2432 class Naive(tzinfo):
2433 def utcoffset(self, dt): return None
2434 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2435 self.assertRaises(TypeError, lambda: t1 == t2)
2436 self.assertEqual(t2, t2)
2437
2438 # OTOH, it's OK to compare two of these mixing the two ways of being
2439 # naive.
2440 t1 = self.theclass(5, 6, 7)
2441 self.assertEqual(t1, t2)
2442
2443 # Try a bogus uctoffset.
2444 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002445 def utcoffset(self, dt):
2446 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002447 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2448 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002449 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002450
Tim Peters2a799bf2002-12-16 20:18:38 +00002451 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002452 # Try one without a tzinfo.
2453 args = 6, 7, 23, 20, 59, 1, 64**2
2454 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002455 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002456 green = pickler.dumps(orig, proto)
2457 derived = unpickler.loads(green)
2458 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002459
2460 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002461 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002462 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002463 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002464 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002465 green = pickler.dumps(orig, proto)
2466 derived = unpickler.loads(green)
2467 self.assertEqual(orig, derived)
2468 self.failUnless(isinstance(derived.tzinfo,
2469 PicklableFixedOffset))
2470 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2471 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002472
2473 def test_extreme_hashes(self):
2474 # If an attempt is made to hash these via subtracting the offset
2475 # then hashing a datetime object, OverflowError results. The
2476 # Python implementation used to blow up here.
2477 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2478 hash(t)
2479 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2480 tzinfo=FixedOffset(-1439, ""))
2481 hash(t)
2482
2483 # OTOH, an OOB offset should blow up.
2484 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2485 self.assertRaises(ValueError, hash, t)
2486
2487 def test_zones(self):
2488 est = FixedOffset(-300, "EST")
2489 utc = FixedOffset(0, "UTC")
2490 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002491 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2492 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2493 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002494 self.assertEqual(t1.tzinfo, est)
2495 self.assertEqual(t2.tzinfo, utc)
2496 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002497 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2498 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2499 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002500 self.assertEqual(t1.tzname(), "EST")
2501 self.assertEqual(t2.tzname(), "UTC")
2502 self.assertEqual(t3.tzname(), "MET")
2503 self.assertEqual(hash(t1), hash(t2))
2504 self.assertEqual(hash(t1), hash(t3))
2505 self.assertEqual(hash(t2), hash(t3))
2506 self.assertEqual(t1, t2)
2507 self.assertEqual(t1, t3)
2508 self.assertEqual(t2, t3)
2509 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2510 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2511 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002512 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002513 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2514 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2515 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2516
2517 def test_combine(self):
2518 met = FixedOffset(60, "MET")
2519 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002520 tz = time(18, 45, 3, 1234, tzinfo=met)
2521 dt = datetime.combine(d, tz)
2522 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002523 tzinfo=met))
2524
2525 def test_extract(self):
2526 met = FixedOffset(60, "MET")
2527 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2528 self.assertEqual(dt.date(), date(2002, 3, 4))
2529 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002530 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002531
2532 def test_tz_aware_arithmetic(self):
2533 import random
2534
2535 now = self.theclass.now()
2536 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002537 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002538 nowaware = self.theclass.combine(now.date(), timeaware)
2539 self.failUnless(nowaware.tzinfo is tz55)
2540 self.assertEqual(nowaware.timetz(), timeaware)
2541
2542 # Can't mix aware and non-aware.
2543 self.assertRaises(TypeError, lambda: now - nowaware)
2544 self.assertRaises(TypeError, lambda: nowaware - now)
2545
Tim Peters0bf60bd2003-01-08 20:40:01 +00002546 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002547 self.assertRaises(TypeError, lambda: now + nowaware)
2548 self.assertRaises(TypeError, lambda: nowaware + now)
2549 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2550
2551 # Subtracting should yield 0.
2552 self.assertEqual(now - now, timedelta(0))
2553 self.assertEqual(nowaware - nowaware, timedelta(0))
2554
2555 # Adding a delta should preserve tzinfo.
2556 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2557 nowawareplus = nowaware + delta
2558 self.failUnless(nowaware.tzinfo is tz55)
2559 nowawareplus2 = delta + nowaware
2560 self.failUnless(nowawareplus2.tzinfo is tz55)
2561 self.assertEqual(nowawareplus, nowawareplus2)
2562
2563 # that - delta should be what we started with, and that - what we
2564 # started with should be delta.
2565 diff = nowawareplus - delta
2566 self.failUnless(diff.tzinfo is tz55)
2567 self.assertEqual(nowaware, diff)
2568 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2569 self.assertEqual(nowawareplus - nowaware, delta)
2570
2571 # Make up a random timezone.
2572 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002573 # Attach it to nowawareplus.
2574 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002575 self.failUnless(nowawareplus.tzinfo is tzr)
2576 # Make sure the difference takes the timezone adjustments into account.
2577 got = nowaware - nowawareplus
2578 # Expected: (nowaware base - nowaware offset) -
2579 # (nowawareplus base - nowawareplus offset) =
2580 # (nowaware base - nowawareplus base) +
2581 # (nowawareplus offset - nowaware offset) =
2582 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002583 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002584 self.assertEqual(got, expected)
2585
2586 # Try max possible difference.
2587 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2588 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2589 tzinfo=FixedOffset(-1439, "max"))
2590 maxdiff = max - min
2591 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2592 timedelta(minutes=2*1439))
2593
2594 def test_tzinfo_now(self):
2595 meth = self.theclass.now
2596 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2597 base = meth()
2598 # Try with and without naming the keyword.
2599 off42 = FixedOffset(42, "42")
2600 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002601 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002602 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002603 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002604 # Bad argument with and w/o naming the keyword.
2605 self.assertRaises(TypeError, meth, 16)
2606 self.assertRaises(TypeError, meth, tzinfo=16)
2607 # Bad keyword name.
2608 self.assertRaises(TypeError, meth, tinfo=off42)
2609 # Too many args.
2610 self.assertRaises(TypeError, meth, off42, off42)
2611
Tim Peters10cadce2003-01-23 19:58:02 +00002612 # We don't know which time zone we're in, and don't have a tzinfo
2613 # class to represent it, so seeing whether a tz argument actually
2614 # does a conversion is tricky.
2615 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2616 utc = FixedOffset(0, "utc", 0)
2617 for dummy in range(3):
2618 now = datetime.now(weirdtz)
2619 self.failUnless(now.tzinfo is weirdtz)
2620 utcnow = datetime.utcnow().replace(tzinfo=utc)
2621 now2 = utcnow.astimezone(weirdtz)
2622 if abs(now - now2) < timedelta(seconds=30):
2623 break
2624 # Else the code is broken, or more than 30 seconds passed between
2625 # calls; assuming the latter, just try again.
2626 else:
2627 # Three strikes and we're out.
2628 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2629
Tim Peters2a799bf2002-12-16 20:18:38 +00002630 def test_tzinfo_fromtimestamp(self):
2631 import time
2632 meth = self.theclass.fromtimestamp
2633 ts = time.time()
2634 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2635 base = meth(ts)
2636 # Try with and without naming the keyword.
2637 off42 = FixedOffset(42, "42")
2638 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002639 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002640 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002641 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002642 # Bad argument with and w/o naming the keyword.
2643 self.assertRaises(TypeError, meth, ts, 16)
2644 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2645 # Bad keyword name.
2646 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2647 # Too many args.
2648 self.assertRaises(TypeError, meth, ts, off42, off42)
2649 # Too few args.
2650 self.assertRaises(TypeError, meth)
2651
Tim Peters2a44a8d2003-01-23 20:53:10 +00002652 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002653 timestamp = 1000000000
2654 utcdatetime = datetime.utcfromtimestamp(timestamp)
2655 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2656 # But on some flavor of Mac, it's nowhere near that. So we can't have
2657 # any idea here what time that actually is, we can only test that
2658 # relative changes match.
2659 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2660 tz = FixedOffset(utcoffset, "tz", 0)
2661 expected = utcdatetime + utcoffset
2662 got = datetime.fromtimestamp(timestamp, tz)
2663 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002664
Tim Peters2a799bf2002-12-16 20:18:38 +00002665 def test_tzinfo_utcnow(self):
2666 meth = self.theclass.utcnow
2667 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2668 base = meth()
2669 # Try with and without naming the keyword; for whatever reason,
2670 # utcnow() doesn't accept a tzinfo argument.
2671 off42 = FixedOffset(42, "42")
2672 self.assertRaises(TypeError, meth, off42)
2673 self.assertRaises(TypeError, meth, tzinfo=off42)
2674
2675 def test_tzinfo_utcfromtimestamp(self):
2676 import time
2677 meth = self.theclass.utcfromtimestamp
2678 ts = time.time()
2679 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2680 base = meth(ts)
2681 # Try with and without naming the keyword; for whatever reason,
2682 # utcfromtimestamp() doesn't accept a tzinfo argument.
2683 off42 = FixedOffset(42, "42")
2684 self.assertRaises(TypeError, meth, ts, off42)
2685 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2686
2687 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002688 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002689 # DST flag.
2690 class DST(tzinfo):
2691 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002692 if isinstance(dstvalue, int):
2693 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002694 self.dstvalue = dstvalue
2695 def dst(self, dt):
2696 return self.dstvalue
2697
2698 cls = self.theclass
2699 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2700 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2701 t = d.timetuple()
2702 self.assertEqual(1, t.tm_year)
2703 self.assertEqual(1, t.tm_mon)
2704 self.assertEqual(1, t.tm_mday)
2705 self.assertEqual(10, t.tm_hour)
2706 self.assertEqual(20, t.tm_min)
2707 self.assertEqual(30, t.tm_sec)
2708 self.assertEqual(0, t.tm_wday)
2709 self.assertEqual(1, t.tm_yday)
2710 self.assertEqual(flag, t.tm_isdst)
2711
2712 # dst() returns wrong type.
2713 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2714
2715 # dst() at the edge.
2716 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2717 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2718
2719 # dst() out of range.
2720 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2721 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2722
2723 def test_utctimetuple(self):
2724 class DST(tzinfo):
2725 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002726 if isinstance(dstvalue, int):
2727 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002728 self.dstvalue = dstvalue
2729 def dst(self, dt):
2730 return self.dstvalue
2731
2732 cls = self.theclass
2733 # This can't work: DST didn't implement utcoffset.
2734 self.assertRaises(NotImplementedError,
2735 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2736
2737 class UOFS(DST):
2738 def __init__(self, uofs, dofs=None):
2739 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002740 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002741 def utcoffset(self, dt):
2742 return self.uofs
2743
2744 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2745 # in effect for a UTC time.
2746 for dstvalue in -33, 33, 0, None:
2747 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2748 t = d.utctimetuple()
2749 self.assertEqual(d.year, t.tm_year)
2750 self.assertEqual(d.month, t.tm_mon)
2751 self.assertEqual(d.day, t.tm_mday)
2752 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2753 self.assertEqual(13, t.tm_min)
2754 self.assertEqual(d.second, t.tm_sec)
2755 self.assertEqual(d.weekday(), t.tm_wday)
2756 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2757 t.tm_yday)
2758 self.assertEqual(0, t.tm_isdst)
2759
2760 # At the edges, UTC adjustment can normalize into years out-of-range
2761 # for a datetime object. Ensure that a correct timetuple is
2762 # created anyway.
2763 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2764 # That goes back 1 minute less than a full day.
2765 t = tiny.utctimetuple()
2766 self.assertEqual(t.tm_year, MINYEAR-1)
2767 self.assertEqual(t.tm_mon, 12)
2768 self.assertEqual(t.tm_mday, 31)
2769 self.assertEqual(t.tm_hour, 0)
2770 self.assertEqual(t.tm_min, 1)
2771 self.assertEqual(t.tm_sec, 37)
2772 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2773 self.assertEqual(t.tm_isdst, 0)
2774
2775 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2776 # That goes forward 1 minute less than a full day.
2777 t = huge.utctimetuple()
2778 self.assertEqual(t.tm_year, MAXYEAR+1)
2779 self.assertEqual(t.tm_mon, 1)
2780 self.assertEqual(t.tm_mday, 1)
2781 self.assertEqual(t.tm_hour, 23)
2782 self.assertEqual(t.tm_min, 58)
2783 self.assertEqual(t.tm_sec, 37)
2784 self.assertEqual(t.tm_yday, 1)
2785 self.assertEqual(t.tm_isdst, 0)
2786
2787 def test_tzinfo_isoformat(self):
2788 zero = FixedOffset(0, "+00:00")
2789 plus = FixedOffset(220, "+03:40")
2790 minus = FixedOffset(-231, "-03:51")
2791 unknown = FixedOffset(None, "")
2792
2793 cls = self.theclass
2794 datestr = '0001-02-03'
2795 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002796 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002797 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2798 timestr = '04:05:59' + (us and '.987001' or '')
2799 ofsstr = ofs is not None and d.tzname() or ''
2800 tailstr = timestr + ofsstr
2801 iso = d.isoformat()
2802 self.assertEqual(iso, datestr + 'T' + tailstr)
2803 self.assertEqual(iso, d.isoformat('T'))
2804 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2805 self.assertEqual(str(d), datestr + ' ' + tailstr)
2806
Tim Peters12bf3392002-12-24 05:41:27 +00002807 def test_replace(self):
2808 cls = self.theclass
2809 z100 = FixedOffset(100, "+100")
2810 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2811 args = [1, 2, 3, 4, 5, 6, 7, z100]
2812 base = cls(*args)
2813 self.assertEqual(base, base.replace())
2814
2815 i = 0
2816 for name, newval in (("year", 2),
2817 ("month", 3),
2818 ("day", 4),
2819 ("hour", 5),
2820 ("minute", 6),
2821 ("second", 7),
2822 ("microsecond", 8),
2823 ("tzinfo", zm200)):
2824 newargs = args[:]
2825 newargs[i] = newval
2826 expected = cls(*newargs)
2827 got = base.replace(**{name: newval})
2828 self.assertEqual(expected, got)
2829 i += 1
2830
2831 # Ensure we can get rid of a tzinfo.
2832 self.assertEqual(base.tzname(), "+100")
2833 base2 = base.replace(tzinfo=None)
2834 self.failUnless(base2.tzinfo is None)
2835 self.failUnless(base2.tzname() is None)
2836
2837 # Ensure we can add one.
2838 base3 = base2.replace(tzinfo=z100)
2839 self.assertEqual(base, base3)
2840 self.failUnless(base.tzinfo is base3.tzinfo)
2841
2842 # Out of bounds.
2843 base = cls(2000, 2, 29)
2844 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002845
Tim Peters80475bb2002-12-25 07:40:55 +00002846 def test_more_astimezone(self):
2847 # The inherited test_astimezone covered some trivial and error cases.
2848 fnone = FixedOffset(None, "None")
2849 f44m = FixedOffset(44, "44")
2850 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2851
Tim Peters10cadce2003-01-23 19:58:02 +00002852 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002853 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002854 # Replacing with degenerate tzinfo raises an exception.
2855 self.assertRaises(ValueError, dt.astimezone, fnone)
2856 # Ditto with None tz.
2857 self.assertRaises(TypeError, dt.astimezone, None)
2858 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002859 x = dt.astimezone(dt.tzinfo)
2860 self.failUnless(x.tzinfo is f44m)
2861 self.assertEqual(x.date(), dt.date())
2862 self.assertEqual(x.time(), dt.time())
2863
2864 # Replacing with different tzinfo does adjust.
2865 got = dt.astimezone(fm5h)
2866 self.failUnless(got.tzinfo is fm5h)
2867 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2868 expected = dt - dt.utcoffset() # in effect, convert to UTC
2869 expected += fm5h.utcoffset(dt) # and from there to local time
2870 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2871 self.assertEqual(got.date(), expected.date())
2872 self.assertEqual(got.time(), expected.time())
2873 self.assertEqual(got.timetz(), expected.timetz())
2874 self.failUnless(got.tzinfo is expected.tzinfo)
2875 self.assertEqual(got, expected)
2876
Tim Peters4c0db782002-12-26 05:01:19 +00002877 def test_aware_subtract(self):
2878 cls = self.theclass
2879
Tim Peters60c76e42002-12-27 00:41:11 +00002880 # Ensure that utcoffset() is ignored when the operands have the
2881 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002882 class OperandDependentOffset(tzinfo):
2883 def utcoffset(self, t):
2884 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002885 # d0 and d1 equal after adjustment
2886 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002887 else:
Tim Peters397301e2003-01-02 21:28:08 +00002888 # d2 off in the weeds
2889 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002890
2891 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2892 d0 = base.replace(minute=3)
2893 d1 = base.replace(minute=9)
2894 d2 = base.replace(minute=11)
2895 for x in d0, d1, d2:
2896 for y in d0, d1, d2:
2897 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002898 expected = timedelta(minutes=x.minute - y.minute)
2899 self.assertEqual(got, expected)
2900
2901 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2902 # ignored.
2903 base = cls(8, 9, 10, 11, 12, 13, 14)
2904 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2905 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2906 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2907 for x in d0, d1, d2:
2908 for y in d0, d1, d2:
2909 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002910 if (x is d0 or x is d1) and (y is d0 or y is d1):
2911 expected = timedelta(0)
2912 elif x is y is d2:
2913 expected = timedelta(0)
2914 elif x is d2:
2915 expected = timedelta(minutes=(11-59)-0)
2916 else:
2917 assert y is d2
2918 expected = timedelta(minutes=0-(11-59))
2919 self.assertEqual(got, expected)
2920
Tim Peters60c76e42002-12-27 00:41:11 +00002921 def test_mixed_compare(self):
2922 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002923 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002924 self.assertEqual(t1, t2)
2925 t2 = t2.replace(tzinfo=None)
2926 self.assertEqual(t1, t2)
2927 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2928 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002929 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2930 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002931
Tim Peters0bf60bd2003-01-08 20:40:01 +00002932 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002933 class Varies(tzinfo):
2934 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002935 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002936 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002937 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002938 return self.offset
2939
2940 v = Varies()
2941 t1 = t2.replace(tzinfo=v)
2942 t2 = t2.replace(tzinfo=v)
2943 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2944 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2945 self.assertEqual(t1, t2)
2946
2947 # But if they're not identical, it isn't ignored.
2948 t2 = t2.replace(tzinfo=Varies())
2949 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002950
Tim Petersa98924a2003-05-17 05:55:19 +00002951 def test_subclass_datetimetz(self):
2952
2953 class C(self.theclass):
2954 theAnswer = 42
2955
2956 def __new__(cls, *args, **kws):
2957 temp = kws.copy()
2958 extra = temp.pop('extra')
2959 result = self.theclass.__new__(cls, *args, **temp)
2960 result.extra = extra
2961 return result
2962
2963 def newmeth(self, start):
2964 return start + self.hour + self.year
2965
2966 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2967
2968 dt1 = self.theclass(*args)
2969 dt2 = C(*args, **{'extra': 7})
2970
2971 self.assertEqual(dt2.__class__, C)
2972 self.assertEqual(dt2.theAnswer, 42)
2973 self.assertEqual(dt2.extra, 7)
2974 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2975 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2976
Tim Peters621818b2002-12-29 23:44:49 +00002977# Pain to set up DST-aware tzinfo classes.
2978
2979def first_sunday_on_or_after(dt):
2980 days_to_go = 6 - dt.weekday()
2981 if days_to_go:
2982 dt += timedelta(days_to_go)
2983 return dt
2984
2985ZERO = timedelta(0)
2986HOUR = timedelta(hours=1)
2987DAY = timedelta(days=1)
2988# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2989DSTSTART = datetime(1, 4, 1, 2)
2990# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002991# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2992# being standard time on that day, there is no spelling in local time of
2993# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2994DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002995
2996class USTimeZone(tzinfo):
2997
2998 def __init__(self, hours, reprname, stdname, dstname):
2999 self.stdoffset = timedelta(hours=hours)
3000 self.reprname = reprname
3001 self.stdname = stdname
3002 self.dstname = dstname
3003
3004 def __repr__(self):
3005 return self.reprname
3006
3007 def tzname(self, dt):
3008 if self.dst(dt):
3009 return self.dstname
3010 else:
3011 return self.stdname
3012
3013 def utcoffset(self, dt):
3014 return self.stdoffset + self.dst(dt)
3015
3016 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003017 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003018 # An exception instead may be sensible here, in one or more of
3019 # the cases.
3020 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003021 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003022
3023 # Find first Sunday in April.
3024 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3025 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3026
3027 # Find last Sunday in October.
3028 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3029 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3030
Tim Peters621818b2002-12-29 23:44:49 +00003031 # Can't compare naive to aware objects, so strip the timezone from
3032 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003033 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003034 return HOUR
3035 else:
3036 return ZERO
3037
Tim Peters521fc152002-12-31 17:36:56 +00003038Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3039Central = USTimeZone(-6, "Central", "CST", "CDT")
3040Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3041Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003042utc_real = FixedOffset(0, "UTC", 0)
3043# For better test coverage, we want another flavor of UTC that's west of
3044# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003045utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003046
3047class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003048 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003049 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003050 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003051
Tim Peters0bf60bd2003-01-08 20:40:01 +00003052 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003053
Tim Peters521fc152002-12-31 17:36:56 +00003054 # Check a time that's inside DST.
3055 def checkinside(self, dt, tz, utc, dston, dstoff):
3056 self.assertEqual(dt.dst(), HOUR)
3057
3058 # Conversion to our own timezone is always an identity.
3059 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003060
3061 asutc = dt.astimezone(utc)
3062 there_and_back = asutc.astimezone(tz)
3063
3064 # Conversion to UTC and back isn't always an identity here,
3065 # because there are redundant spellings (in local time) of
3066 # UTC time when DST begins: the clock jumps from 1:59:59
3067 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3068 # make sense then. The classes above treat 2:MM:SS as
3069 # daylight time then (it's "after 2am"), really an alias
3070 # for 1:MM:SS standard time. The latter form is what
3071 # conversion back from UTC produces.
3072 if dt.date() == dston.date() and dt.hour == 2:
3073 # We're in the redundant hour, and coming back from
3074 # UTC gives the 1:MM:SS standard-time spelling.
3075 self.assertEqual(there_and_back + HOUR, dt)
3076 # Although during was considered to be in daylight
3077 # time, there_and_back is not.
3078 self.assertEqual(there_and_back.dst(), ZERO)
3079 # They're the same times in UTC.
3080 self.assertEqual(there_and_back.astimezone(utc),
3081 dt.astimezone(utc))
3082 else:
3083 # We're not in the redundant hour.
3084 self.assertEqual(dt, there_and_back)
3085
Tim Peters327098a2003-01-20 22:54:38 +00003086 # Because we have a redundant spelling when DST begins, there is
3087 # (unforunately) an hour when DST ends that can't be spelled at all in
3088 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3089 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3090 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3091 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3092 # expressed in local time. Nevertheless, we want conversion back
3093 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003094 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003095 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003096 if dt.date() == dstoff.date() and dt.hour == 0:
3097 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003098 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003099 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3100 nexthour_utc += HOUR
3101 nexthour_tz = nexthour_utc.astimezone(tz)
3102 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003103 else:
Tim Peters327098a2003-01-20 22:54:38 +00003104 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003105
3106 # Check a time that's outside DST.
3107 def checkoutside(self, dt, tz, utc):
3108 self.assertEqual(dt.dst(), ZERO)
3109
3110 # Conversion to our own timezone is always an identity.
3111 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003112
3113 # Converting to UTC and back is an identity too.
3114 asutc = dt.astimezone(utc)
3115 there_and_back = asutc.astimezone(tz)
3116 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003117
Tim Peters1024bf82002-12-30 17:09:40 +00003118 def convert_between_tz_and_utc(self, tz, utc):
3119 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003120 # Because 1:MM on the day DST ends is taken as being standard time,
3121 # there is no spelling in tz for the last hour of daylight time.
3122 # For purposes of the test, the last hour of DST is 0:MM, which is
3123 # taken as being daylight time (and 1:MM is taken as being standard
3124 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003125 dstoff = self.dstoff.replace(tzinfo=tz)
3126 for delta in (timedelta(weeks=13),
3127 DAY,
3128 HOUR,
3129 timedelta(minutes=1),
3130 timedelta(microseconds=1)):
3131
Tim Peters521fc152002-12-31 17:36:56 +00003132 self.checkinside(dston, tz, utc, dston, dstoff)
3133 for during in dston + delta, dstoff - delta:
3134 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003135
Tim Peters521fc152002-12-31 17:36:56 +00003136 self.checkoutside(dstoff, tz, utc)
3137 for outside in dston - delta, dstoff + delta:
3138 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003139
Tim Peters621818b2002-12-29 23:44:49 +00003140 def test_easy(self):
3141 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003142 self.convert_between_tz_and_utc(Eastern, utc_real)
3143 self.convert_between_tz_and_utc(Pacific, utc_real)
3144 self.convert_between_tz_and_utc(Eastern, utc_fake)
3145 self.convert_between_tz_and_utc(Pacific, utc_fake)
3146 # The next is really dancing near the edge. It works because
3147 # Pacific and Eastern are far enough apart that their "problem
3148 # hours" don't overlap.
3149 self.convert_between_tz_and_utc(Eastern, Pacific)
3150 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003151 # OTOH, these fail! Don't enable them. The difficulty is that
3152 # the edge case tests assume that every hour is representable in
3153 # the "utc" class. This is always true for a fixed-offset tzinfo
3154 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3155 # For these adjacent DST-aware time zones, the range of time offsets
3156 # tested ends up creating hours in the one that aren't representable
3157 # in the other. For the same reason, we would see failures in the
3158 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3159 # offset deltas in convert_between_tz_and_utc().
3160 #
3161 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3162 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003163
Tim Petersf3615152003-01-01 21:51:37 +00003164 def test_tricky(self):
3165 # 22:00 on day before daylight starts.
3166 fourback = self.dston - timedelta(hours=4)
3167 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003168 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003169 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3170 # 2", we should get the 3 spelling.
3171 # If we plug 22:00 the day before into Eastern, it "looks like std
3172 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3173 # to 22:00 lands on 2:00, which makes no sense in local time (the
3174 # local clock jumps from 1 to 3). The point here is to make sure we
3175 # get the 3 spelling.
3176 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003177 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003178 self.assertEqual(expected, got)
3179
3180 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3181 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003182 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003183 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3184 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3185 # spelling.
3186 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003187 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003188 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003189
Tim Petersadf64202003-01-04 06:03:15 +00003190 # Now on the day DST ends, we want "repeat an hour" behavior.
3191 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3192 # EST 23:MM 0:MM 1:MM 2:MM
3193 # EDT 0:MM 1:MM 2:MM 3:MM
3194 # wall 0:MM 1:MM 1:MM 2:MM against these
3195 for utc in utc_real, utc_fake:
3196 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003197 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003198 # Convert that to UTC.
3199 first_std_hour -= tz.utcoffset(None)
3200 # Adjust for possibly fake UTC.
3201 asutc = first_std_hour + utc.utcoffset(None)
3202 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3203 # tz=Eastern.
3204 asutcbase = asutc.replace(tzinfo=utc)
3205 for tzhour in (0, 1, 1, 2):
3206 expectedbase = self.dstoff.replace(hour=tzhour)
3207 for minute in 0, 30, 59:
3208 expected = expectedbase.replace(minute=minute)
3209 asutc = asutcbase.replace(minute=minute)
3210 astz = asutc.astimezone(tz)
3211 self.assertEqual(astz.replace(tzinfo=None), expected)
3212 asutcbase += HOUR
3213
3214
Tim Peters710fb152003-01-02 19:35:54 +00003215 def test_bogus_dst(self):
3216 class ok(tzinfo):
3217 def utcoffset(self, dt): return HOUR
3218 def dst(self, dt): return HOUR
3219
3220 now = self.theclass.now().replace(tzinfo=utc_real)
3221 # Doesn't blow up.
3222 now.astimezone(ok())
3223
3224 # Does blow up.
3225 class notok(ok):
3226 def dst(self, dt): return None
3227 self.assertRaises(ValueError, now.astimezone, notok())
3228
Tim Peters52dcce22003-01-23 16:36:11 +00003229 def test_fromutc(self):
3230 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3231 now = datetime.utcnow().replace(tzinfo=utc_real)
3232 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3233 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3234 enow = Eastern.fromutc(now) # doesn't blow up
3235 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3236 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3237 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3238
3239 # Always converts UTC to standard time.
3240 class FauxUSTimeZone(USTimeZone):
3241 def fromutc(self, dt):
3242 return dt + self.stdoffset
3243 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3244
3245 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3246 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3247 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3248
3249 # Check around DST start.
3250 start = self.dston.replace(hour=4, tzinfo=Eastern)
3251 fstart = start.replace(tzinfo=FEastern)
3252 for wall in 23, 0, 1, 3, 4, 5:
3253 expected = start.replace(hour=wall)
3254 if wall == 23:
3255 expected -= timedelta(days=1)
3256 got = Eastern.fromutc(start)
3257 self.assertEqual(expected, got)
3258
3259 expected = fstart + FEastern.stdoffset
3260 got = FEastern.fromutc(fstart)
3261 self.assertEqual(expected, got)
3262
3263 # Ensure astimezone() calls fromutc() too.
3264 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3265 self.assertEqual(expected, got)
3266
3267 start += HOUR
3268 fstart += HOUR
3269
3270 # Check around DST end.
3271 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3272 fstart = start.replace(tzinfo=FEastern)
3273 for wall in 0, 1, 1, 2, 3, 4:
3274 expected = start.replace(hour=wall)
3275 got = Eastern.fromutc(start)
3276 self.assertEqual(expected, got)
3277
3278 expected = fstart + FEastern.stdoffset
3279 got = FEastern.fromutc(fstart)
3280 self.assertEqual(expected, got)
3281
3282 # Ensure astimezone() calls fromutc() too.
3283 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3284 self.assertEqual(expected, got)
3285
3286 start += HOUR
3287 fstart += HOUR
3288
Tim Peters710fb152003-01-02 19:35:54 +00003289
Tim Peters528ca532004-09-16 01:30:50 +00003290#############################################################################
3291# oddballs
3292
3293class Oddballs(unittest.TestCase):
3294
3295 def test_bug_1028306(self):
3296 # Trying to compare a date to a datetime should act like a mixed-
3297 # type comparison, despite that datetime is a subclass of date.
3298 as_date = date.today()
3299 as_datetime = datetime.combine(as_date, time())
3300 self.assert_(as_date != as_datetime)
3301 self.assert_(as_datetime != as_date)
3302 self.assert_(not as_date == as_datetime)
3303 self.assert_(not as_datetime == as_date)
3304 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3305 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3306 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3307 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3308 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3309 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3310 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3311 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3312
3313 # Neverthelss, comparison should work with the base-class (date)
3314 # projection if use of a date method is forced.
3315 self.assert_(as_date.__eq__(as_datetime))
3316 different_day = (as_date.day + 1) % 20 + 1
3317 self.assert_(not as_date.__eq__(as_datetime.replace(day=
3318 different_day)))
3319
3320 # And date should compare with other subclasses of date. If a
3321 # subclass wants to stop this, it's up to the subclass to do so.
3322 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3323 self.assertEqual(as_date, date_sc)
3324 self.assertEqual(date_sc, as_date)
3325
3326 # Ditto for datetimes.
3327 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3328 as_date.day, 0, 0, 0)
3329 self.assertEqual(as_datetime, datetime_sc)
3330 self.assertEqual(datetime_sc, as_datetime)
3331
Tim Peters2a799bf2002-12-16 20:18:38 +00003332def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003333 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003334
3335if __name__ == "__main__":
3336 test_main()