blob: dbd2f8f0e51eb4fea1b8216bf1d6a6e0e7cee6cb [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
Benjamin Petersonbe2c0a92008-10-04 21:33:08 +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
Georg Brandl686d53e2009-01-14 00:08:09 +0000859 #make sure that invalid format specifiers are handled correctly
Benjamin Petersonf5def212009-01-19 15:06:00 +0000860 #self.assertRaises(ValueError, t.strftime, "%e")
861 #self.assertRaises(ValueError, t.strftime, "%")
862 #self.assertRaises(ValueError, t.strftime, "%#")
863
864 #oh well, some systems just ignore those invalid ones.
865 #at least, excercise them to make sure that no crashes
866 #are generated
867 for f in ["%e", "%", "%#"]:
868 try:
869 t.strftime(f)
870 except ValueError:
871 pass
Georg Brandl686d53e2009-01-14 00:08:09 +0000872
873 #check that this standard extension works
874 t.strftime("%f")
875
Gregory P. Smith137d8242008-06-02 04:05:52 +0000876
Eric Smitha9f7d622008-02-17 19:46:49 +0000877 def test_format(self):
878 dt = self.theclass(2007, 9, 10)
879 self.assertEqual(dt.__format__(''), str(dt))
880
881 # check that a derived class's __str__() gets called
882 class A(self.theclass):
883 def __str__(self):
884 return 'A'
885 a = A(2007, 9, 10)
886 self.assertEqual(a.__format__(''), 'A')
887
888 # check that a derived class's strftime gets called
889 class B(self.theclass):
890 def strftime(self, format_spec):
891 return 'B'
892 b = B(2007, 9, 10)
893 self.assertEqual(b.__format__(''), str(dt))
894
895 for fmt in ["m:%m d:%d y:%y",
896 "m:%m d:%d y:%y H:%H M:%M S:%S",
897 "%z %Z",
898 ]:
899 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
900 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
901 self.assertEqual(b.__format__(fmt), 'B')
902
Tim Peters2a799bf2002-12-16 20:18:38 +0000903 def test_resolution_info(self):
904 self.assert_(isinstance(self.theclass.min, self.theclass))
905 self.assert_(isinstance(self.theclass.max, self.theclass))
906 self.assert_(isinstance(self.theclass.resolution, timedelta))
907 self.assert_(self.theclass.max > self.theclass.min)
908
909 def test_extreme_timedelta(self):
910 big = self.theclass.max - self.theclass.min
911 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
912 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
913 # n == 315537897599999999 ~= 2**58.13
914 justasbig = timedelta(0, 0, n)
915 self.assertEqual(big, justasbig)
916 self.assertEqual(self.theclass.min + big, self.theclass.max)
917 self.assertEqual(self.theclass.max - big, self.theclass.min)
918
919 def test_timetuple(self):
920 for i in range(7):
921 # January 2, 1956 is a Monday (0)
922 d = self.theclass(1956, 1, 2+i)
923 t = d.timetuple()
924 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
925 # February 1, 1956 is a Wednesday (2)
926 d = self.theclass(1956, 2, 1+i)
927 t = d.timetuple()
928 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
929 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
930 # of the year.
931 d = self.theclass(1956, 3, 1+i)
932 t = d.timetuple()
933 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
934 self.assertEqual(t.tm_year, 1956)
935 self.assertEqual(t.tm_mon, 3)
936 self.assertEqual(t.tm_mday, 1+i)
937 self.assertEqual(t.tm_hour, 0)
938 self.assertEqual(t.tm_min, 0)
939 self.assertEqual(t.tm_sec, 0)
940 self.assertEqual(t.tm_wday, (3+i)%7)
941 self.assertEqual(t.tm_yday, 61+i)
942 self.assertEqual(t.tm_isdst, -1)
943
944 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000945 args = 6, 7, 23
946 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000947 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000948 green = pickler.dumps(orig, proto)
949 derived = unpickler.loads(green)
950 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000951
952 def test_compare(self):
953 t1 = self.theclass(2, 3, 4)
954 t2 = self.theclass(2, 3, 4)
955 self.failUnless(t1 == t2)
956 self.failUnless(t1 <= t2)
957 self.failUnless(t1 >= t2)
958 self.failUnless(not t1 != t2)
959 self.failUnless(not t1 < t2)
960 self.failUnless(not t1 > t2)
961 self.assertEqual(cmp(t1, t2), 0)
962 self.assertEqual(cmp(t2, t1), 0)
963
964 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
965 t2 = self.theclass(*args) # this is larger than t1
966 self.failUnless(t1 < t2)
967 self.failUnless(t2 > t1)
968 self.failUnless(t1 <= t2)
969 self.failUnless(t2 >= t1)
970 self.failUnless(t1 != t2)
971 self.failUnless(t2 != t1)
972 self.failUnless(not t1 == t2)
973 self.failUnless(not t2 == t1)
974 self.failUnless(not t1 > t2)
975 self.failUnless(not t2 < t1)
976 self.failUnless(not t1 >= t2)
977 self.failUnless(not t2 <= t1)
978 self.assertEqual(cmp(t1, t2), -1)
979 self.assertEqual(cmp(t2, t1), 1)
980
Tim Peters68124bb2003-02-08 03:46:31 +0000981 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000982 self.assertEqual(t1 == badarg, False)
983 self.assertEqual(t1 != badarg, True)
984 self.assertEqual(badarg == t1, False)
985 self.assertEqual(badarg != t1, True)
986
Tim Peters2a799bf2002-12-16 20:18:38 +0000987 self.assertRaises(TypeError, lambda: t1 < badarg)
988 self.assertRaises(TypeError, lambda: t1 > badarg)
989 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000990 self.assertRaises(TypeError, lambda: badarg <= t1)
991 self.assertRaises(TypeError, lambda: badarg < t1)
992 self.assertRaises(TypeError, lambda: badarg > t1)
993 self.assertRaises(TypeError, lambda: badarg >= t1)
994
Tim Peters8d81a012003-01-24 22:36:34 +0000995 def test_mixed_compare(self):
996 our = self.theclass(2000, 4, 5)
997 self.assertRaises(TypeError, cmp, our, 1)
998 self.assertRaises(TypeError, cmp, 1, our)
999
1000 class AnotherDateTimeClass(object):
1001 def __cmp__(self, other):
1002 # Return "equal" so calling this can't be confused with
1003 # compare-by-address (which never says "equal" for distinct
1004 # objects).
1005 return 0
Nick Coghlan48361f52008-08-11 15:45:58 +00001006 __hash__ = None # Silence Py3k warning
Tim Peters8d81a012003-01-24 22:36:34 +00001007
1008 # This still errors, because date and datetime comparison raise
1009 # TypeError instead of NotImplemented when they don't know what to
1010 # do, in order to stop comparison from falling back to the default
1011 # compare-by-address.
1012 their = AnotherDateTimeClass()
1013 self.assertRaises(TypeError, cmp, our, their)
1014 # Oops: The next stab raises TypeError in the C implementation,
1015 # but not in the Python implementation of datetime. The difference
1016 # is due to that the Python implementation defines __cmp__ but
1017 # the C implementation defines tp_richcompare. This is more pain
1018 # to fix than it's worth, so commenting out the test.
1019 # self.assertEqual(cmp(their, our), 0)
1020
1021 # But date and datetime comparison return NotImplemented instead if the
1022 # other object has a timetuple attr. This gives the other object a
1023 # chance to do the comparison.
1024 class Comparable(AnotherDateTimeClass):
1025 def timetuple(self):
1026 return ()
1027
1028 their = Comparable()
1029 self.assertEqual(cmp(our, their), 0)
1030 self.assertEqual(cmp(their, our), 0)
1031 self.failUnless(our == their)
1032 self.failUnless(their == our)
1033
Tim Peters2a799bf2002-12-16 20:18:38 +00001034 def test_bool(self):
1035 # All dates are considered true.
1036 self.failUnless(self.theclass.min)
1037 self.failUnless(self.theclass.max)
1038
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001039 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001040 # For nasty technical reasons, we can't handle years before 1900.
1041 cls = self.theclass
1042 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1043 for y in 1, 49, 51, 99, 100, 1000, 1899:
1044 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001045
1046 def test_replace(self):
1047 cls = self.theclass
1048 args = [1, 2, 3]
1049 base = cls(*args)
1050 self.assertEqual(base, base.replace())
1051
1052 i = 0
1053 for name, newval in (("year", 2),
1054 ("month", 3),
1055 ("day", 4)):
1056 newargs = args[:]
1057 newargs[i] = newval
1058 expected = cls(*newargs)
1059 got = base.replace(**{name: newval})
1060 self.assertEqual(expected, got)
1061 i += 1
1062
1063 # Out of bounds.
1064 base = cls(2000, 2, 29)
1065 self.assertRaises(ValueError, base.replace, year=2001)
1066
Tim Petersa98924a2003-05-17 05:55:19 +00001067 def test_subclass_date(self):
1068
1069 class C(self.theclass):
1070 theAnswer = 42
1071
1072 def __new__(cls, *args, **kws):
1073 temp = kws.copy()
1074 extra = temp.pop('extra')
1075 result = self.theclass.__new__(cls, *args, **temp)
1076 result.extra = extra
1077 return result
1078
1079 def newmeth(self, start):
1080 return start + self.year + self.month
1081
1082 args = 2003, 4, 14
1083
1084 dt1 = self.theclass(*args)
1085 dt2 = C(*args, **{'extra': 7})
1086
1087 self.assertEqual(dt2.__class__, C)
1088 self.assertEqual(dt2.theAnswer, 42)
1089 self.assertEqual(dt2.extra, 7)
1090 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1091 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1092
Tim Peters604c0132004-06-07 23:04:33 +00001093 def test_pickling_subclass_date(self):
1094
1095 args = 6, 7, 23
1096 orig = SubclassDate(*args)
1097 for pickler, unpickler, proto in pickle_choices:
1098 green = pickler.dumps(orig, proto)
1099 derived = unpickler.loads(green)
1100 self.assertEqual(orig, derived)
1101
Tim Peters3f606292004-03-21 23:38:41 +00001102 def test_backdoor_resistance(self):
1103 # For fast unpickling, the constructor accepts a pickle string.
1104 # This is a low-overhead backdoor. A user can (by intent or
1105 # mistake) pass a string directly, which (if it's the right length)
1106 # will get treated like a pickle, and bypass the normal sanity
1107 # checks in the constructor. This can create insane objects.
1108 # The constructor doesn't want to burn the time to validate all
1109 # fields, but does check the month field. This stops, e.g.,
1110 # datetime.datetime('1995-03-25') from yielding an insane object.
1111 base = '1995-03-25'
1112 if not issubclass(self.theclass, datetime):
1113 base = base[:4]
1114 for month_byte in '9', chr(0), chr(13), '\xff':
1115 self.assertRaises(TypeError, self.theclass,
1116 base[:2] + month_byte + base[3:])
1117 for ord_byte in range(1, 13):
1118 # This shouldn't blow up because of the month byte alone. If
1119 # the implementation changes to do more-careful checking, it may
1120 # blow up because other fields are insane.
1121 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001122
Tim Peters2a799bf2002-12-16 20:18:38 +00001123#############################################################################
1124# datetime tests
1125
Tim Peters604c0132004-06-07 23:04:33 +00001126class SubclassDatetime(datetime):
1127 sub_var = 1
1128
Tim Peters2a799bf2002-12-16 20:18:38 +00001129class TestDateTime(TestDate):
1130
1131 theclass = datetime
1132
1133 def test_basic_attributes(self):
1134 dt = self.theclass(2002, 3, 1, 12, 0)
1135 self.assertEqual(dt.year, 2002)
1136 self.assertEqual(dt.month, 3)
1137 self.assertEqual(dt.day, 1)
1138 self.assertEqual(dt.hour, 12)
1139 self.assertEqual(dt.minute, 0)
1140 self.assertEqual(dt.second, 0)
1141 self.assertEqual(dt.microsecond, 0)
1142
1143 def test_basic_attributes_nonzero(self):
1144 # Make sure all attributes are non-zero so bugs in
1145 # bit-shifting access show up.
1146 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1147 self.assertEqual(dt.year, 2002)
1148 self.assertEqual(dt.month, 3)
1149 self.assertEqual(dt.day, 1)
1150 self.assertEqual(dt.hour, 12)
1151 self.assertEqual(dt.minute, 59)
1152 self.assertEqual(dt.second, 59)
1153 self.assertEqual(dt.microsecond, 8000)
1154
1155 def test_roundtrip(self):
1156 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1157 self.theclass.now()):
1158 # Verify dt -> string -> datetime identity.
1159 s = repr(dt)
1160 self.failUnless(s.startswith('datetime.'))
1161 s = s[9:]
1162 dt2 = eval(s)
1163 self.assertEqual(dt, dt2)
1164
1165 # Verify identity via reconstructing from pieces.
1166 dt2 = self.theclass(dt.year, dt.month, dt.day,
1167 dt.hour, dt.minute, dt.second,
1168 dt.microsecond)
1169 self.assertEqual(dt, dt2)
1170
1171 def test_isoformat(self):
1172 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1173 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1174 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1175 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arc7682d042009-12-29 22:39:49 +00001176 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001177 # str is ISO format with the separator forced to a blank.
1178 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1179
1180 t = self.theclass(2, 3, 2)
1181 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1182 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1183 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1184 # str is ISO format with the separator forced to a blank.
1185 self.assertEqual(str(t), "0002-03-02 00:00:00")
1186
Eric Smitha9f7d622008-02-17 19:46:49 +00001187 def test_format(self):
1188 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1189 self.assertEqual(dt.__format__(''), str(dt))
1190
1191 # check that a derived class's __str__() gets called
1192 class A(self.theclass):
1193 def __str__(self):
1194 return 'A'
1195 a = A(2007, 9, 10, 4, 5, 1, 123)
1196 self.assertEqual(a.__format__(''), 'A')
1197
1198 # check that a derived class's strftime gets called
1199 class B(self.theclass):
1200 def strftime(self, format_spec):
1201 return 'B'
1202 b = B(2007, 9, 10, 4, 5, 1, 123)
1203 self.assertEqual(b.__format__(''), str(dt))
1204
1205 for fmt in ["m:%m d:%d y:%y",
1206 "m:%m d:%d y:%y H:%H M:%M S:%S",
1207 "%z %Z",
1208 ]:
1209 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1210 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1211 self.assertEqual(b.__format__(fmt), 'B')
1212
Tim Peters2a799bf2002-12-16 20:18:38 +00001213 def test_more_ctime(self):
1214 # Test fields that TestDate doesn't touch.
1215 import time
1216
1217 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1218 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1219 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1220 # out. The difference is that t.ctime() produces " 2" for the day,
1221 # but platform ctime() produces "02" for the day. According to
1222 # C99, t.ctime() is correct here.
1223 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1224
1225 # So test a case where that difference doesn't matter.
1226 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1227 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1228
1229 def test_tz_independent_comparing(self):
1230 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1231 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1232 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1233 self.assertEqual(dt1, dt3)
1234 self.assert_(dt2 > dt3)
1235
1236 # Make sure comparison doesn't forget microseconds, and isn't done
1237 # via comparing a float timestamp (an IEEE double doesn't have enough
1238 # precision to span microsecond resolution across years 1 thru 9999,
1239 # so comparing via timestamp necessarily calls some distinct values
1240 # equal).
1241 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1242 us = timedelta(microseconds=1)
1243 dt2 = dt1 + us
1244 self.assertEqual(dt2 - dt1, us)
1245 self.assert_(dt1 < dt2)
1246
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001247 def test_strftime_with_bad_tzname_replace(self):
1248 # verify ok if tzinfo.tzname().replace() returns a non-string
1249 class MyTzInfo(FixedOffset):
1250 def tzname(self, dt):
1251 class MyStr(str):
1252 def replace(self, *args):
1253 return None
1254 return MyStr('name')
1255 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1256 self.assertRaises(TypeError, t.strftime, '%Z')
1257
Tim Peters2a799bf2002-12-16 20:18:38 +00001258 def test_bad_constructor_arguments(self):
1259 # bad years
1260 self.theclass(MINYEAR, 1, 1) # no exception
1261 self.theclass(MAXYEAR, 1, 1) # no exception
1262 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1263 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1264 # bad months
1265 self.theclass(2000, 1, 1) # no exception
1266 self.theclass(2000, 12, 1) # no exception
1267 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1268 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1269 # bad days
1270 self.theclass(2000, 2, 29) # no exception
1271 self.theclass(2004, 2, 29) # no exception
1272 self.theclass(2400, 2, 29) # no exception
1273 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1274 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1275 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1276 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1277 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1278 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1279 # bad hours
1280 self.theclass(2000, 1, 31, 0) # no exception
1281 self.theclass(2000, 1, 31, 23) # no exception
1282 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1283 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1284 # bad minutes
1285 self.theclass(2000, 1, 31, 23, 0) # no exception
1286 self.theclass(2000, 1, 31, 23, 59) # no exception
1287 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1288 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1289 # bad seconds
1290 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1291 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1292 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1293 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1294 # bad microseconds
1295 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1296 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1297 self.assertRaises(ValueError, self.theclass,
1298 2000, 1, 31, 23, 59, 59, -1)
1299 self.assertRaises(ValueError, self.theclass,
1300 2000, 1, 31, 23, 59, 59,
1301 1000000)
1302
1303 def test_hash_equality(self):
1304 d = self.theclass(2000, 12, 31, 23, 30, 17)
1305 e = self.theclass(2000, 12, 31, 23, 30, 17)
1306 self.assertEqual(d, e)
1307 self.assertEqual(hash(d), hash(e))
1308
1309 dic = {d: 1}
1310 dic[e] = 2
1311 self.assertEqual(len(dic), 1)
1312 self.assertEqual(dic[d], 2)
1313 self.assertEqual(dic[e], 2)
1314
1315 d = self.theclass(2001, 1, 1, 0, 5, 17)
1316 e = self.theclass(2001, 1, 1, 0, 5, 17)
1317 self.assertEqual(d, e)
1318 self.assertEqual(hash(d), hash(e))
1319
1320 dic = {d: 1}
1321 dic[e] = 2
1322 self.assertEqual(len(dic), 1)
1323 self.assertEqual(dic[d], 2)
1324 self.assertEqual(dic[e], 2)
1325
1326 def test_computations(self):
1327 a = self.theclass(2002, 1, 31)
1328 b = self.theclass(1956, 1, 31)
1329 diff = a-b
1330 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1331 self.assertEqual(diff.seconds, 0)
1332 self.assertEqual(diff.microseconds, 0)
1333 a = self.theclass(2002, 3, 2, 17, 6)
1334 millisec = timedelta(0, 0, 1000)
1335 hour = timedelta(0, 3600)
1336 day = timedelta(1)
1337 week = timedelta(7)
1338 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1339 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1340 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1341 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1342 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1343 self.assertEqual(a - hour, a + -hour)
1344 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1345 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1346 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1347 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1348 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1349 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1350 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1351 self.assertEqual((a + week) - a, week)
1352 self.assertEqual((a + day) - a, day)
1353 self.assertEqual((a + hour) - a, hour)
1354 self.assertEqual((a + millisec) - a, millisec)
1355 self.assertEqual((a - week) - a, -week)
1356 self.assertEqual((a - day) - a, -day)
1357 self.assertEqual((a - hour) - a, -hour)
1358 self.assertEqual((a - millisec) - a, -millisec)
1359 self.assertEqual(a - (a + week), -week)
1360 self.assertEqual(a - (a + day), -day)
1361 self.assertEqual(a - (a + hour), -hour)
1362 self.assertEqual(a - (a + millisec), -millisec)
1363 self.assertEqual(a - (a - week), week)
1364 self.assertEqual(a - (a - day), day)
1365 self.assertEqual(a - (a - hour), hour)
1366 self.assertEqual(a - (a - millisec), millisec)
1367 self.assertEqual(a + (week + day + hour + millisec),
1368 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1369 self.assertEqual(a + (week + day + hour + millisec),
1370 (((a + week) + day) + hour) + millisec)
1371 self.assertEqual(a - (week + day + hour + millisec),
1372 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1373 self.assertEqual(a - (week + day + hour + millisec),
1374 (((a - week) - day) - hour) - millisec)
1375 # Add/sub ints, longs, floats should be illegal
1376 for i in 1, 1L, 1.0:
1377 self.assertRaises(TypeError, lambda: a+i)
1378 self.assertRaises(TypeError, lambda: a-i)
1379 self.assertRaises(TypeError, lambda: i+a)
1380 self.assertRaises(TypeError, lambda: i-a)
1381
1382 # delta - datetime is senseless.
1383 self.assertRaises(TypeError, lambda: day - a)
1384 # mixing datetime and (delta or datetime) via * or // is senseless
1385 self.assertRaises(TypeError, lambda: day * a)
1386 self.assertRaises(TypeError, lambda: a * day)
1387 self.assertRaises(TypeError, lambda: day // a)
1388 self.assertRaises(TypeError, lambda: a // day)
1389 self.assertRaises(TypeError, lambda: a * a)
1390 self.assertRaises(TypeError, lambda: a // a)
1391 # datetime + datetime is senseless
1392 self.assertRaises(TypeError, lambda: a + a)
1393
1394 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001395 args = 6, 7, 23, 20, 59, 1, 64**2
1396 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001397 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001398 green = pickler.dumps(orig, proto)
1399 derived = unpickler.loads(green)
1400 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001401
Guido van Rossum275666f2003-02-07 21:49:01 +00001402 def test_more_pickling(self):
1403 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1404 s = pickle.dumps(a)
1405 b = pickle.loads(s)
1406 self.assertEqual(b.year, 2003)
1407 self.assertEqual(b.month, 2)
1408 self.assertEqual(b.day, 7)
1409
Tim Peters604c0132004-06-07 23:04:33 +00001410 def test_pickling_subclass_datetime(self):
1411 args = 6, 7, 23, 20, 59, 1, 64**2
1412 orig = SubclassDatetime(*args)
1413 for pickler, unpickler, proto in pickle_choices:
1414 green = pickler.dumps(orig, proto)
1415 derived = unpickler.loads(green)
1416 self.assertEqual(orig, derived)
1417
Tim Peters2a799bf2002-12-16 20:18:38 +00001418 def test_more_compare(self):
1419 # The test_compare() inherited from TestDate covers the error cases.
1420 # We just want to test lexicographic ordering on the members datetime
1421 # has that date lacks.
1422 args = [2000, 11, 29, 20, 58, 16, 999998]
1423 t1 = self.theclass(*args)
1424 t2 = self.theclass(*args)
1425 self.failUnless(t1 == t2)
1426 self.failUnless(t1 <= t2)
1427 self.failUnless(t1 >= t2)
1428 self.failUnless(not t1 != t2)
1429 self.failUnless(not t1 < t2)
1430 self.failUnless(not t1 > t2)
1431 self.assertEqual(cmp(t1, t2), 0)
1432 self.assertEqual(cmp(t2, t1), 0)
1433
1434 for i in range(len(args)):
1435 newargs = args[:]
1436 newargs[i] = args[i] + 1
1437 t2 = self.theclass(*newargs) # this is larger than t1
1438 self.failUnless(t1 < t2)
1439 self.failUnless(t2 > t1)
1440 self.failUnless(t1 <= t2)
1441 self.failUnless(t2 >= t1)
1442 self.failUnless(t1 != t2)
1443 self.failUnless(t2 != t1)
1444 self.failUnless(not t1 == t2)
1445 self.failUnless(not t2 == t1)
1446 self.failUnless(not t1 > t2)
1447 self.failUnless(not t2 < t1)
1448 self.failUnless(not t1 >= t2)
1449 self.failUnless(not t2 <= t1)
1450 self.assertEqual(cmp(t1, t2), -1)
1451 self.assertEqual(cmp(t2, t1), 1)
1452
1453
1454 # A helper for timestamp constructor tests.
1455 def verify_field_equality(self, expected, got):
1456 self.assertEqual(expected.tm_year, got.year)
1457 self.assertEqual(expected.tm_mon, got.month)
1458 self.assertEqual(expected.tm_mday, got.day)
1459 self.assertEqual(expected.tm_hour, got.hour)
1460 self.assertEqual(expected.tm_min, got.minute)
1461 self.assertEqual(expected.tm_sec, got.second)
1462
1463 def test_fromtimestamp(self):
1464 import time
1465
1466 ts = time.time()
1467 expected = time.localtime(ts)
1468 got = self.theclass.fromtimestamp(ts)
1469 self.verify_field_equality(expected, got)
1470
1471 def test_utcfromtimestamp(self):
1472 import time
1473
1474 ts = time.time()
1475 expected = time.gmtime(ts)
1476 got = self.theclass.utcfromtimestamp(ts)
1477 self.verify_field_equality(expected, got)
1478
Georg Brandl6d78a582006-04-28 19:09:24 +00001479 def test_microsecond_rounding(self):
1480 # Test whether fromtimestamp "rounds up" floats that are less
1481 # than one microsecond smaller than an integer.
1482 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1483 self.theclass.fromtimestamp(1))
1484
Tim Peters1b6f7a92004-06-20 02:50:16 +00001485 def test_insane_fromtimestamp(self):
1486 # It's possible that some platform maps time_t to double,
1487 # and that this test will fail there. This test should
1488 # exempt such platforms (provided they return reasonable
1489 # results!).
1490 for insane in -1e200, 1e200:
1491 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1492 insane)
1493
1494 def test_insane_utcfromtimestamp(self):
1495 # It's possible that some platform maps time_t to double,
1496 # and that this test will fail there. This test should
1497 # exempt such platforms (provided they return reasonable
1498 # results!).
1499 for insane in -1e200, 1e200:
1500 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1501 insane)
1502
Guido van Rossum2054ee92007-03-06 15:50:01 +00001503 def test_negative_float_fromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001504 # Windows doesn't accept negative timestamps
1505 if os.name == "nt":
1506 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001507 # The result is tz-dependent; at least test that this doesn't
1508 # fail (like it did before bug 1646728 was fixed).
1509 self.theclass.fromtimestamp(-1.05)
1510
1511 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001512 # Windows doesn't accept negative timestamps
1513 if os.name == "nt":
1514 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001515 d = self.theclass.utcfromtimestamp(-1.05)
1516 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1517
Tim Peters2a799bf2002-12-16 20:18:38 +00001518 def test_utcnow(self):
1519 import time
1520
1521 # Call it a success if utcnow() and utcfromtimestamp() are within
1522 # a second of each other.
1523 tolerance = timedelta(seconds=1)
1524 for dummy in range(3):
1525 from_now = self.theclass.utcnow()
1526 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1527 if abs(from_timestamp - from_now) <= tolerance:
1528 break
1529 # Else try again a few times.
1530 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1531
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001532 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001533 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001534
Skip Montanarofc070d22008-03-15 16:04:45 +00001535 string = '2004-12-01 13:02:47.197'
1536 format = '%Y-%m-%d %H:%M:%S.%f'
1537 result, frac = _strptime._strptime(string, format)
1538 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001539 got = self.theclass.strptime(string, format)
1540 self.assertEqual(expected, got)
1541
Tim Peters2a799bf2002-12-16 20:18:38 +00001542 def test_more_timetuple(self):
1543 # This tests fields beyond those tested by the TestDate.test_timetuple.
1544 t = self.theclass(2004, 12, 31, 6, 22, 33)
1545 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1546 self.assertEqual(t.timetuple(),
1547 (t.year, t.month, t.day,
1548 t.hour, t.minute, t.second,
1549 t.weekday(),
1550 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1551 -1))
1552 tt = t.timetuple()
1553 self.assertEqual(tt.tm_year, t.year)
1554 self.assertEqual(tt.tm_mon, t.month)
1555 self.assertEqual(tt.tm_mday, t.day)
1556 self.assertEqual(tt.tm_hour, t.hour)
1557 self.assertEqual(tt.tm_min, t.minute)
1558 self.assertEqual(tt.tm_sec, t.second)
1559 self.assertEqual(tt.tm_wday, t.weekday())
1560 self.assertEqual(tt.tm_yday, t.toordinal() -
1561 date(t.year, 1, 1).toordinal() + 1)
1562 self.assertEqual(tt.tm_isdst, -1)
1563
1564 def test_more_strftime(self):
1565 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001566 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1567 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1568 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001569
1570 def test_extract(self):
1571 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1572 self.assertEqual(dt.date(), date(2002, 3, 4))
1573 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1574
1575 def test_combine(self):
1576 d = date(2002, 3, 4)
1577 t = time(18, 45, 3, 1234)
1578 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1579 combine = self.theclass.combine
1580 dt = combine(d, t)
1581 self.assertEqual(dt, expected)
1582
1583 dt = combine(time=t, date=d)
1584 self.assertEqual(dt, expected)
1585
1586 self.assertEqual(d, dt.date())
1587 self.assertEqual(t, dt.time())
1588 self.assertEqual(dt, combine(dt.date(), dt.time()))
1589
1590 self.assertRaises(TypeError, combine) # need an arg
1591 self.assertRaises(TypeError, combine, d) # need two args
1592 self.assertRaises(TypeError, combine, t, d) # args reversed
1593 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1594 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1595
Tim Peters12bf3392002-12-24 05:41:27 +00001596 def test_replace(self):
1597 cls = self.theclass
1598 args = [1, 2, 3, 4, 5, 6, 7]
1599 base = cls(*args)
1600 self.assertEqual(base, base.replace())
1601
1602 i = 0
1603 for name, newval in (("year", 2),
1604 ("month", 3),
1605 ("day", 4),
1606 ("hour", 5),
1607 ("minute", 6),
1608 ("second", 7),
1609 ("microsecond", 8)):
1610 newargs = args[:]
1611 newargs[i] = newval
1612 expected = cls(*newargs)
1613 got = base.replace(**{name: newval})
1614 self.assertEqual(expected, got)
1615 i += 1
1616
1617 # Out of bounds.
1618 base = cls(2000, 2, 29)
1619 self.assertRaises(ValueError, base.replace, year=2001)
1620
Tim Peters80475bb2002-12-25 07:40:55 +00001621 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001622 # Pretty boring! The TZ test is more interesting here. astimezone()
1623 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001624 dt = self.theclass.now()
1625 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001626 self.assertRaises(TypeError, dt.astimezone) # not enough args
1627 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1628 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001629 self.assertRaises(ValueError, dt.astimezone, f) # naive
1630 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001631
Tim Peters52dcce22003-01-23 16:36:11 +00001632 class Bogus(tzinfo):
1633 def utcoffset(self, dt): return None
1634 def dst(self, dt): return timedelta(0)
1635 bog = Bogus()
1636 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1637
1638 class AlsoBogus(tzinfo):
1639 def utcoffset(self, dt): return timedelta(0)
1640 def dst(self, dt): return None
1641 alsobog = AlsoBogus()
1642 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001643
Tim Petersa98924a2003-05-17 05:55:19 +00001644 def test_subclass_datetime(self):
1645
1646 class C(self.theclass):
1647 theAnswer = 42
1648
1649 def __new__(cls, *args, **kws):
1650 temp = kws.copy()
1651 extra = temp.pop('extra')
1652 result = self.theclass.__new__(cls, *args, **temp)
1653 result.extra = extra
1654 return result
1655
1656 def newmeth(self, start):
1657 return start + self.year + self.month + self.second
1658
1659 args = 2003, 4, 14, 12, 13, 41
1660
1661 dt1 = self.theclass(*args)
1662 dt2 = C(*args, **{'extra': 7})
1663
1664 self.assertEqual(dt2.__class__, C)
1665 self.assertEqual(dt2.theAnswer, 42)
1666 self.assertEqual(dt2.extra, 7)
1667 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1668 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1669 dt1.second - 7)
1670
Tim Peters604c0132004-06-07 23:04:33 +00001671class SubclassTime(time):
1672 sub_var = 1
1673
Collin Winterc2898c52007-04-25 17:29:52 +00001674class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001675
1676 theclass = time
1677
1678 def test_basic_attributes(self):
1679 t = self.theclass(12, 0)
1680 self.assertEqual(t.hour, 12)
1681 self.assertEqual(t.minute, 0)
1682 self.assertEqual(t.second, 0)
1683 self.assertEqual(t.microsecond, 0)
1684
1685 def test_basic_attributes_nonzero(self):
1686 # Make sure all attributes are non-zero so bugs in
1687 # bit-shifting access show up.
1688 t = self.theclass(12, 59, 59, 8000)
1689 self.assertEqual(t.hour, 12)
1690 self.assertEqual(t.minute, 59)
1691 self.assertEqual(t.second, 59)
1692 self.assertEqual(t.microsecond, 8000)
1693
1694 def test_roundtrip(self):
1695 t = self.theclass(1, 2, 3, 4)
1696
1697 # Verify t -> string -> time identity.
1698 s = repr(t)
1699 self.failUnless(s.startswith('datetime.'))
1700 s = s[9:]
1701 t2 = eval(s)
1702 self.assertEqual(t, t2)
1703
1704 # Verify identity via reconstructing from pieces.
1705 t2 = self.theclass(t.hour, t.minute, t.second,
1706 t.microsecond)
1707 self.assertEqual(t, t2)
1708
1709 def test_comparing(self):
1710 args = [1, 2, 3, 4]
1711 t1 = self.theclass(*args)
1712 t2 = self.theclass(*args)
1713 self.failUnless(t1 == t2)
1714 self.failUnless(t1 <= t2)
1715 self.failUnless(t1 >= t2)
1716 self.failUnless(not t1 != t2)
1717 self.failUnless(not t1 < t2)
1718 self.failUnless(not t1 > t2)
1719 self.assertEqual(cmp(t1, t2), 0)
1720 self.assertEqual(cmp(t2, t1), 0)
1721
1722 for i in range(len(args)):
1723 newargs = args[:]
1724 newargs[i] = args[i] + 1
1725 t2 = self.theclass(*newargs) # this is larger than t1
1726 self.failUnless(t1 < t2)
1727 self.failUnless(t2 > t1)
1728 self.failUnless(t1 <= t2)
1729 self.failUnless(t2 >= t1)
1730 self.failUnless(t1 != t2)
1731 self.failUnless(t2 != t1)
1732 self.failUnless(not t1 == t2)
1733 self.failUnless(not t2 == t1)
1734 self.failUnless(not t1 > t2)
1735 self.failUnless(not t2 < t1)
1736 self.failUnless(not t1 >= t2)
1737 self.failUnless(not t2 <= t1)
1738 self.assertEqual(cmp(t1, t2), -1)
1739 self.assertEqual(cmp(t2, t1), 1)
1740
Tim Peters68124bb2003-02-08 03:46:31 +00001741 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001742 self.assertEqual(t1 == badarg, False)
1743 self.assertEqual(t1 != badarg, True)
1744 self.assertEqual(badarg == t1, False)
1745 self.assertEqual(badarg != t1, True)
1746
Tim Peters2a799bf2002-12-16 20:18:38 +00001747 self.assertRaises(TypeError, lambda: t1 <= badarg)
1748 self.assertRaises(TypeError, lambda: t1 < badarg)
1749 self.assertRaises(TypeError, lambda: t1 > badarg)
1750 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001751 self.assertRaises(TypeError, lambda: badarg <= t1)
1752 self.assertRaises(TypeError, lambda: badarg < t1)
1753 self.assertRaises(TypeError, lambda: badarg > t1)
1754 self.assertRaises(TypeError, lambda: badarg >= t1)
1755
1756 def test_bad_constructor_arguments(self):
1757 # bad hours
1758 self.theclass(0, 0) # no exception
1759 self.theclass(23, 0) # no exception
1760 self.assertRaises(ValueError, self.theclass, -1, 0)
1761 self.assertRaises(ValueError, self.theclass, 24, 0)
1762 # bad minutes
1763 self.theclass(23, 0) # no exception
1764 self.theclass(23, 59) # no exception
1765 self.assertRaises(ValueError, self.theclass, 23, -1)
1766 self.assertRaises(ValueError, self.theclass, 23, 60)
1767 # bad seconds
1768 self.theclass(23, 59, 0) # no exception
1769 self.theclass(23, 59, 59) # no exception
1770 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1771 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1772 # bad microseconds
1773 self.theclass(23, 59, 59, 0) # no exception
1774 self.theclass(23, 59, 59, 999999) # no exception
1775 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1776 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1777
1778 def test_hash_equality(self):
1779 d = self.theclass(23, 30, 17)
1780 e = self.theclass(23, 30, 17)
1781 self.assertEqual(d, e)
1782 self.assertEqual(hash(d), hash(e))
1783
1784 dic = {d: 1}
1785 dic[e] = 2
1786 self.assertEqual(len(dic), 1)
1787 self.assertEqual(dic[d], 2)
1788 self.assertEqual(dic[e], 2)
1789
1790 d = self.theclass(0, 5, 17)
1791 e = self.theclass(0, 5, 17)
1792 self.assertEqual(d, e)
1793 self.assertEqual(hash(d), hash(e))
1794
1795 dic = {d: 1}
1796 dic[e] = 2
1797 self.assertEqual(len(dic), 1)
1798 self.assertEqual(dic[d], 2)
1799 self.assertEqual(dic[e], 2)
1800
1801 def test_isoformat(self):
1802 t = self.theclass(4, 5, 1, 123)
1803 self.assertEqual(t.isoformat(), "04:05:01.000123")
1804 self.assertEqual(t.isoformat(), str(t))
1805
1806 t = self.theclass()
1807 self.assertEqual(t.isoformat(), "00:00:00")
1808 self.assertEqual(t.isoformat(), str(t))
1809
1810 t = self.theclass(microsecond=1)
1811 self.assertEqual(t.isoformat(), "00:00:00.000001")
1812 self.assertEqual(t.isoformat(), str(t))
1813
1814 t = self.theclass(microsecond=10)
1815 self.assertEqual(t.isoformat(), "00:00:00.000010")
1816 self.assertEqual(t.isoformat(), str(t))
1817
1818 t = self.theclass(microsecond=100)
1819 self.assertEqual(t.isoformat(), "00:00:00.000100")
1820 self.assertEqual(t.isoformat(), str(t))
1821
1822 t = self.theclass(microsecond=1000)
1823 self.assertEqual(t.isoformat(), "00:00:00.001000")
1824 self.assertEqual(t.isoformat(), str(t))
1825
1826 t = self.theclass(microsecond=10000)
1827 self.assertEqual(t.isoformat(), "00:00:00.010000")
1828 self.assertEqual(t.isoformat(), str(t))
1829
1830 t = self.theclass(microsecond=100000)
1831 self.assertEqual(t.isoformat(), "00:00:00.100000")
1832 self.assertEqual(t.isoformat(), str(t))
1833
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001834 def test_1653736(self):
1835 # verify it doesn't accept extra keyword arguments
1836 t = self.theclass(second=1)
1837 self.assertRaises(TypeError, t.isoformat, foo=3)
1838
Tim Peters2a799bf2002-12-16 20:18:38 +00001839 def test_strftime(self):
1840 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001841 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001842 # A naive object replaces %z and %Z with empty strings.
1843 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1844
Eric Smitha9f7d622008-02-17 19:46:49 +00001845 def test_format(self):
1846 t = self.theclass(1, 2, 3, 4)
1847 self.assertEqual(t.__format__(''), str(t))
1848
1849 # check that a derived class's __str__() gets called
1850 class A(self.theclass):
1851 def __str__(self):
1852 return 'A'
1853 a = A(1, 2, 3, 4)
1854 self.assertEqual(a.__format__(''), 'A')
1855
1856 # check that a derived class's strftime gets called
1857 class B(self.theclass):
1858 def strftime(self, format_spec):
1859 return 'B'
1860 b = B(1, 2, 3, 4)
1861 self.assertEqual(b.__format__(''), str(t))
1862
1863 for fmt in ['%H %M %S',
1864 ]:
1865 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1866 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1867 self.assertEqual(b.__format__(fmt), 'B')
1868
Tim Peters2a799bf2002-12-16 20:18:38 +00001869 def test_str(self):
1870 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1871 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1872 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1873 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1874 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1875
1876 def test_repr(self):
1877 name = 'datetime.' + self.theclass.__name__
1878 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1879 "%s(1, 2, 3, 4)" % name)
1880 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1881 "%s(10, 2, 3, 4000)" % name)
1882 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1883 "%s(0, 2, 3, 400000)" % name)
1884 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1885 "%s(12, 2, 3)" % name)
1886 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1887 "%s(23, 15)" % name)
1888
1889 def test_resolution_info(self):
1890 self.assert_(isinstance(self.theclass.min, self.theclass))
1891 self.assert_(isinstance(self.theclass.max, self.theclass))
1892 self.assert_(isinstance(self.theclass.resolution, timedelta))
1893 self.assert_(self.theclass.max > self.theclass.min)
1894
1895 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001896 args = 20, 59, 16, 64**2
1897 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001898 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001899 green = pickler.dumps(orig, proto)
1900 derived = unpickler.loads(green)
1901 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001902
Tim Peters604c0132004-06-07 23:04:33 +00001903 def test_pickling_subclass_time(self):
1904 args = 20, 59, 16, 64**2
1905 orig = SubclassTime(*args)
1906 for pickler, unpickler, proto in pickle_choices:
1907 green = pickler.dumps(orig, proto)
1908 derived = unpickler.loads(green)
1909 self.assertEqual(orig, derived)
1910
Tim Peters2a799bf2002-12-16 20:18:38 +00001911 def test_bool(self):
1912 cls = self.theclass
1913 self.failUnless(cls(1))
1914 self.failUnless(cls(0, 1))
1915 self.failUnless(cls(0, 0, 1))
1916 self.failUnless(cls(0, 0, 0, 1))
1917 self.failUnless(not cls(0))
1918 self.failUnless(not cls())
1919
Tim Peters12bf3392002-12-24 05:41:27 +00001920 def test_replace(self):
1921 cls = self.theclass
1922 args = [1, 2, 3, 4]
1923 base = cls(*args)
1924 self.assertEqual(base, base.replace())
1925
1926 i = 0
1927 for name, newval in (("hour", 5),
1928 ("minute", 6),
1929 ("second", 7),
1930 ("microsecond", 8)):
1931 newargs = args[:]
1932 newargs[i] = newval
1933 expected = cls(*newargs)
1934 got = base.replace(**{name: newval})
1935 self.assertEqual(expected, got)
1936 i += 1
1937
1938 # Out of bounds.
1939 base = cls(1)
1940 self.assertRaises(ValueError, base.replace, hour=24)
1941 self.assertRaises(ValueError, base.replace, minute=-1)
1942 self.assertRaises(ValueError, base.replace, second=100)
1943 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1944
Tim Petersa98924a2003-05-17 05:55:19 +00001945 def test_subclass_time(self):
1946
1947 class C(self.theclass):
1948 theAnswer = 42
1949
1950 def __new__(cls, *args, **kws):
1951 temp = kws.copy()
1952 extra = temp.pop('extra')
1953 result = self.theclass.__new__(cls, *args, **temp)
1954 result.extra = extra
1955 return result
1956
1957 def newmeth(self, start):
1958 return start + self.hour + self.second
1959
1960 args = 4, 5, 6
1961
1962 dt1 = self.theclass(*args)
1963 dt2 = C(*args, **{'extra': 7})
1964
1965 self.assertEqual(dt2.__class__, C)
1966 self.assertEqual(dt2.theAnswer, 42)
1967 self.assertEqual(dt2.extra, 7)
1968 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1969 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1970
Armin Rigof4afb212005-11-07 07:15:48 +00001971 def test_backdoor_resistance(self):
1972 # see TestDate.test_backdoor_resistance().
1973 base = '2:59.0'
1974 for hour_byte in ' ', '9', chr(24), '\xff':
1975 self.assertRaises(TypeError, self.theclass,
1976 hour_byte + base[1:])
1977
Tim Peters855fe882002-12-22 03:43:39 +00001978# A mixin for classes with a tzinfo= argument. Subclasses must define
1979# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001980# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001981class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001982
Tim Petersbad8ff02002-12-30 20:52:32 +00001983 def test_argument_passing(self):
1984 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001985 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001986 class introspective(tzinfo):
1987 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001988 def utcoffset(self, dt):
1989 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001990 dst = utcoffset
1991
1992 obj = cls(1, 2, 3, tzinfo=introspective())
1993
Tim Peters0bf60bd2003-01-08 20:40:01 +00001994 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001995 self.assertEqual(obj.tzname(), expected)
1996
Tim Peters0bf60bd2003-01-08 20:40:01 +00001997 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001998 self.assertEqual(obj.utcoffset(), expected)
1999 self.assertEqual(obj.dst(), expected)
2000
Tim Peters855fe882002-12-22 03:43:39 +00002001 def test_bad_tzinfo_classes(self):
2002 cls = self.theclass
2003 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002004
Tim Peters855fe882002-12-22 03:43:39 +00002005 class NiceTry(object):
2006 def __init__(self): pass
2007 def utcoffset(self, dt): pass
2008 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2009
2010 class BetterTry(tzinfo):
2011 def __init__(self): pass
2012 def utcoffset(self, dt): pass
2013 b = BetterTry()
2014 t = cls(1, 1, 1, tzinfo=b)
2015 self.failUnless(t.tzinfo is b)
2016
2017 def test_utc_offset_out_of_bounds(self):
2018 class Edgy(tzinfo):
2019 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002020 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002021 def utcoffset(self, dt):
2022 return self.offset
2023
2024 cls = self.theclass
2025 for offset, legit in ((-1440, False),
2026 (-1439, True),
2027 (1439, True),
2028 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002029 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002030 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002031 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002032 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002033 else:
2034 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002035 if legit:
2036 aofs = abs(offset)
2037 h, m = divmod(aofs, 60)
2038 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002039 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002040 t = t.timetz()
2041 self.assertEqual(str(t), "01:02:03" + tag)
2042 else:
2043 self.assertRaises(ValueError, str, t)
2044
2045 def test_tzinfo_classes(self):
2046 cls = self.theclass
2047 class C1(tzinfo):
2048 def utcoffset(self, dt): return None
2049 def dst(self, dt): return None
2050 def tzname(self, dt): return None
2051 for t in (cls(1, 1, 1),
2052 cls(1, 1, 1, tzinfo=None),
2053 cls(1, 1, 1, tzinfo=C1())):
2054 self.failUnless(t.utcoffset() is None)
2055 self.failUnless(t.dst() is None)
2056 self.failUnless(t.tzname() is None)
2057
Tim Peters855fe882002-12-22 03:43:39 +00002058 class C3(tzinfo):
2059 def utcoffset(self, dt): return timedelta(minutes=-1439)
2060 def dst(self, dt): return timedelta(minutes=1439)
2061 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002062 t = cls(1, 1, 1, tzinfo=C3())
2063 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2064 self.assertEqual(t.dst(), timedelta(minutes=1439))
2065 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002066
2067 # Wrong types.
2068 class C4(tzinfo):
2069 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002070 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002071 def tzname(self, dt): return 0
2072 t = cls(1, 1, 1, tzinfo=C4())
2073 self.assertRaises(TypeError, t.utcoffset)
2074 self.assertRaises(TypeError, t.dst)
2075 self.assertRaises(TypeError, t.tzname)
2076
2077 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002078 class C6(tzinfo):
2079 def utcoffset(self, dt): return timedelta(hours=-24)
2080 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002081 t = cls(1, 1, 1, tzinfo=C6())
2082 self.assertRaises(ValueError, t.utcoffset)
2083 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002084
2085 # Not a whole number of minutes.
2086 class C7(tzinfo):
2087 def utcoffset(self, dt): return timedelta(seconds=61)
2088 def dst(self, dt): return timedelta(microseconds=-81)
2089 t = cls(1, 1, 1, tzinfo=C7())
2090 self.assertRaises(ValueError, t.utcoffset)
2091 self.assertRaises(ValueError, t.dst)
2092
Tim Peters4c0db782002-12-26 05:01:19 +00002093 def test_aware_compare(self):
2094 cls = self.theclass
2095
Tim Peters60c76e42002-12-27 00:41:11 +00002096 # Ensure that utcoffset() gets ignored if the comparands have
2097 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002098 class OperandDependentOffset(tzinfo):
2099 def utcoffset(self, t):
2100 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002101 # d0 and d1 equal after adjustment
2102 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002103 else:
Tim Peters397301e2003-01-02 21:28:08 +00002104 # d2 off in the weeds
2105 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002106
2107 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2108 d0 = base.replace(minute=3)
2109 d1 = base.replace(minute=9)
2110 d2 = base.replace(minute=11)
2111 for x in d0, d1, d2:
2112 for y in d0, d1, d2:
2113 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002114 expected = cmp(x.minute, y.minute)
2115 self.assertEqual(got, expected)
2116
2117 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002118 # Note that a time can't actually have an operand-depedent offset,
2119 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2120 # so skip this test for time.
2121 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002122 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2123 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2124 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2125 for x in d0, d1, d2:
2126 for y in d0, d1, d2:
2127 got = cmp(x, y)
2128 if (x is d0 or x is d1) and (y is d0 or y is d1):
2129 expected = 0
2130 elif x is y is d2:
2131 expected = 0
2132 elif x is d2:
2133 expected = -1
2134 else:
2135 assert y is d2
2136 expected = 1
2137 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002138
Tim Peters855fe882002-12-22 03:43:39 +00002139
Tim Peters0bf60bd2003-01-08 20:40:01 +00002140# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002141class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002142 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002143
2144 def test_empty(self):
2145 t = self.theclass()
2146 self.assertEqual(t.hour, 0)
2147 self.assertEqual(t.minute, 0)
2148 self.assertEqual(t.second, 0)
2149 self.assertEqual(t.microsecond, 0)
2150 self.failUnless(t.tzinfo is None)
2151
Tim Peters2a799bf2002-12-16 20:18:38 +00002152 def test_zones(self):
2153 est = FixedOffset(-300, "EST", 1)
2154 utc = FixedOffset(0, "UTC", -2)
2155 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002156 t1 = time( 7, 47, tzinfo=est)
2157 t2 = time(12, 47, tzinfo=utc)
2158 t3 = time(13, 47, tzinfo=met)
2159 t4 = time(microsecond=40)
2160 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002161
2162 self.assertEqual(t1.tzinfo, est)
2163 self.assertEqual(t2.tzinfo, utc)
2164 self.assertEqual(t3.tzinfo, met)
2165 self.failUnless(t4.tzinfo is None)
2166 self.assertEqual(t5.tzinfo, utc)
2167
Tim Peters855fe882002-12-22 03:43:39 +00002168 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2169 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2170 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002171 self.failUnless(t4.utcoffset() is None)
2172 self.assertRaises(TypeError, t1.utcoffset, "no args")
2173
2174 self.assertEqual(t1.tzname(), "EST")
2175 self.assertEqual(t2.tzname(), "UTC")
2176 self.assertEqual(t3.tzname(), "MET")
2177 self.failUnless(t4.tzname() is None)
2178 self.assertRaises(TypeError, t1.tzname, "no args")
2179
Tim Peters855fe882002-12-22 03:43:39 +00002180 self.assertEqual(t1.dst(), timedelta(minutes=1))
2181 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2182 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002183 self.failUnless(t4.dst() is None)
2184 self.assertRaises(TypeError, t1.dst, "no args")
2185
2186 self.assertEqual(hash(t1), hash(t2))
2187 self.assertEqual(hash(t1), hash(t3))
2188 self.assertEqual(hash(t2), hash(t3))
2189
2190 self.assertEqual(t1, t2)
2191 self.assertEqual(t1, t3)
2192 self.assertEqual(t2, t3)
2193 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2194 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2195 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2196
2197 self.assertEqual(str(t1), "07:47:00-05:00")
2198 self.assertEqual(str(t2), "12:47:00+00:00")
2199 self.assertEqual(str(t3), "13:47:00+01:00")
2200 self.assertEqual(str(t4), "00:00:00.000040")
2201 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2202
2203 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2204 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2205 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2206 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2207 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2208
Tim Peters0bf60bd2003-01-08 20:40:01 +00002209 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002210 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2211 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2212 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2213 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2214 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2215
2216 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2217 "07:47:00 %Z=EST %z=-0500")
2218 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2219 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2220
2221 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002222 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002223 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2224 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2225
Tim Petersb92bb712002-12-21 17:44:07 +00002226 # Check that an invalid tzname result raises an exception.
2227 class Badtzname(tzinfo):
2228 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002229 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002230 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2231 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002232
2233 def test_hash_edge_cases(self):
2234 # Offsets that overflow a basic time.
2235 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2236 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2237 self.assertEqual(hash(t1), hash(t2))
2238
2239 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2240 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2241 self.assertEqual(hash(t1), hash(t2))
2242
Tim Peters2a799bf2002-12-16 20:18:38 +00002243 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002244 # Try one without a tzinfo.
2245 args = 20, 59, 16, 64**2
2246 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002247 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002248 green = pickler.dumps(orig, proto)
2249 derived = unpickler.loads(green)
2250 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002251
2252 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002253 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002254 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002255 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002256 green = pickler.dumps(orig, proto)
2257 derived = unpickler.loads(green)
2258 self.assertEqual(orig, derived)
2259 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2260 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2261 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002262
2263 def test_more_bool(self):
2264 # Test cases with non-None tzinfo.
2265 cls = self.theclass
2266
2267 t = cls(0, tzinfo=FixedOffset(-300, ""))
2268 self.failUnless(t)
2269
2270 t = cls(5, tzinfo=FixedOffset(-300, ""))
2271 self.failUnless(t)
2272
2273 t = cls(5, tzinfo=FixedOffset(300, ""))
2274 self.failUnless(not t)
2275
2276 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2277 self.failUnless(not t)
2278
2279 # Mostly ensuring this doesn't overflow internally.
2280 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2281 self.failUnless(t)
2282
2283 # But this should yield a value error -- the utcoffset is bogus.
2284 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2285 self.assertRaises(ValueError, lambda: bool(t))
2286
2287 # Likewise.
2288 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2289 self.assertRaises(ValueError, lambda: bool(t))
2290
Tim Peters12bf3392002-12-24 05:41:27 +00002291 def test_replace(self):
2292 cls = self.theclass
2293 z100 = FixedOffset(100, "+100")
2294 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2295 args = [1, 2, 3, 4, z100]
2296 base = cls(*args)
2297 self.assertEqual(base, base.replace())
2298
2299 i = 0
2300 for name, newval in (("hour", 5),
2301 ("minute", 6),
2302 ("second", 7),
2303 ("microsecond", 8),
2304 ("tzinfo", zm200)):
2305 newargs = args[:]
2306 newargs[i] = newval
2307 expected = cls(*newargs)
2308 got = base.replace(**{name: newval})
2309 self.assertEqual(expected, got)
2310 i += 1
2311
2312 # Ensure we can get rid of a tzinfo.
2313 self.assertEqual(base.tzname(), "+100")
2314 base2 = base.replace(tzinfo=None)
2315 self.failUnless(base2.tzinfo is None)
2316 self.failUnless(base2.tzname() is None)
2317
2318 # Ensure we can add one.
2319 base3 = base2.replace(tzinfo=z100)
2320 self.assertEqual(base, base3)
2321 self.failUnless(base.tzinfo is base3.tzinfo)
2322
2323 # Out of bounds.
2324 base = cls(1)
2325 self.assertRaises(ValueError, base.replace, hour=24)
2326 self.assertRaises(ValueError, base.replace, minute=-1)
2327 self.assertRaises(ValueError, base.replace, second=100)
2328 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2329
Tim Peters60c76e42002-12-27 00:41:11 +00002330 def test_mixed_compare(self):
2331 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002332 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002333 self.assertEqual(t1, t2)
2334 t2 = t2.replace(tzinfo=None)
2335 self.assertEqual(t1, t2)
2336 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2337 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002338 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2339 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002340
Tim Peters0bf60bd2003-01-08 20:40:01 +00002341 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002342 class Varies(tzinfo):
2343 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002344 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002345 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002346 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002347 return self.offset
2348
2349 v = Varies()
2350 t1 = t2.replace(tzinfo=v)
2351 t2 = t2.replace(tzinfo=v)
2352 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2353 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2354 self.assertEqual(t1, t2)
2355
2356 # But if they're not identical, it isn't ignored.
2357 t2 = t2.replace(tzinfo=Varies())
2358 self.failUnless(t1 < t2) # t1's offset counter still going up
2359
Tim Petersa98924a2003-05-17 05:55:19 +00002360 def test_subclass_timetz(self):
2361
2362 class C(self.theclass):
2363 theAnswer = 42
2364
2365 def __new__(cls, *args, **kws):
2366 temp = kws.copy()
2367 extra = temp.pop('extra')
2368 result = self.theclass.__new__(cls, *args, **temp)
2369 result.extra = extra
2370 return result
2371
2372 def newmeth(self, start):
2373 return start + self.hour + self.second
2374
2375 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2376
2377 dt1 = self.theclass(*args)
2378 dt2 = C(*args, **{'extra': 7})
2379
2380 self.assertEqual(dt2.__class__, C)
2381 self.assertEqual(dt2.theAnswer, 42)
2382 self.assertEqual(dt2.extra, 7)
2383 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2384 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2385
Tim Peters4c0db782002-12-26 05:01:19 +00002386
Tim Peters0bf60bd2003-01-08 20:40:01 +00002387# Testing datetime objects with a non-None tzinfo.
2388
Collin Winterc2898c52007-04-25 17:29:52 +00002389class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002390 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002391
2392 def test_trivial(self):
2393 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2394 self.assertEqual(dt.year, 1)
2395 self.assertEqual(dt.month, 2)
2396 self.assertEqual(dt.day, 3)
2397 self.assertEqual(dt.hour, 4)
2398 self.assertEqual(dt.minute, 5)
2399 self.assertEqual(dt.second, 6)
2400 self.assertEqual(dt.microsecond, 7)
2401 self.assertEqual(dt.tzinfo, None)
2402
2403 def test_even_more_compare(self):
2404 # The test_compare() and test_more_compare() inherited from TestDate
2405 # and TestDateTime covered non-tzinfo cases.
2406
2407 # Smallest possible after UTC adjustment.
2408 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2409 # Largest possible after UTC adjustment.
2410 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2411 tzinfo=FixedOffset(-1439, ""))
2412
2413 # Make sure those compare correctly, and w/o overflow.
2414 self.failUnless(t1 < t2)
2415 self.failUnless(t1 != t2)
2416 self.failUnless(t2 > t1)
2417
2418 self.failUnless(t1 == t1)
2419 self.failUnless(t2 == t2)
2420
2421 # Equal afer adjustment.
2422 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2423 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2424 self.assertEqual(t1, t2)
2425
2426 # Change t1 not to subtract a minute, and t1 should be larger.
2427 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2428 self.failUnless(t1 > t2)
2429
2430 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2431 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2432 self.failUnless(t1 < t2)
2433
2434 # Back to the original t1, but make seconds resolve it.
2435 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2436 second=1)
2437 self.failUnless(t1 > t2)
2438
2439 # Likewise, but make microseconds resolve it.
2440 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2441 microsecond=1)
2442 self.failUnless(t1 > t2)
2443
2444 # Make t2 naive and it should fail.
2445 t2 = self.theclass.min
2446 self.assertRaises(TypeError, lambda: t1 == t2)
2447 self.assertEqual(t2, t2)
2448
2449 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2450 class Naive(tzinfo):
2451 def utcoffset(self, dt): return None
2452 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2453 self.assertRaises(TypeError, lambda: t1 == t2)
2454 self.assertEqual(t2, t2)
2455
2456 # OTOH, it's OK to compare two of these mixing the two ways of being
2457 # naive.
2458 t1 = self.theclass(5, 6, 7)
2459 self.assertEqual(t1, t2)
2460
2461 # Try a bogus uctoffset.
2462 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002463 def utcoffset(self, dt):
2464 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002465 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2466 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002467 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002468
Tim Peters2a799bf2002-12-16 20:18:38 +00002469 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002470 # Try one without a tzinfo.
2471 args = 6, 7, 23, 20, 59, 1, 64**2
2472 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002473 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002474 green = pickler.dumps(orig, proto)
2475 derived = unpickler.loads(green)
2476 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002477
2478 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002479 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002480 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002481 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002482 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002483 green = pickler.dumps(orig, proto)
2484 derived = unpickler.loads(green)
2485 self.assertEqual(orig, derived)
2486 self.failUnless(isinstance(derived.tzinfo,
2487 PicklableFixedOffset))
2488 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2489 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002490
2491 def test_extreme_hashes(self):
2492 # If an attempt is made to hash these via subtracting the offset
2493 # then hashing a datetime object, OverflowError results. The
2494 # Python implementation used to blow up here.
2495 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2496 hash(t)
2497 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2498 tzinfo=FixedOffset(-1439, ""))
2499 hash(t)
2500
2501 # OTOH, an OOB offset should blow up.
2502 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2503 self.assertRaises(ValueError, hash, t)
2504
2505 def test_zones(self):
2506 est = FixedOffset(-300, "EST")
2507 utc = FixedOffset(0, "UTC")
2508 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002509 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2510 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2511 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002512 self.assertEqual(t1.tzinfo, est)
2513 self.assertEqual(t2.tzinfo, utc)
2514 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002515 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2516 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2517 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002518 self.assertEqual(t1.tzname(), "EST")
2519 self.assertEqual(t2.tzname(), "UTC")
2520 self.assertEqual(t3.tzname(), "MET")
2521 self.assertEqual(hash(t1), hash(t2))
2522 self.assertEqual(hash(t1), hash(t3))
2523 self.assertEqual(hash(t2), hash(t3))
2524 self.assertEqual(t1, t2)
2525 self.assertEqual(t1, t3)
2526 self.assertEqual(t2, t3)
2527 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2528 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2529 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002530 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002531 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2532 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2533 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2534
2535 def test_combine(self):
2536 met = FixedOffset(60, "MET")
2537 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002538 tz = time(18, 45, 3, 1234, tzinfo=met)
2539 dt = datetime.combine(d, tz)
2540 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002541 tzinfo=met))
2542
2543 def test_extract(self):
2544 met = FixedOffset(60, "MET")
2545 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2546 self.assertEqual(dt.date(), date(2002, 3, 4))
2547 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002548 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002549
2550 def test_tz_aware_arithmetic(self):
2551 import random
2552
2553 now = self.theclass.now()
2554 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002555 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002556 nowaware = self.theclass.combine(now.date(), timeaware)
2557 self.failUnless(nowaware.tzinfo is tz55)
2558 self.assertEqual(nowaware.timetz(), timeaware)
2559
2560 # Can't mix aware and non-aware.
2561 self.assertRaises(TypeError, lambda: now - nowaware)
2562 self.assertRaises(TypeError, lambda: nowaware - now)
2563
Tim Peters0bf60bd2003-01-08 20:40:01 +00002564 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002565 self.assertRaises(TypeError, lambda: now + nowaware)
2566 self.assertRaises(TypeError, lambda: nowaware + now)
2567 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2568
2569 # Subtracting should yield 0.
2570 self.assertEqual(now - now, timedelta(0))
2571 self.assertEqual(nowaware - nowaware, timedelta(0))
2572
2573 # Adding a delta should preserve tzinfo.
2574 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2575 nowawareplus = nowaware + delta
2576 self.failUnless(nowaware.tzinfo is tz55)
2577 nowawareplus2 = delta + nowaware
2578 self.failUnless(nowawareplus2.tzinfo is tz55)
2579 self.assertEqual(nowawareplus, nowawareplus2)
2580
2581 # that - delta should be what we started with, and that - what we
2582 # started with should be delta.
2583 diff = nowawareplus - delta
2584 self.failUnless(diff.tzinfo is tz55)
2585 self.assertEqual(nowaware, diff)
2586 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2587 self.assertEqual(nowawareplus - nowaware, delta)
2588
2589 # Make up a random timezone.
2590 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002591 # Attach it to nowawareplus.
2592 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002593 self.failUnless(nowawareplus.tzinfo is tzr)
2594 # Make sure the difference takes the timezone adjustments into account.
2595 got = nowaware - nowawareplus
2596 # Expected: (nowaware base - nowaware offset) -
2597 # (nowawareplus base - nowawareplus offset) =
2598 # (nowaware base - nowawareplus base) +
2599 # (nowawareplus offset - nowaware offset) =
2600 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002601 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002602 self.assertEqual(got, expected)
2603
2604 # Try max possible difference.
2605 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2606 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2607 tzinfo=FixedOffset(-1439, "max"))
2608 maxdiff = max - min
2609 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2610 timedelta(minutes=2*1439))
2611
2612 def test_tzinfo_now(self):
2613 meth = self.theclass.now
2614 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2615 base = meth()
2616 # Try with and without naming the keyword.
2617 off42 = FixedOffset(42, "42")
2618 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002619 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002620 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002621 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002622 # Bad argument with and w/o naming the keyword.
2623 self.assertRaises(TypeError, meth, 16)
2624 self.assertRaises(TypeError, meth, tzinfo=16)
2625 # Bad keyword name.
2626 self.assertRaises(TypeError, meth, tinfo=off42)
2627 # Too many args.
2628 self.assertRaises(TypeError, meth, off42, off42)
2629
Tim Peters10cadce2003-01-23 19:58:02 +00002630 # We don't know which time zone we're in, and don't have a tzinfo
2631 # class to represent it, so seeing whether a tz argument actually
2632 # does a conversion is tricky.
2633 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2634 utc = FixedOffset(0, "utc", 0)
2635 for dummy in range(3):
2636 now = datetime.now(weirdtz)
2637 self.failUnless(now.tzinfo is weirdtz)
2638 utcnow = datetime.utcnow().replace(tzinfo=utc)
2639 now2 = utcnow.astimezone(weirdtz)
2640 if abs(now - now2) < timedelta(seconds=30):
2641 break
2642 # Else the code is broken, or more than 30 seconds passed between
2643 # calls; assuming the latter, just try again.
2644 else:
2645 # Three strikes and we're out.
2646 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2647
Tim Peters2a799bf2002-12-16 20:18:38 +00002648 def test_tzinfo_fromtimestamp(self):
2649 import time
2650 meth = self.theclass.fromtimestamp
2651 ts = time.time()
2652 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2653 base = meth(ts)
2654 # Try with and without naming the keyword.
2655 off42 = FixedOffset(42, "42")
2656 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002657 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002658 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002659 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002660 # Bad argument with and w/o naming the keyword.
2661 self.assertRaises(TypeError, meth, ts, 16)
2662 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2663 # Bad keyword name.
2664 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2665 # Too many args.
2666 self.assertRaises(TypeError, meth, ts, off42, off42)
2667 # Too few args.
2668 self.assertRaises(TypeError, meth)
2669
Tim Peters2a44a8d2003-01-23 20:53:10 +00002670 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002671 timestamp = 1000000000
2672 utcdatetime = datetime.utcfromtimestamp(timestamp)
2673 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2674 # But on some flavor of Mac, it's nowhere near that. So we can't have
2675 # any idea here what time that actually is, we can only test that
2676 # relative changes match.
2677 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2678 tz = FixedOffset(utcoffset, "tz", 0)
2679 expected = utcdatetime + utcoffset
2680 got = datetime.fromtimestamp(timestamp, tz)
2681 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002682
Tim Peters2a799bf2002-12-16 20:18:38 +00002683 def test_tzinfo_utcnow(self):
2684 meth = self.theclass.utcnow
2685 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2686 base = meth()
2687 # Try with and without naming the keyword; for whatever reason,
2688 # utcnow() doesn't accept a tzinfo argument.
2689 off42 = FixedOffset(42, "42")
2690 self.assertRaises(TypeError, meth, off42)
2691 self.assertRaises(TypeError, meth, tzinfo=off42)
2692
2693 def test_tzinfo_utcfromtimestamp(self):
2694 import time
2695 meth = self.theclass.utcfromtimestamp
2696 ts = time.time()
2697 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2698 base = meth(ts)
2699 # Try with and without naming the keyword; for whatever reason,
2700 # utcfromtimestamp() doesn't accept a tzinfo argument.
2701 off42 = FixedOffset(42, "42")
2702 self.assertRaises(TypeError, meth, ts, off42)
2703 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2704
2705 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002706 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002707 # DST flag.
2708 class DST(tzinfo):
2709 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002710 if isinstance(dstvalue, int):
2711 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002712 self.dstvalue = dstvalue
2713 def dst(self, dt):
2714 return self.dstvalue
2715
2716 cls = self.theclass
2717 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2718 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2719 t = d.timetuple()
2720 self.assertEqual(1, t.tm_year)
2721 self.assertEqual(1, t.tm_mon)
2722 self.assertEqual(1, t.tm_mday)
2723 self.assertEqual(10, t.tm_hour)
2724 self.assertEqual(20, t.tm_min)
2725 self.assertEqual(30, t.tm_sec)
2726 self.assertEqual(0, t.tm_wday)
2727 self.assertEqual(1, t.tm_yday)
2728 self.assertEqual(flag, t.tm_isdst)
2729
2730 # dst() returns wrong type.
2731 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2732
2733 # dst() at the edge.
2734 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2735 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2736
2737 # dst() out of range.
2738 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2739 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2740
2741 def test_utctimetuple(self):
2742 class DST(tzinfo):
2743 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002744 if isinstance(dstvalue, int):
2745 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002746 self.dstvalue = dstvalue
2747 def dst(self, dt):
2748 return self.dstvalue
2749
2750 cls = self.theclass
2751 # This can't work: DST didn't implement utcoffset.
2752 self.assertRaises(NotImplementedError,
2753 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2754
2755 class UOFS(DST):
2756 def __init__(self, uofs, dofs=None):
2757 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002758 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002759 def utcoffset(self, dt):
2760 return self.uofs
2761
2762 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2763 # in effect for a UTC time.
2764 for dstvalue in -33, 33, 0, None:
2765 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2766 t = d.utctimetuple()
2767 self.assertEqual(d.year, t.tm_year)
2768 self.assertEqual(d.month, t.tm_mon)
2769 self.assertEqual(d.day, t.tm_mday)
2770 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2771 self.assertEqual(13, t.tm_min)
2772 self.assertEqual(d.second, t.tm_sec)
2773 self.assertEqual(d.weekday(), t.tm_wday)
2774 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2775 t.tm_yday)
2776 self.assertEqual(0, t.tm_isdst)
2777
2778 # At the edges, UTC adjustment can normalize into years out-of-range
2779 # for a datetime object. Ensure that a correct timetuple is
2780 # created anyway.
2781 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2782 # That goes back 1 minute less than a full day.
2783 t = tiny.utctimetuple()
2784 self.assertEqual(t.tm_year, MINYEAR-1)
2785 self.assertEqual(t.tm_mon, 12)
2786 self.assertEqual(t.tm_mday, 31)
2787 self.assertEqual(t.tm_hour, 0)
2788 self.assertEqual(t.tm_min, 1)
2789 self.assertEqual(t.tm_sec, 37)
2790 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2791 self.assertEqual(t.tm_isdst, 0)
2792
2793 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2794 # That goes forward 1 minute less than a full day.
2795 t = huge.utctimetuple()
2796 self.assertEqual(t.tm_year, MAXYEAR+1)
2797 self.assertEqual(t.tm_mon, 1)
2798 self.assertEqual(t.tm_mday, 1)
2799 self.assertEqual(t.tm_hour, 23)
2800 self.assertEqual(t.tm_min, 58)
2801 self.assertEqual(t.tm_sec, 37)
2802 self.assertEqual(t.tm_yday, 1)
2803 self.assertEqual(t.tm_isdst, 0)
2804
2805 def test_tzinfo_isoformat(self):
2806 zero = FixedOffset(0, "+00:00")
2807 plus = FixedOffset(220, "+03:40")
2808 minus = FixedOffset(-231, "-03:51")
2809 unknown = FixedOffset(None, "")
2810
2811 cls = self.theclass
2812 datestr = '0001-02-03'
2813 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002814 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002815 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2816 timestr = '04:05:59' + (us and '.987001' or '')
2817 ofsstr = ofs is not None and d.tzname() or ''
2818 tailstr = timestr + ofsstr
2819 iso = d.isoformat()
2820 self.assertEqual(iso, datestr + 'T' + tailstr)
2821 self.assertEqual(iso, d.isoformat('T'))
2822 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2823 self.assertEqual(str(d), datestr + ' ' + tailstr)
2824
Tim Peters12bf3392002-12-24 05:41:27 +00002825 def test_replace(self):
2826 cls = self.theclass
2827 z100 = FixedOffset(100, "+100")
2828 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2829 args = [1, 2, 3, 4, 5, 6, 7, z100]
2830 base = cls(*args)
2831 self.assertEqual(base, base.replace())
2832
2833 i = 0
2834 for name, newval in (("year", 2),
2835 ("month", 3),
2836 ("day", 4),
2837 ("hour", 5),
2838 ("minute", 6),
2839 ("second", 7),
2840 ("microsecond", 8),
2841 ("tzinfo", zm200)):
2842 newargs = args[:]
2843 newargs[i] = newval
2844 expected = cls(*newargs)
2845 got = base.replace(**{name: newval})
2846 self.assertEqual(expected, got)
2847 i += 1
2848
2849 # Ensure we can get rid of a tzinfo.
2850 self.assertEqual(base.tzname(), "+100")
2851 base2 = base.replace(tzinfo=None)
2852 self.failUnless(base2.tzinfo is None)
2853 self.failUnless(base2.tzname() is None)
2854
2855 # Ensure we can add one.
2856 base3 = base2.replace(tzinfo=z100)
2857 self.assertEqual(base, base3)
2858 self.failUnless(base.tzinfo is base3.tzinfo)
2859
2860 # Out of bounds.
2861 base = cls(2000, 2, 29)
2862 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002863
Tim Peters80475bb2002-12-25 07:40:55 +00002864 def test_more_astimezone(self):
2865 # The inherited test_astimezone covered some trivial and error cases.
2866 fnone = FixedOffset(None, "None")
2867 f44m = FixedOffset(44, "44")
2868 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2869
Tim Peters10cadce2003-01-23 19:58:02 +00002870 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002871 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002872 # Replacing with degenerate tzinfo raises an exception.
2873 self.assertRaises(ValueError, dt.astimezone, fnone)
2874 # Ditto with None tz.
2875 self.assertRaises(TypeError, dt.astimezone, None)
2876 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002877 x = dt.astimezone(dt.tzinfo)
2878 self.failUnless(x.tzinfo is f44m)
2879 self.assertEqual(x.date(), dt.date())
2880 self.assertEqual(x.time(), dt.time())
2881
2882 # Replacing with different tzinfo does adjust.
2883 got = dt.astimezone(fm5h)
2884 self.failUnless(got.tzinfo is fm5h)
2885 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2886 expected = dt - dt.utcoffset() # in effect, convert to UTC
2887 expected += fm5h.utcoffset(dt) # and from there to local time
2888 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2889 self.assertEqual(got.date(), expected.date())
2890 self.assertEqual(got.time(), expected.time())
2891 self.assertEqual(got.timetz(), expected.timetz())
2892 self.failUnless(got.tzinfo is expected.tzinfo)
2893 self.assertEqual(got, expected)
2894
Tim Peters4c0db782002-12-26 05:01:19 +00002895 def test_aware_subtract(self):
2896 cls = self.theclass
2897
Tim Peters60c76e42002-12-27 00:41:11 +00002898 # Ensure that utcoffset() is ignored when the operands have the
2899 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002900 class OperandDependentOffset(tzinfo):
2901 def utcoffset(self, t):
2902 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002903 # d0 and d1 equal after adjustment
2904 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002905 else:
Tim Peters397301e2003-01-02 21:28:08 +00002906 # d2 off in the weeds
2907 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002908
2909 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2910 d0 = base.replace(minute=3)
2911 d1 = base.replace(minute=9)
2912 d2 = base.replace(minute=11)
2913 for x in d0, d1, d2:
2914 for y in d0, d1, d2:
2915 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002916 expected = timedelta(minutes=x.minute - y.minute)
2917 self.assertEqual(got, expected)
2918
2919 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2920 # ignored.
2921 base = cls(8, 9, 10, 11, 12, 13, 14)
2922 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2923 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2924 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2925 for x in d0, d1, d2:
2926 for y in d0, d1, d2:
2927 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002928 if (x is d0 or x is d1) and (y is d0 or y is d1):
2929 expected = timedelta(0)
2930 elif x is y is d2:
2931 expected = timedelta(0)
2932 elif x is d2:
2933 expected = timedelta(minutes=(11-59)-0)
2934 else:
2935 assert y is d2
2936 expected = timedelta(minutes=0-(11-59))
2937 self.assertEqual(got, expected)
2938
Tim Peters60c76e42002-12-27 00:41:11 +00002939 def test_mixed_compare(self):
2940 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002941 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002942 self.assertEqual(t1, t2)
2943 t2 = t2.replace(tzinfo=None)
2944 self.assertEqual(t1, t2)
2945 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2946 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002947 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2948 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002949
Tim Peters0bf60bd2003-01-08 20:40:01 +00002950 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002951 class Varies(tzinfo):
2952 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002953 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002954 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002955 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002956 return self.offset
2957
2958 v = Varies()
2959 t1 = t2.replace(tzinfo=v)
2960 t2 = t2.replace(tzinfo=v)
2961 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2962 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2963 self.assertEqual(t1, t2)
2964
2965 # But if they're not identical, it isn't ignored.
2966 t2 = t2.replace(tzinfo=Varies())
2967 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002968
Tim Petersa98924a2003-05-17 05:55:19 +00002969 def test_subclass_datetimetz(self):
2970
2971 class C(self.theclass):
2972 theAnswer = 42
2973
2974 def __new__(cls, *args, **kws):
2975 temp = kws.copy()
2976 extra = temp.pop('extra')
2977 result = self.theclass.__new__(cls, *args, **temp)
2978 result.extra = extra
2979 return result
2980
2981 def newmeth(self, start):
2982 return start + self.hour + self.year
2983
2984 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2985
2986 dt1 = self.theclass(*args)
2987 dt2 = C(*args, **{'extra': 7})
2988
2989 self.assertEqual(dt2.__class__, C)
2990 self.assertEqual(dt2.theAnswer, 42)
2991 self.assertEqual(dt2.extra, 7)
2992 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2993 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2994
Tim Peters621818b2002-12-29 23:44:49 +00002995# Pain to set up DST-aware tzinfo classes.
2996
2997def first_sunday_on_or_after(dt):
2998 days_to_go = 6 - dt.weekday()
2999 if days_to_go:
3000 dt += timedelta(days_to_go)
3001 return dt
3002
3003ZERO = timedelta(0)
3004HOUR = timedelta(hours=1)
3005DAY = timedelta(days=1)
3006# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3007DSTSTART = datetime(1, 4, 1, 2)
3008# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003009# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3010# being standard time on that day, there is no spelling in local time of
3011# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3012DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003013
3014class USTimeZone(tzinfo):
3015
3016 def __init__(self, hours, reprname, stdname, dstname):
3017 self.stdoffset = timedelta(hours=hours)
3018 self.reprname = reprname
3019 self.stdname = stdname
3020 self.dstname = dstname
3021
3022 def __repr__(self):
3023 return self.reprname
3024
3025 def tzname(self, dt):
3026 if self.dst(dt):
3027 return self.dstname
3028 else:
3029 return self.stdname
3030
3031 def utcoffset(self, dt):
3032 return self.stdoffset + self.dst(dt)
3033
3034 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003035 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003036 # An exception instead may be sensible here, in one or more of
3037 # the cases.
3038 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003039 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003040
3041 # Find first Sunday in April.
3042 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3043 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3044
3045 # Find last Sunday in October.
3046 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3047 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3048
Tim Peters621818b2002-12-29 23:44:49 +00003049 # Can't compare naive to aware objects, so strip the timezone from
3050 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003051 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003052 return HOUR
3053 else:
3054 return ZERO
3055
Tim Peters521fc152002-12-31 17:36:56 +00003056Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3057Central = USTimeZone(-6, "Central", "CST", "CDT")
3058Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3059Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003060utc_real = FixedOffset(0, "UTC", 0)
3061# For better test coverage, we want another flavor of UTC that's west of
3062# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003063utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003064
3065class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003066 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003067 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003068 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003069
Tim Peters0bf60bd2003-01-08 20:40:01 +00003070 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003071
Tim Peters521fc152002-12-31 17:36:56 +00003072 # Check a time that's inside DST.
3073 def checkinside(self, dt, tz, utc, dston, dstoff):
3074 self.assertEqual(dt.dst(), HOUR)
3075
3076 # Conversion to our own timezone is always an identity.
3077 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003078
3079 asutc = dt.astimezone(utc)
3080 there_and_back = asutc.astimezone(tz)
3081
3082 # Conversion to UTC and back isn't always an identity here,
3083 # because there are redundant spellings (in local time) of
3084 # UTC time when DST begins: the clock jumps from 1:59:59
3085 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3086 # make sense then. The classes above treat 2:MM:SS as
3087 # daylight time then (it's "after 2am"), really an alias
3088 # for 1:MM:SS standard time. The latter form is what
3089 # conversion back from UTC produces.
3090 if dt.date() == dston.date() and dt.hour == 2:
3091 # We're in the redundant hour, and coming back from
3092 # UTC gives the 1:MM:SS standard-time spelling.
3093 self.assertEqual(there_and_back + HOUR, dt)
3094 # Although during was considered to be in daylight
3095 # time, there_and_back is not.
3096 self.assertEqual(there_and_back.dst(), ZERO)
3097 # They're the same times in UTC.
3098 self.assertEqual(there_and_back.astimezone(utc),
3099 dt.astimezone(utc))
3100 else:
3101 # We're not in the redundant hour.
3102 self.assertEqual(dt, there_and_back)
3103
Tim Peters327098a2003-01-20 22:54:38 +00003104 # Because we have a redundant spelling when DST begins, there is
3105 # (unforunately) an hour when DST ends that can't be spelled at all in
3106 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3107 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3108 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3109 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3110 # expressed in local time. Nevertheless, we want conversion back
3111 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003112 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003113 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003114 if dt.date() == dstoff.date() and dt.hour == 0:
3115 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003116 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003117 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3118 nexthour_utc += HOUR
3119 nexthour_tz = nexthour_utc.astimezone(tz)
3120 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003121 else:
Tim Peters327098a2003-01-20 22:54:38 +00003122 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003123
3124 # Check a time that's outside DST.
3125 def checkoutside(self, dt, tz, utc):
3126 self.assertEqual(dt.dst(), ZERO)
3127
3128 # Conversion to our own timezone is always an identity.
3129 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003130
3131 # Converting to UTC and back is an identity too.
3132 asutc = dt.astimezone(utc)
3133 there_and_back = asutc.astimezone(tz)
3134 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003135
Tim Peters1024bf82002-12-30 17:09:40 +00003136 def convert_between_tz_and_utc(self, tz, utc):
3137 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003138 # Because 1:MM on the day DST ends is taken as being standard time,
3139 # there is no spelling in tz for the last hour of daylight time.
3140 # For purposes of the test, the last hour of DST is 0:MM, which is
3141 # taken as being daylight time (and 1:MM is taken as being standard
3142 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003143 dstoff = self.dstoff.replace(tzinfo=tz)
3144 for delta in (timedelta(weeks=13),
3145 DAY,
3146 HOUR,
3147 timedelta(minutes=1),
3148 timedelta(microseconds=1)):
3149
Tim Peters521fc152002-12-31 17:36:56 +00003150 self.checkinside(dston, tz, utc, dston, dstoff)
3151 for during in dston + delta, dstoff - delta:
3152 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003153
Tim Peters521fc152002-12-31 17:36:56 +00003154 self.checkoutside(dstoff, tz, utc)
3155 for outside in dston - delta, dstoff + delta:
3156 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003157
Tim Peters621818b2002-12-29 23:44:49 +00003158 def test_easy(self):
3159 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003160 self.convert_between_tz_and_utc(Eastern, utc_real)
3161 self.convert_between_tz_and_utc(Pacific, utc_real)
3162 self.convert_between_tz_and_utc(Eastern, utc_fake)
3163 self.convert_between_tz_and_utc(Pacific, utc_fake)
3164 # The next is really dancing near the edge. It works because
3165 # Pacific and Eastern are far enough apart that their "problem
3166 # hours" don't overlap.
3167 self.convert_between_tz_and_utc(Eastern, Pacific)
3168 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003169 # OTOH, these fail! Don't enable them. The difficulty is that
3170 # the edge case tests assume that every hour is representable in
3171 # the "utc" class. This is always true for a fixed-offset tzinfo
3172 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3173 # For these adjacent DST-aware time zones, the range of time offsets
3174 # tested ends up creating hours in the one that aren't representable
3175 # in the other. For the same reason, we would see failures in the
3176 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3177 # offset deltas in convert_between_tz_and_utc().
3178 #
3179 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3180 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003181
Tim Petersf3615152003-01-01 21:51:37 +00003182 def test_tricky(self):
3183 # 22:00 on day before daylight starts.
3184 fourback = self.dston - timedelta(hours=4)
3185 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003186 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003187 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3188 # 2", we should get the 3 spelling.
3189 # If we plug 22:00 the day before into Eastern, it "looks like std
3190 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3191 # to 22:00 lands on 2:00, which makes no sense in local time (the
3192 # local clock jumps from 1 to 3). The point here is to make sure we
3193 # get the 3 spelling.
3194 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003195 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003196 self.assertEqual(expected, got)
3197
3198 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3199 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003200 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003201 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3202 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3203 # spelling.
3204 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003205 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003206 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003207
Tim Petersadf64202003-01-04 06:03:15 +00003208 # Now on the day DST ends, we want "repeat an hour" behavior.
3209 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3210 # EST 23:MM 0:MM 1:MM 2:MM
3211 # EDT 0:MM 1:MM 2:MM 3:MM
3212 # wall 0:MM 1:MM 1:MM 2:MM against these
3213 for utc in utc_real, utc_fake:
3214 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003215 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003216 # Convert that to UTC.
3217 first_std_hour -= tz.utcoffset(None)
3218 # Adjust for possibly fake UTC.
3219 asutc = first_std_hour + utc.utcoffset(None)
3220 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3221 # tz=Eastern.
3222 asutcbase = asutc.replace(tzinfo=utc)
3223 for tzhour in (0, 1, 1, 2):
3224 expectedbase = self.dstoff.replace(hour=tzhour)
3225 for minute in 0, 30, 59:
3226 expected = expectedbase.replace(minute=minute)
3227 asutc = asutcbase.replace(minute=minute)
3228 astz = asutc.astimezone(tz)
3229 self.assertEqual(astz.replace(tzinfo=None), expected)
3230 asutcbase += HOUR
3231
3232
Tim Peters710fb152003-01-02 19:35:54 +00003233 def test_bogus_dst(self):
3234 class ok(tzinfo):
3235 def utcoffset(self, dt): return HOUR
3236 def dst(self, dt): return HOUR
3237
3238 now = self.theclass.now().replace(tzinfo=utc_real)
3239 # Doesn't blow up.
3240 now.astimezone(ok())
3241
3242 # Does blow up.
3243 class notok(ok):
3244 def dst(self, dt): return None
3245 self.assertRaises(ValueError, now.astimezone, notok())
3246
Tim Peters52dcce22003-01-23 16:36:11 +00003247 def test_fromutc(self):
3248 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3249 now = datetime.utcnow().replace(tzinfo=utc_real)
3250 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3251 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3252 enow = Eastern.fromutc(now) # doesn't blow up
3253 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3254 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3255 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3256
3257 # Always converts UTC to standard time.
3258 class FauxUSTimeZone(USTimeZone):
3259 def fromutc(self, dt):
3260 return dt + self.stdoffset
3261 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3262
3263 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3264 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3265 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3266
3267 # Check around DST start.
3268 start = self.dston.replace(hour=4, tzinfo=Eastern)
3269 fstart = start.replace(tzinfo=FEastern)
3270 for wall in 23, 0, 1, 3, 4, 5:
3271 expected = start.replace(hour=wall)
3272 if wall == 23:
3273 expected -= timedelta(days=1)
3274 got = Eastern.fromutc(start)
3275 self.assertEqual(expected, got)
3276
3277 expected = fstart + FEastern.stdoffset
3278 got = FEastern.fromutc(fstart)
3279 self.assertEqual(expected, got)
3280
3281 # Ensure astimezone() calls fromutc() too.
3282 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3283 self.assertEqual(expected, got)
3284
3285 start += HOUR
3286 fstart += HOUR
3287
3288 # Check around DST end.
3289 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3290 fstart = start.replace(tzinfo=FEastern)
3291 for wall in 0, 1, 1, 2, 3, 4:
3292 expected = start.replace(hour=wall)
3293 got = Eastern.fromutc(start)
3294 self.assertEqual(expected, got)
3295
3296 expected = fstart + FEastern.stdoffset
3297 got = FEastern.fromutc(fstart)
3298 self.assertEqual(expected, got)
3299
3300 # Ensure astimezone() calls fromutc() too.
3301 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3302 self.assertEqual(expected, got)
3303
3304 start += HOUR
3305 fstart += HOUR
3306
Tim Peters710fb152003-01-02 19:35:54 +00003307
Tim Peters528ca532004-09-16 01:30:50 +00003308#############################################################################
3309# oddballs
3310
3311class Oddballs(unittest.TestCase):
3312
3313 def test_bug_1028306(self):
3314 # Trying to compare a date to a datetime should act like a mixed-
3315 # type comparison, despite that datetime is a subclass of date.
3316 as_date = date.today()
3317 as_datetime = datetime.combine(as_date, time())
3318 self.assert_(as_date != as_datetime)
3319 self.assert_(as_datetime != as_date)
3320 self.assert_(not as_date == as_datetime)
3321 self.assert_(not as_datetime == as_date)
3322 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3323 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3324 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3325 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3326 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3327 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3328 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3329 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3330
3331 # Neverthelss, comparison should work with the base-class (date)
3332 # projection if use of a date method is forced.
3333 self.assert_(as_date.__eq__(as_datetime))
3334 different_day = (as_date.day + 1) % 20 + 1
3335 self.assert_(not as_date.__eq__(as_datetime.replace(day=
3336 different_day)))
3337
3338 # And date should compare with other subclasses of date. If a
3339 # subclass wants to stop this, it's up to the subclass to do so.
3340 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3341 self.assertEqual(as_date, date_sc)
3342 self.assertEqual(date_sc, as_date)
3343
3344 # Ditto for datetimes.
3345 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3346 as_date.day, 0, 0, 0)
3347 self.assertEqual(as_datetime, datetime_sc)
3348 self.assertEqual(datetime_sc, as_datetime)
3349
Tim Peters2a799bf2002-12-16 20:18:38 +00003350def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003351 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003352
3353if __name__ == "__main__":
3354 test_main()