blob: cdc9eed6b9ce64b81f4cdcebad73ea97b1d7efeb [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
256 # Divison of int by timedelta doesn't make sense.
257 # Division by zero doesn't make sense.
258 for zero in 0, 0L:
259 self.assertRaises(TypeError, lambda: zero // a)
260 self.assertRaises(ZeroDivisionError, lambda: a // zero)
261
262 def test_basic_attributes(self):
263 days, seconds, us = 1, 7, 31
264 td = timedelta(days, seconds, us)
265 self.assertEqual(td.days, days)
266 self.assertEqual(td.seconds, seconds)
267 self.assertEqual(td.microseconds, us)
268
269 def test_carries(self):
270 t1 = timedelta(days=100,
271 weeks=-7,
272 hours=-24*(100-49),
273 minutes=-3,
274 seconds=12,
275 microseconds=(3*60 - 12) * 1e6 + 1)
276 t2 = timedelta(microseconds=1)
277 self.assertEqual(t1, t2)
278
279 def test_hash_equality(self):
280 t1 = timedelta(days=100,
281 weeks=-7,
282 hours=-24*(100-49),
283 minutes=-3,
284 seconds=12,
285 microseconds=(3*60 - 12) * 1000000)
286 t2 = timedelta()
287 self.assertEqual(hash(t1), hash(t2))
288
289 t1 += timedelta(weeks=7)
290 t2 += timedelta(days=7*7)
291 self.assertEqual(t1, t2)
292 self.assertEqual(hash(t1), hash(t2))
293
294 d = {t1: 1}
295 d[t2] = 2
296 self.assertEqual(len(d), 1)
297 self.assertEqual(d[t1], 2)
298
299 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000300 args = 12, 34, 56
301 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000302 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000303 green = pickler.dumps(orig, proto)
304 derived = unpickler.loads(green)
305 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000306
307 def test_compare(self):
308 t1 = timedelta(2, 3, 4)
309 t2 = timedelta(2, 3, 4)
310 self.failUnless(t1 == t2)
311 self.failUnless(t1 <= t2)
312 self.failUnless(t1 >= t2)
313 self.failUnless(not t1 != t2)
314 self.failUnless(not t1 < t2)
315 self.failUnless(not t1 > t2)
316 self.assertEqual(cmp(t1, t2), 0)
317 self.assertEqual(cmp(t2, t1), 0)
318
319 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
320 t2 = timedelta(*args) # this is larger than t1
321 self.failUnless(t1 < t2)
322 self.failUnless(t2 > t1)
323 self.failUnless(t1 <= t2)
324 self.failUnless(t2 >= t1)
325 self.failUnless(t1 != t2)
326 self.failUnless(t2 != t1)
327 self.failUnless(not t1 == t2)
328 self.failUnless(not t2 == t1)
329 self.failUnless(not t1 > t2)
330 self.failUnless(not t2 < t1)
331 self.failUnless(not t1 >= t2)
332 self.failUnless(not t2 <= t1)
333 self.assertEqual(cmp(t1, t2), -1)
334 self.assertEqual(cmp(t2, t1), 1)
335
Tim Peters68124bb2003-02-08 03:46:31 +0000336 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000337 self.assertEqual(t1 == badarg, False)
338 self.assertEqual(t1 != badarg, True)
339 self.assertEqual(badarg == t1, False)
340 self.assertEqual(badarg != t1, True)
341
Tim Peters2a799bf2002-12-16 20:18:38 +0000342 self.assertRaises(TypeError, lambda: t1 <= badarg)
343 self.assertRaises(TypeError, lambda: t1 < badarg)
344 self.assertRaises(TypeError, lambda: t1 > badarg)
345 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000346 self.assertRaises(TypeError, lambda: badarg <= t1)
347 self.assertRaises(TypeError, lambda: badarg < t1)
348 self.assertRaises(TypeError, lambda: badarg > t1)
349 self.assertRaises(TypeError, lambda: badarg >= t1)
350
351 def test_str(self):
352 td = timedelta
353 eq = self.assertEqual
354
355 eq(str(td(1)), "1 day, 0:00:00")
356 eq(str(td(-1)), "-1 day, 0:00:00")
357 eq(str(td(2)), "2 days, 0:00:00")
358 eq(str(td(-2)), "-2 days, 0:00:00")
359
360 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
361 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
362 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
363 "-210 days, 23:12:34")
364
365 eq(str(td(milliseconds=1)), "0:00:00.001000")
366 eq(str(td(microseconds=3)), "0:00:00.000003")
367
368 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
369 microseconds=999999)),
370 "999999999 days, 23:59:59.999999")
371
372 def test_roundtrip(self):
373 for td in (timedelta(days=999999999, hours=23, minutes=59,
374 seconds=59, microseconds=999999),
375 timedelta(days=-999999999),
376 timedelta(days=1, seconds=2, microseconds=3)):
377
378 # Verify td -> string -> td identity.
379 s = repr(td)
380 self.failUnless(s.startswith('datetime.'))
381 s = s[9:]
382 td2 = eval(s)
383 self.assertEqual(td, td2)
384
385 # Verify identity via reconstructing from pieces.
386 td2 = timedelta(td.days, td.seconds, td.microseconds)
387 self.assertEqual(td, td2)
388
389 def test_resolution_info(self):
390 self.assert_(isinstance(timedelta.min, timedelta))
391 self.assert_(isinstance(timedelta.max, timedelta))
392 self.assert_(isinstance(timedelta.resolution, timedelta))
393 self.assert_(timedelta.max > timedelta.min)
394 self.assertEqual(timedelta.min, timedelta(-999999999))
395 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
396 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
397
398 def test_overflow(self):
399 tiny = timedelta.resolution
400
401 td = timedelta.min + tiny
402 td -= tiny # no problem
403 self.assertRaises(OverflowError, td.__sub__, tiny)
404 self.assertRaises(OverflowError, td.__add__, -tiny)
405
406 td = timedelta.max - tiny
407 td += tiny # no problem
408 self.assertRaises(OverflowError, td.__add__, tiny)
409 self.assertRaises(OverflowError, td.__sub__, -tiny)
410
411 self.assertRaises(OverflowError, lambda: -timedelta.max)
412
413 def test_microsecond_rounding(self):
414 td = timedelta
415 eq = self.assertEqual
416
417 # Single-field rounding.
418 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
419 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
420 eq(td(milliseconds=0.6/1000), td(microseconds=1))
421 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
422
423 # Rounding due to contributions from more than one field.
424 us_per_hour = 3600e6
425 us_per_day = us_per_hour * 24
426 eq(td(days=.4/us_per_day), td(0))
427 eq(td(hours=.2/us_per_hour), td(0))
428 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
429
430 eq(td(days=-.4/us_per_day), td(0))
431 eq(td(hours=-.2/us_per_hour), td(0))
432 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
433
434 def test_massive_normalization(self):
435 td = timedelta(microseconds=-1)
436 self.assertEqual((td.days, td.seconds, td.microseconds),
437 (-1, 24*3600-1, 999999))
438
439 def test_bool(self):
440 self.failUnless(timedelta(1))
441 self.failUnless(timedelta(0, 1))
442 self.failUnless(timedelta(0, 0, 1))
443 self.failUnless(timedelta(microseconds=1))
444 self.failUnless(not timedelta(0))
445
Tim Petersb0c854d2003-05-17 15:57:00 +0000446 def test_subclass_timedelta(self):
447
448 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000449 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000450 def from_td(td):
451 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000452
453 def as_hours(self):
454 sum = (self.days * 24 +
455 self.seconds / 3600.0 +
456 self.microseconds / 3600e6)
457 return round(sum)
458
459 t1 = T(days=1)
460 self.assert_(type(t1) is T)
461 self.assertEqual(t1.as_hours(), 24)
462
463 t2 = T(days=-1, seconds=-3600)
464 self.assert_(type(t2) is T)
465 self.assertEqual(t2.as_hours(), -25)
466
467 t3 = t1 + t2
468 self.assert_(type(t3) is timedelta)
469 t4 = T.from_td(t3)
470 self.assert_(type(t4) is T)
471 self.assertEqual(t3.days, t4.days)
472 self.assertEqual(t3.seconds, t4.seconds)
473 self.assertEqual(t3.microseconds, t4.microseconds)
474 self.assertEqual(str(t3), str(t4))
475 self.assertEqual(t4.as_hours(), -1)
476
Tim Peters2a799bf2002-12-16 20:18:38 +0000477#############################################################################
478# date tests
479
480class TestDateOnly(unittest.TestCase):
481 # Tests here won't pass if also run on datetime objects, so don't
482 # subclass this to test datetimes too.
483
484 def test_delta_non_days_ignored(self):
485 dt = date(2000, 1, 2)
486 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
487 microseconds=5)
488 days = timedelta(delta.days)
489 self.assertEqual(days, timedelta(1))
490
491 dt2 = dt + delta
492 self.assertEqual(dt2, dt + days)
493
494 dt2 = delta + dt
495 self.assertEqual(dt2, dt + days)
496
497 dt2 = dt - delta
498 self.assertEqual(dt2, dt - days)
499
500 delta = -delta
501 days = timedelta(delta.days)
502 self.assertEqual(days, timedelta(-2))
503
504 dt2 = dt + delta
505 self.assertEqual(dt2, dt + days)
506
507 dt2 = delta + dt
508 self.assertEqual(dt2, dt + days)
509
510 dt2 = dt - delta
511 self.assertEqual(dt2, dt - days)
512
Tim Peters604c0132004-06-07 23:04:33 +0000513class SubclassDate(date):
514 sub_var = 1
515
Collin Winterc2898c52007-04-25 17:29:52 +0000516class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000517 # Tests here should pass for both dates and datetimes, except for a
518 # few tests that TestDateTime overrides.
519
520 theclass = date
521
522 def test_basic_attributes(self):
523 dt = self.theclass(2002, 3, 1)
524 self.assertEqual(dt.year, 2002)
525 self.assertEqual(dt.month, 3)
526 self.assertEqual(dt.day, 1)
527
528 def test_roundtrip(self):
529 for dt in (self.theclass(1, 2, 3),
530 self.theclass.today()):
531 # Verify dt -> string -> date identity.
532 s = repr(dt)
533 self.failUnless(s.startswith('datetime.'))
534 s = s[9:]
535 dt2 = eval(s)
536 self.assertEqual(dt, dt2)
537
538 # Verify identity via reconstructing from pieces.
539 dt2 = self.theclass(dt.year, dt.month, dt.day)
540 self.assertEqual(dt, dt2)
541
542 def test_ordinal_conversions(self):
543 # Check some fixed values.
544 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
545 (1, 12, 31, 365),
546 (2, 1, 1, 366),
547 # first example from "Calendrical Calculations"
548 (1945, 11, 12, 710347)]:
549 d = self.theclass(y, m, d)
550 self.assertEqual(n, d.toordinal())
551 fromord = self.theclass.fromordinal(n)
552 self.assertEqual(d, fromord)
553 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000554 # if we're checking something fancier than a date, verify
555 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000556 self.assertEqual(fromord.hour, 0)
557 self.assertEqual(fromord.minute, 0)
558 self.assertEqual(fromord.second, 0)
559 self.assertEqual(fromord.microsecond, 0)
560
Tim Peters0bf60bd2003-01-08 20:40:01 +0000561 # Check first and last days of year spottily across the whole
562 # range of years supported.
563 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000564 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
565 d = self.theclass(year, 1, 1)
566 n = d.toordinal()
567 d2 = self.theclass.fromordinal(n)
568 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000569 # Verify that moving back a day gets to the end of year-1.
570 if year > 1:
571 d = self.theclass.fromordinal(n-1)
572 d2 = self.theclass(year-1, 12, 31)
573 self.assertEqual(d, d2)
574 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000575
576 # Test every day in a leap-year and a non-leap year.
577 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
578 for year, isleap in (2000, True), (2002, False):
579 n = self.theclass(year, 1, 1).toordinal()
580 for month, maxday in zip(range(1, 13), dim):
581 if month == 2 and isleap:
582 maxday += 1
583 for day in range(1, maxday+1):
584 d = self.theclass(year, month, day)
585 self.assertEqual(d.toordinal(), n)
586 self.assertEqual(d, self.theclass.fromordinal(n))
587 n += 1
588
589 def test_extreme_ordinals(self):
590 a = self.theclass.min
591 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
592 aord = a.toordinal()
593 b = a.fromordinal(aord)
594 self.assertEqual(a, b)
595
596 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
597
598 b = a + timedelta(days=1)
599 self.assertEqual(b.toordinal(), aord + 1)
600 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
601
602 a = self.theclass.max
603 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
604 aord = a.toordinal()
605 b = a.fromordinal(aord)
606 self.assertEqual(a, b)
607
608 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
609
610 b = a - timedelta(days=1)
611 self.assertEqual(b.toordinal(), aord - 1)
612 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
613
614 def test_bad_constructor_arguments(self):
615 # bad years
616 self.theclass(MINYEAR, 1, 1) # no exception
617 self.theclass(MAXYEAR, 1, 1) # no exception
618 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
619 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
620 # bad months
621 self.theclass(2000, 1, 1) # no exception
622 self.theclass(2000, 12, 1) # no exception
623 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
624 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
625 # bad days
626 self.theclass(2000, 2, 29) # no exception
627 self.theclass(2004, 2, 29) # no exception
628 self.theclass(2400, 2, 29) # no exception
629 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
630 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
631 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
632 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
633 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
634 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
635
636 def test_hash_equality(self):
637 d = self.theclass(2000, 12, 31)
638 # same thing
639 e = self.theclass(2000, 12, 31)
640 self.assertEqual(d, e)
641 self.assertEqual(hash(d), hash(e))
642
643 dic = {d: 1}
644 dic[e] = 2
645 self.assertEqual(len(dic), 1)
646 self.assertEqual(dic[d], 2)
647 self.assertEqual(dic[e], 2)
648
649 d = self.theclass(2001, 1, 1)
650 # same thing
651 e = self.theclass(2001, 1, 1)
652 self.assertEqual(d, e)
653 self.assertEqual(hash(d), hash(e))
654
655 dic = {d: 1}
656 dic[e] = 2
657 self.assertEqual(len(dic), 1)
658 self.assertEqual(dic[d], 2)
659 self.assertEqual(dic[e], 2)
660
661 def test_computations(self):
662 a = self.theclass(2002, 1, 31)
663 b = self.theclass(1956, 1, 31)
664
665 diff = a-b
666 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
667 self.assertEqual(diff.seconds, 0)
668 self.assertEqual(diff.microseconds, 0)
669
670 day = timedelta(1)
671 week = timedelta(7)
672 a = self.theclass(2002, 3, 2)
673 self.assertEqual(a + day, self.theclass(2002, 3, 3))
674 self.assertEqual(day + a, self.theclass(2002, 3, 3))
675 self.assertEqual(a - day, self.theclass(2002, 3, 1))
676 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
677 self.assertEqual(a + week, self.theclass(2002, 3, 9))
678 self.assertEqual(a - week, self.theclass(2002, 2, 23))
679 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
680 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
681 self.assertEqual((a + week) - a, week)
682 self.assertEqual((a + day) - a, day)
683 self.assertEqual((a - week) - a, -week)
684 self.assertEqual((a - day) - a, -day)
685 self.assertEqual(a - (a + week), -week)
686 self.assertEqual(a - (a + day), -day)
687 self.assertEqual(a - (a - week), week)
688 self.assertEqual(a - (a - day), day)
689
690 # Add/sub ints, longs, floats should be illegal
691 for i in 1, 1L, 1.0:
692 self.assertRaises(TypeError, lambda: a+i)
693 self.assertRaises(TypeError, lambda: a-i)
694 self.assertRaises(TypeError, lambda: i+a)
695 self.assertRaises(TypeError, lambda: i-a)
696
697 # delta - date is senseless.
698 self.assertRaises(TypeError, lambda: day - a)
699 # mixing date and (delta or date) via * or // is senseless
700 self.assertRaises(TypeError, lambda: day * a)
701 self.assertRaises(TypeError, lambda: a * day)
702 self.assertRaises(TypeError, lambda: day // a)
703 self.assertRaises(TypeError, lambda: a // day)
704 self.assertRaises(TypeError, lambda: a * a)
705 self.assertRaises(TypeError, lambda: a // a)
706 # date + date is senseless
707 self.assertRaises(TypeError, lambda: a + a)
708
709 def test_overflow(self):
710 tiny = self.theclass.resolution
711
712 dt = self.theclass.min + tiny
713 dt -= tiny # no problem
714 self.assertRaises(OverflowError, dt.__sub__, tiny)
715 self.assertRaises(OverflowError, dt.__add__, -tiny)
716
717 dt = self.theclass.max - tiny
718 dt += tiny # no problem
719 self.assertRaises(OverflowError, dt.__add__, tiny)
720 self.assertRaises(OverflowError, dt.__sub__, -tiny)
721
722 def test_fromtimestamp(self):
723 import time
724
725 # Try an arbitrary fixed value.
726 year, month, day = 1999, 9, 19
727 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
728 d = self.theclass.fromtimestamp(ts)
729 self.assertEqual(d.year, year)
730 self.assertEqual(d.month, month)
731 self.assertEqual(d.day, day)
732
Tim Peters1b6f7a92004-06-20 02:50:16 +0000733 def test_insane_fromtimestamp(self):
734 # It's possible that some platform maps time_t to double,
735 # and that this test will fail there. This test should
736 # exempt such platforms (provided they return reasonable
737 # results!).
738 for insane in -1e200, 1e200:
739 self.assertRaises(ValueError, self.theclass.fromtimestamp,
740 insane)
741
Tim Peters2a799bf2002-12-16 20:18:38 +0000742 def test_today(self):
743 import time
744
745 # We claim that today() is like fromtimestamp(time.time()), so
746 # prove it.
747 for dummy in range(3):
748 today = self.theclass.today()
749 ts = time.time()
750 todayagain = self.theclass.fromtimestamp(ts)
751 if today == todayagain:
752 break
753 # There are several legit reasons that could fail:
754 # 1. It recently became midnight, between the today() and the
755 # time() calls.
756 # 2. The platform time() has such fine resolution that we'll
757 # never get the same value twice.
758 # 3. The platform time() has poor resolution, and we just
759 # happened to call today() right before a resolution quantum
760 # boundary.
761 # 4. The system clock got fiddled between calls.
762 # In any case, wait a little while and try again.
763 time.sleep(0.1)
764
765 # It worked or it didn't. If it didn't, assume it's reason #2, and
766 # let the test pass if they're within half a second of each other.
767 self.failUnless(today == todayagain or
768 abs(todayagain - today) < timedelta(seconds=0.5))
769
770 def test_weekday(self):
771 for i in range(7):
772 # March 4, 2002 is a Monday
773 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
774 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
775 # January 2, 1956 is a Monday
776 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
777 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
778
779 def test_isocalendar(self):
780 # Check examples from
781 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
782 for i in range(7):
783 d = self.theclass(2003, 12, 22+i)
784 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
785 d = self.theclass(2003, 12, 29) + timedelta(i)
786 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
787 d = self.theclass(2004, 1, 5+i)
788 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
789 d = self.theclass(2009, 12, 21+i)
790 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
791 d = self.theclass(2009, 12, 28) + timedelta(i)
792 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
793 d = self.theclass(2010, 1, 4+i)
794 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
795
796 def test_iso_long_years(self):
797 # Calculate long ISO years and compare to table from
798 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
799 ISO_LONG_YEARS_TABLE = """
800 4 32 60 88
801 9 37 65 93
802 15 43 71 99
803 20 48 76
804 26 54 82
805
806 105 133 161 189
807 111 139 167 195
808 116 144 172
809 122 150 178
810 128 156 184
811
812 201 229 257 285
813 207 235 263 291
814 212 240 268 296
815 218 246 274
816 224 252 280
817
818 303 331 359 387
819 308 336 364 392
820 314 342 370 398
821 320 348 376
822 325 353 381
823 """
824 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
825 iso_long_years.sort()
826 L = []
827 for i in range(400):
828 d = self.theclass(2000+i, 12, 31)
829 d1 = self.theclass(1600+i, 12, 31)
830 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
831 if d.isocalendar()[1] == 53:
832 L.append(i)
833 self.assertEqual(L, iso_long_years)
834
835 def test_isoformat(self):
836 t = self.theclass(2, 3, 2)
837 self.assertEqual(t.isoformat(), "0002-03-02")
838
839 def test_ctime(self):
840 t = self.theclass(2002, 3, 2)
841 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
842
843 def test_strftime(self):
844 t = self.theclass(2005, 3, 2)
845 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000846 self.assertEqual(t.strftime(""), "") # SF bug #761337
Georg Brandl4ddfcd32006-09-30 11:17:34 +0000847 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000848
849 self.assertRaises(TypeError, t.strftime) # needs an arg
850 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
851 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
852
Gregory P. Smith137d8242008-06-02 04:05:52 +0000853 # test that unicode input is allowed (issue 2782)
854 self.assertEqual(t.strftime(u"%m"), "03")
855
Tim Peters2a799bf2002-12-16 20:18:38 +0000856 # A naive object replaces %z and %Z w/ empty strings.
857 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
858
Gregory P. Smith137d8242008-06-02 04:05:52 +0000859
Eric Smitha9f7d622008-02-17 19:46:49 +0000860 def test_format(self):
861 dt = self.theclass(2007, 9, 10)
862 self.assertEqual(dt.__format__(''), str(dt))
863
864 # check that a derived class's __str__() gets called
865 class A(self.theclass):
866 def __str__(self):
867 return 'A'
868 a = A(2007, 9, 10)
869 self.assertEqual(a.__format__(''), 'A')
870
871 # check that a derived class's strftime gets called
872 class B(self.theclass):
873 def strftime(self, format_spec):
874 return 'B'
875 b = B(2007, 9, 10)
876 self.assertEqual(b.__format__(''), str(dt))
877
878 for fmt in ["m:%m d:%d y:%y",
879 "m:%m d:%d y:%y H:%H M:%M S:%S",
880 "%z %Z",
881 ]:
882 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
883 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
884 self.assertEqual(b.__format__(fmt), 'B')
885
Tim Peters2a799bf2002-12-16 20:18:38 +0000886 def test_resolution_info(self):
887 self.assert_(isinstance(self.theclass.min, self.theclass))
888 self.assert_(isinstance(self.theclass.max, self.theclass))
889 self.assert_(isinstance(self.theclass.resolution, timedelta))
890 self.assert_(self.theclass.max > self.theclass.min)
891
892 def test_extreme_timedelta(self):
893 big = self.theclass.max - self.theclass.min
894 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
895 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
896 # n == 315537897599999999 ~= 2**58.13
897 justasbig = timedelta(0, 0, n)
898 self.assertEqual(big, justasbig)
899 self.assertEqual(self.theclass.min + big, self.theclass.max)
900 self.assertEqual(self.theclass.max - big, self.theclass.min)
901
902 def test_timetuple(self):
903 for i in range(7):
904 # January 2, 1956 is a Monday (0)
905 d = self.theclass(1956, 1, 2+i)
906 t = d.timetuple()
907 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
908 # February 1, 1956 is a Wednesday (2)
909 d = self.theclass(1956, 2, 1+i)
910 t = d.timetuple()
911 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
912 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
913 # of the year.
914 d = self.theclass(1956, 3, 1+i)
915 t = d.timetuple()
916 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
917 self.assertEqual(t.tm_year, 1956)
918 self.assertEqual(t.tm_mon, 3)
919 self.assertEqual(t.tm_mday, 1+i)
920 self.assertEqual(t.tm_hour, 0)
921 self.assertEqual(t.tm_min, 0)
922 self.assertEqual(t.tm_sec, 0)
923 self.assertEqual(t.tm_wday, (3+i)%7)
924 self.assertEqual(t.tm_yday, 61+i)
925 self.assertEqual(t.tm_isdst, -1)
926
927 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000928 args = 6, 7, 23
929 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000930 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000931 green = pickler.dumps(orig, proto)
932 derived = unpickler.loads(green)
933 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000934
935 def test_compare(self):
936 t1 = self.theclass(2, 3, 4)
937 t2 = self.theclass(2, 3, 4)
938 self.failUnless(t1 == t2)
939 self.failUnless(t1 <= t2)
940 self.failUnless(t1 >= t2)
941 self.failUnless(not t1 != t2)
942 self.failUnless(not t1 < t2)
943 self.failUnless(not t1 > t2)
944 self.assertEqual(cmp(t1, t2), 0)
945 self.assertEqual(cmp(t2, t1), 0)
946
947 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
948 t2 = self.theclass(*args) # this is larger than t1
949 self.failUnless(t1 < t2)
950 self.failUnless(t2 > t1)
951 self.failUnless(t1 <= t2)
952 self.failUnless(t2 >= t1)
953 self.failUnless(t1 != t2)
954 self.failUnless(t2 != t1)
955 self.failUnless(not t1 == t2)
956 self.failUnless(not t2 == t1)
957 self.failUnless(not t1 > t2)
958 self.failUnless(not t2 < t1)
959 self.failUnless(not t1 >= t2)
960 self.failUnless(not t2 <= t1)
961 self.assertEqual(cmp(t1, t2), -1)
962 self.assertEqual(cmp(t2, t1), 1)
963
Tim Peters68124bb2003-02-08 03:46:31 +0000964 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000965 self.assertEqual(t1 == badarg, False)
966 self.assertEqual(t1 != badarg, True)
967 self.assertEqual(badarg == t1, False)
968 self.assertEqual(badarg != t1, True)
969
Tim Peters2a799bf2002-12-16 20:18:38 +0000970 self.assertRaises(TypeError, lambda: t1 < badarg)
971 self.assertRaises(TypeError, lambda: t1 > badarg)
972 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000973 self.assertRaises(TypeError, lambda: badarg <= t1)
974 self.assertRaises(TypeError, lambda: badarg < t1)
975 self.assertRaises(TypeError, lambda: badarg > t1)
976 self.assertRaises(TypeError, lambda: badarg >= t1)
977
Tim Peters8d81a012003-01-24 22:36:34 +0000978 def test_mixed_compare(self):
979 our = self.theclass(2000, 4, 5)
980 self.assertRaises(TypeError, cmp, our, 1)
981 self.assertRaises(TypeError, cmp, 1, our)
982
983 class AnotherDateTimeClass(object):
984 def __cmp__(self, other):
985 # Return "equal" so calling this can't be confused with
986 # compare-by-address (which never says "equal" for distinct
987 # objects).
988 return 0
989
990 # This still errors, because date and datetime comparison raise
991 # TypeError instead of NotImplemented when they don't know what to
992 # do, in order to stop comparison from falling back to the default
993 # compare-by-address.
994 their = AnotherDateTimeClass()
995 self.assertRaises(TypeError, cmp, our, their)
996 # Oops: The next stab raises TypeError in the C implementation,
997 # but not in the Python implementation of datetime. The difference
998 # is due to that the Python implementation defines __cmp__ but
999 # the C implementation defines tp_richcompare. This is more pain
1000 # to fix than it's worth, so commenting out the test.
1001 # self.assertEqual(cmp(their, our), 0)
1002
1003 # But date and datetime comparison return NotImplemented instead if the
1004 # other object has a timetuple attr. This gives the other object a
1005 # chance to do the comparison.
1006 class Comparable(AnotherDateTimeClass):
1007 def timetuple(self):
1008 return ()
1009
1010 their = Comparable()
1011 self.assertEqual(cmp(our, their), 0)
1012 self.assertEqual(cmp(their, our), 0)
1013 self.failUnless(our == their)
1014 self.failUnless(their == our)
1015
Tim Peters2a799bf2002-12-16 20:18:38 +00001016 def test_bool(self):
1017 # All dates are considered true.
1018 self.failUnless(self.theclass.min)
1019 self.failUnless(self.theclass.max)
1020
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001021 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001022 # For nasty technical reasons, we can't handle years before 1900.
1023 cls = self.theclass
1024 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1025 for y in 1, 49, 51, 99, 100, 1000, 1899:
1026 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001027
1028 def test_replace(self):
1029 cls = self.theclass
1030 args = [1, 2, 3]
1031 base = cls(*args)
1032 self.assertEqual(base, base.replace())
1033
1034 i = 0
1035 for name, newval in (("year", 2),
1036 ("month", 3),
1037 ("day", 4)):
1038 newargs = args[:]
1039 newargs[i] = newval
1040 expected = cls(*newargs)
1041 got = base.replace(**{name: newval})
1042 self.assertEqual(expected, got)
1043 i += 1
1044
1045 # Out of bounds.
1046 base = cls(2000, 2, 29)
1047 self.assertRaises(ValueError, base.replace, year=2001)
1048
Tim Petersa98924a2003-05-17 05:55:19 +00001049 def test_subclass_date(self):
1050
1051 class C(self.theclass):
1052 theAnswer = 42
1053
1054 def __new__(cls, *args, **kws):
1055 temp = kws.copy()
1056 extra = temp.pop('extra')
1057 result = self.theclass.__new__(cls, *args, **temp)
1058 result.extra = extra
1059 return result
1060
1061 def newmeth(self, start):
1062 return start + self.year + self.month
1063
1064 args = 2003, 4, 14
1065
1066 dt1 = self.theclass(*args)
1067 dt2 = C(*args, **{'extra': 7})
1068
1069 self.assertEqual(dt2.__class__, C)
1070 self.assertEqual(dt2.theAnswer, 42)
1071 self.assertEqual(dt2.extra, 7)
1072 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1073 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1074
Tim Peters604c0132004-06-07 23:04:33 +00001075 def test_pickling_subclass_date(self):
1076
1077 args = 6, 7, 23
1078 orig = SubclassDate(*args)
1079 for pickler, unpickler, proto in pickle_choices:
1080 green = pickler.dumps(orig, proto)
1081 derived = unpickler.loads(green)
1082 self.assertEqual(orig, derived)
1083
Tim Peters3f606292004-03-21 23:38:41 +00001084 def test_backdoor_resistance(self):
1085 # For fast unpickling, the constructor accepts a pickle string.
1086 # This is a low-overhead backdoor. A user can (by intent or
1087 # mistake) pass a string directly, which (if it's the right length)
1088 # will get treated like a pickle, and bypass the normal sanity
1089 # checks in the constructor. This can create insane objects.
1090 # The constructor doesn't want to burn the time to validate all
1091 # fields, but does check the month field. This stops, e.g.,
1092 # datetime.datetime('1995-03-25') from yielding an insane object.
1093 base = '1995-03-25'
1094 if not issubclass(self.theclass, datetime):
1095 base = base[:4]
1096 for month_byte in '9', chr(0), chr(13), '\xff':
1097 self.assertRaises(TypeError, self.theclass,
1098 base[:2] + month_byte + base[3:])
1099 for ord_byte in range(1, 13):
1100 # This shouldn't blow up because of the month byte alone. If
1101 # the implementation changes to do more-careful checking, it may
1102 # blow up because other fields are insane.
1103 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001104
Tim Peters2a799bf2002-12-16 20:18:38 +00001105#############################################################################
1106# datetime tests
1107
Tim Peters604c0132004-06-07 23:04:33 +00001108class SubclassDatetime(datetime):
1109 sub_var = 1
1110
Tim Peters2a799bf2002-12-16 20:18:38 +00001111class TestDateTime(TestDate):
1112
1113 theclass = datetime
1114
1115 def test_basic_attributes(self):
1116 dt = self.theclass(2002, 3, 1, 12, 0)
1117 self.assertEqual(dt.year, 2002)
1118 self.assertEqual(dt.month, 3)
1119 self.assertEqual(dt.day, 1)
1120 self.assertEqual(dt.hour, 12)
1121 self.assertEqual(dt.minute, 0)
1122 self.assertEqual(dt.second, 0)
1123 self.assertEqual(dt.microsecond, 0)
1124
1125 def test_basic_attributes_nonzero(self):
1126 # Make sure all attributes are non-zero so bugs in
1127 # bit-shifting access show up.
1128 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1129 self.assertEqual(dt.year, 2002)
1130 self.assertEqual(dt.month, 3)
1131 self.assertEqual(dt.day, 1)
1132 self.assertEqual(dt.hour, 12)
1133 self.assertEqual(dt.minute, 59)
1134 self.assertEqual(dt.second, 59)
1135 self.assertEqual(dt.microsecond, 8000)
1136
1137 def test_roundtrip(self):
1138 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1139 self.theclass.now()):
1140 # Verify dt -> string -> datetime identity.
1141 s = repr(dt)
1142 self.failUnless(s.startswith('datetime.'))
1143 s = s[9:]
1144 dt2 = eval(s)
1145 self.assertEqual(dt, dt2)
1146
1147 # Verify identity via reconstructing from pieces.
1148 dt2 = self.theclass(dt.year, dt.month, dt.day,
1149 dt.hour, dt.minute, dt.second,
1150 dt.microsecond)
1151 self.assertEqual(dt, dt2)
1152
1153 def test_isoformat(self):
1154 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1155 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1156 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1157 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1158 # str is ISO format with the separator forced to a blank.
1159 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1160
1161 t = self.theclass(2, 3, 2)
1162 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1163 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1164 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1165 # str is ISO format with the separator forced to a blank.
1166 self.assertEqual(str(t), "0002-03-02 00:00:00")
1167
Eric Smitha9f7d622008-02-17 19:46:49 +00001168 def test_format(self):
1169 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1170 self.assertEqual(dt.__format__(''), str(dt))
1171
1172 # check that a derived class's __str__() gets called
1173 class A(self.theclass):
1174 def __str__(self):
1175 return 'A'
1176 a = A(2007, 9, 10, 4, 5, 1, 123)
1177 self.assertEqual(a.__format__(''), 'A')
1178
1179 # check that a derived class's strftime gets called
1180 class B(self.theclass):
1181 def strftime(self, format_spec):
1182 return 'B'
1183 b = B(2007, 9, 10, 4, 5, 1, 123)
1184 self.assertEqual(b.__format__(''), str(dt))
1185
1186 for fmt in ["m:%m d:%d y:%y",
1187 "m:%m d:%d y:%y H:%H M:%M S:%S",
1188 "%z %Z",
1189 ]:
1190 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1191 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1192 self.assertEqual(b.__format__(fmt), 'B')
1193
Tim Peters2a799bf2002-12-16 20:18:38 +00001194 def test_more_ctime(self):
1195 # Test fields that TestDate doesn't touch.
1196 import time
1197
1198 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1199 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1200 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1201 # out. The difference is that t.ctime() produces " 2" for the day,
1202 # but platform ctime() produces "02" for the day. According to
1203 # C99, t.ctime() is correct here.
1204 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1205
1206 # So test a case where that difference doesn't matter.
1207 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1208 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1209
1210 def test_tz_independent_comparing(self):
1211 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1212 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1213 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1214 self.assertEqual(dt1, dt3)
1215 self.assert_(dt2 > dt3)
1216
1217 # Make sure comparison doesn't forget microseconds, and isn't done
1218 # via comparing a float timestamp (an IEEE double doesn't have enough
1219 # precision to span microsecond resolution across years 1 thru 9999,
1220 # so comparing via timestamp necessarily calls some distinct values
1221 # equal).
1222 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1223 us = timedelta(microseconds=1)
1224 dt2 = dt1 + us
1225 self.assertEqual(dt2 - dt1, us)
1226 self.assert_(dt1 < dt2)
1227
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001228 def test_strftime_with_bad_tzname_replace(self):
1229 # verify ok if tzinfo.tzname().replace() returns a non-string
1230 class MyTzInfo(FixedOffset):
1231 def tzname(self, dt):
1232 class MyStr(str):
1233 def replace(self, *args):
1234 return None
1235 return MyStr('name')
1236 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1237 self.assertRaises(TypeError, t.strftime, '%Z')
1238
Tim Peters2a799bf2002-12-16 20:18:38 +00001239 def test_bad_constructor_arguments(self):
1240 # bad years
1241 self.theclass(MINYEAR, 1, 1) # no exception
1242 self.theclass(MAXYEAR, 1, 1) # no exception
1243 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1244 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1245 # bad months
1246 self.theclass(2000, 1, 1) # no exception
1247 self.theclass(2000, 12, 1) # no exception
1248 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1249 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1250 # bad days
1251 self.theclass(2000, 2, 29) # no exception
1252 self.theclass(2004, 2, 29) # no exception
1253 self.theclass(2400, 2, 29) # no exception
1254 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1255 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1256 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1257 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1258 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1259 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1260 # bad hours
1261 self.theclass(2000, 1, 31, 0) # no exception
1262 self.theclass(2000, 1, 31, 23) # no exception
1263 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1264 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1265 # bad minutes
1266 self.theclass(2000, 1, 31, 23, 0) # no exception
1267 self.theclass(2000, 1, 31, 23, 59) # no exception
1268 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1269 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1270 # bad seconds
1271 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1272 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1273 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1274 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1275 # bad microseconds
1276 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1277 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1278 self.assertRaises(ValueError, self.theclass,
1279 2000, 1, 31, 23, 59, 59, -1)
1280 self.assertRaises(ValueError, self.theclass,
1281 2000, 1, 31, 23, 59, 59,
1282 1000000)
1283
1284 def test_hash_equality(self):
1285 d = self.theclass(2000, 12, 31, 23, 30, 17)
1286 e = self.theclass(2000, 12, 31, 23, 30, 17)
1287 self.assertEqual(d, e)
1288 self.assertEqual(hash(d), hash(e))
1289
1290 dic = {d: 1}
1291 dic[e] = 2
1292 self.assertEqual(len(dic), 1)
1293 self.assertEqual(dic[d], 2)
1294 self.assertEqual(dic[e], 2)
1295
1296 d = self.theclass(2001, 1, 1, 0, 5, 17)
1297 e = self.theclass(2001, 1, 1, 0, 5, 17)
1298 self.assertEqual(d, e)
1299 self.assertEqual(hash(d), hash(e))
1300
1301 dic = {d: 1}
1302 dic[e] = 2
1303 self.assertEqual(len(dic), 1)
1304 self.assertEqual(dic[d], 2)
1305 self.assertEqual(dic[e], 2)
1306
1307 def test_computations(self):
1308 a = self.theclass(2002, 1, 31)
1309 b = self.theclass(1956, 1, 31)
1310 diff = a-b
1311 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1312 self.assertEqual(diff.seconds, 0)
1313 self.assertEqual(diff.microseconds, 0)
1314 a = self.theclass(2002, 3, 2, 17, 6)
1315 millisec = timedelta(0, 0, 1000)
1316 hour = timedelta(0, 3600)
1317 day = timedelta(1)
1318 week = timedelta(7)
1319 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1320 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1321 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1322 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1323 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1324 self.assertEqual(a - hour, a + -hour)
1325 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1326 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1327 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1328 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1329 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1330 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1331 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1332 self.assertEqual((a + week) - a, week)
1333 self.assertEqual((a + day) - a, day)
1334 self.assertEqual((a + hour) - a, hour)
1335 self.assertEqual((a + millisec) - a, millisec)
1336 self.assertEqual((a - week) - a, -week)
1337 self.assertEqual((a - day) - a, -day)
1338 self.assertEqual((a - hour) - a, -hour)
1339 self.assertEqual((a - millisec) - a, -millisec)
1340 self.assertEqual(a - (a + week), -week)
1341 self.assertEqual(a - (a + day), -day)
1342 self.assertEqual(a - (a + hour), -hour)
1343 self.assertEqual(a - (a + millisec), -millisec)
1344 self.assertEqual(a - (a - week), week)
1345 self.assertEqual(a - (a - day), day)
1346 self.assertEqual(a - (a - hour), hour)
1347 self.assertEqual(a - (a - millisec), millisec)
1348 self.assertEqual(a + (week + day + hour + millisec),
1349 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1350 self.assertEqual(a + (week + day + hour + millisec),
1351 (((a + week) + day) + hour) + millisec)
1352 self.assertEqual(a - (week + day + hour + millisec),
1353 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1354 self.assertEqual(a - (week + day + hour + millisec),
1355 (((a - week) - day) - hour) - millisec)
1356 # Add/sub ints, longs, floats should be illegal
1357 for i in 1, 1L, 1.0:
1358 self.assertRaises(TypeError, lambda: a+i)
1359 self.assertRaises(TypeError, lambda: a-i)
1360 self.assertRaises(TypeError, lambda: i+a)
1361 self.assertRaises(TypeError, lambda: i-a)
1362
1363 # delta - datetime is senseless.
1364 self.assertRaises(TypeError, lambda: day - a)
1365 # mixing datetime and (delta or datetime) via * or // is senseless
1366 self.assertRaises(TypeError, lambda: day * a)
1367 self.assertRaises(TypeError, lambda: a * day)
1368 self.assertRaises(TypeError, lambda: day // a)
1369 self.assertRaises(TypeError, lambda: a // day)
1370 self.assertRaises(TypeError, lambda: a * a)
1371 self.assertRaises(TypeError, lambda: a // a)
1372 # datetime + datetime is senseless
1373 self.assertRaises(TypeError, lambda: a + a)
1374
1375 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001376 args = 6, 7, 23, 20, 59, 1, 64**2
1377 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001378 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001379 green = pickler.dumps(orig, proto)
1380 derived = unpickler.loads(green)
1381 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001382
Guido van Rossum275666f2003-02-07 21:49:01 +00001383 def test_more_pickling(self):
1384 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1385 s = pickle.dumps(a)
1386 b = pickle.loads(s)
1387 self.assertEqual(b.year, 2003)
1388 self.assertEqual(b.month, 2)
1389 self.assertEqual(b.day, 7)
1390
Tim Peters604c0132004-06-07 23:04:33 +00001391 def test_pickling_subclass_datetime(self):
1392 args = 6, 7, 23, 20, 59, 1, 64**2
1393 orig = SubclassDatetime(*args)
1394 for pickler, unpickler, proto in pickle_choices:
1395 green = pickler.dumps(orig, proto)
1396 derived = unpickler.loads(green)
1397 self.assertEqual(orig, derived)
1398
Tim Peters2a799bf2002-12-16 20:18:38 +00001399 def test_more_compare(self):
1400 # The test_compare() inherited from TestDate covers the error cases.
1401 # We just want to test lexicographic ordering on the members datetime
1402 # has that date lacks.
1403 args = [2000, 11, 29, 20, 58, 16, 999998]
1404 t1 = self.theclass(*args)
1405 t2 = self.theclass(*args)
1406 self.failUnless(t1 == t2)
1407 self.failUnless(t1 <= t2)
1408 self.failUnless(t1 >= t2)
1409 self.failUnless(not t1 != t2)
1410 self.failUnless(not t1 < t2)
1411 self.failUnless(not t1 > t2)
1412 self.assertEqual(cmp(t1, t2), 0)
1413 self.assertEqual(cmp(t2, t1), 0)
1414
1415 for i in range(len(args)):
1416 newargs = args[:]
1417 newargs[i] = args[i] + 1
1418 t2 = self.theclass(*newargs) # this is larger than t1
1419 self.failUnless(t1 < t2)
1420 self.failUnless(t2 > t1)
1421 self.failUnless(t1 <= t2)
1422 self.failUnless(t2 >= t1)
1423 self.failUnless(t1 != t2)
1424 self.failUnless(t2 != t1)
1425 self.failUnless(not t1 == t2)
1426 self.failUnless(not t2 == t1)
1427 self.failUnless(not t1 > t2)
1428 self.failUnless(not t2 < t1)
1429 self.failUnless(not t1 >= t2)
1430 self.failUnless(not t2 <= t1)
1431 self.assertEqual(cmp(t1, t2), -1)
1432 self.assertEqual(cmp(t2, t1), 1)
1433
1434
1435 # A helper for timestamp constructor tests.
1436 def verify_field_equality(self, expected, got):
1437 self.assertEqual(expected.tm_year, got.year)
1438 self.assertEqual(expected.tm_mon, got.month)
1439 self.assertEqual(expected.tm_mday, got.day)
1440 self.assertEqual(expected.tm_hour, got.hour)
1441 self.assertEqual(expected.tm_min, got.minute)
1442 self.assertEqual(expected.tm_sec, got.second)
1443
1444 def test_fromtimestamp(self):
1445 import time
1446
1447 ts = time.time()
1448 expected = time.localtime(ts)
1449 got = self.theclass.fromtimestamp(ts)
1450 self.verify_field_equality(expected, got)
1451
1452 def test_utcfromtimestamp(self):
1453 import time
1454
1455 ts = time.time()
1456 expected = time.gmtime(ts)
1457 got = self.theclass.utcfromtimestamp(ts)
1458 self.verify_field_equality(expected, got)
1459
Georg Brandl6d78a582006-04-28 19:09:24 +00001460 def test_microsecond_rounding(self):
1461 # Test whether fromtimestamp "rounds up" floats that are less
1462 # than one microsecond smaller than an integer.
1463 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1464 self.theclass.fromtimestamp(1))
1465
Tim Peters1b6f7a92004-06-20 02:50:16 +00001466 def test_insane_fromtimestamp(self):
1467 # It's possible that some platform maps time_t to double,
1468 # and that this test will fail there. This test should
1469 # exempt such platforms (provided they return reasonable
1470 # results!).
1471 for insane in -1e200, 1e200:
1472 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1473 insane)
1474
1475 def test_insane_utcfromtimestamp(self):
1476 # It's possible that some platform maps time_t to double,
1477 # and that this test will fail there. This test should
1478 # exempt such platforms (provided they return reasonable
1479 # results!).
1480 for insane in -1e200, 1e200:
1481 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1482 insane)
1483
Guido van Rossum2054ee92007-03-06 15:50:01 +00001484 def test_negative_float_fromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001485 # Windows doesn't accept negative timestamps
1486 if os.name == "nt":
1487 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001488 # The result is tz-dependent; at least test that this doesn't
1489 # fail (like it did before bug 1646728 was fixed).
1490 self.theclass.fromtimestamp(-1.05)
1491
1492 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001493 # Windows doesn't accept negative timestamps
1494 if os.name == "nt":
1495 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001496 d = self.theclass.utcfromtimestamp(-1.05)
1497 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1498
Tim Peters2a799bf2002-12-16 20:18:38 +00001499 def test_utcnow(self):
1500 import time
1501
1502 # Call it a success if utcnow() and utcfromtimestamp() are within
1503 # a second of each other.
1504 tolerance = timedelta(seconds=1)
1505 for dummy in range(3):
1506 from_now = self.theclass.utcnow()
1507 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1508 if abs(from_timestamp - from_now) <= tolerance:
1509 break
1510 # Else try again a few times.
1511 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1512
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001513 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001514 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001515
Skip Montanarofc070d22008-03-15 16:04:45 +00001516 string = '2004-12-01 13:02:47.197'
1517 format = '%Y-%m-%d %H:%M:%S.%f'
1518 result, frac = _strptime._strptime(string, format)
1519 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001520 got = self.theclass.strptime(string, format)
1521 self.assertEqual(expected, got)
1522
Tim Peters2a799bf2002-12-16 20:18:38 +00001523 def test_more_timetuple(self):
1524 # This tests fields beyond those tested by the TestDate.test_timetuple.
1525 t = self.theclass(2004, 12, 31, 6, 22, 33)
1526 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1527 self.assertEqual(t.timetuple(),
1528 (t.year, t.month, t.day,
1529 t.hour, t.minute, t.second,
1530 t.weekday(),
1531 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1532 -1))
1533 tt = t.timetuple()
1534 self.assertEqual(tt.tm_year, t.year)
1535 self.assertEqual(tt.tm_mon, t.month)
1536 self.assertEqual(tt.tm_mday, t.day)
1537 self.assertEqual(tt.tm_hour, t.hour)
1538 self.assertEqual(tt.tm_min, t.minute)
1539 self.assertEqual(tt.tm_sec, t.second)
1540 self.assertEqual(tt.tm_wday, t.weekday())
1541 self.assertEqual(tt.tm_yday, t.toordinal() -
1542 date(t.year, 1, 1).toordinal() + 1)
1543 self.assertEqual(tt.tm_isdst, -1)
1544
1545 def test_more_strftime(self):
1546 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001547 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1548 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1549 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001550
1551 def test_extract(self):
1552 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1553 self.assertEqual(dt.date(), date(2002, 3, 4))
1554 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1555
1556 def test_combine(self):
1557 d = date(2002, 3, 4)
1558 t = time(18, 45, 3, 1234)
1559 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1560 combine = self.theclass.combine
1561 dt = combine(d, t)
1562 self.assertEqual(dt, expected)
1563
1564 dt = combine(time=t, date=d)
1565 self.assertEqual(dt, expected)
1566
1567 self.assertEqual(d, dt.date())
1568 self.assertEqual(t, dt.time())
1569 self.assertEqual(dt, combine(dt.date(), dt.time()))
1570
1571 self.assertRaises(TypeError, combine) # need an arg
1572 self.assertRaises(TypeError, combine, d) # need two args
1573 self.assertRaises(TypeError, combine, t, d) # args reversed
1574 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1575 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1576
Tim Peters12bf3392002-12-24 05:41:27 +00001577 def test_replace(self):
1578 cls = self.theclass
1579 args = [1, 2, 3, 4, 5, 6, 7]
1580 base = cls(*args)
1581 self.assertEqual(base, base.replace())
1582
1583 i = 0
1584 for name, newval in (("year", 2),
1585 ("month", 3),
1586 ("day", 4),
1587 ("hour", 5),
1588 ("minute", 6),
1589 ("second", 7),
1590 ("microsecond", 8)):
1591 newargs = args[:]
1592 newargs[i] = newval
1593 expected = cls(*newargs)
1594 got = base.replace(**{name: newval})
1595 self.assertEqual(expected, got)
1596 i += 1
1597
1598 # Out of bounds.
1599 base = cls(2000, 2, 29)
1600 self.assertRaises(ValueError, base.replace, year=2001)
1601
Tim Peters80475bb2002-12-25 07:40:55 +00001602 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001603 # Pretty boring! The TZ test is more interesting here. astimezone()
1604 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001605 dt = self.theclass.now()
1606 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001607 self.assertRaises(TypeError, dt.astimezone) # not enough args
1608 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1609 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001610 self.assertRaises(ValueError, dt.astimezone, f) # naive
1611 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001612
Tim Peters52dcce22003-01-23 16:36:11 +00001613 class Bogus(tzinfo):
1614 def utcoffset(self, dt): return None
1615 def dst(self, dt): return timedelta(0)
1616 bog = Bogus()
1617 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1618
1619 class AlsoBogus(tzinfo):
1620 def utcoffset(self, dt): return timedelta(0)
1621 def dst(self, dt): return None
1622 alsobog = AlsoBogus()
1623 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001624
Tim Petersa98924a2003-05-17 05:55:19 +00001625 def test_subclass_datetime(self):
1626
1627 class C(self.theclass):
1628 theAnswer = 42
1629
1630 def __new__(cls, *args, **kws):
1631 temp = kws.copy()
1632 extra = temp.pop('extra')
1633 result = self.theclass.__new__(cls, *args, **temp)
1634 result.extra = extra
1635 return result
1636
1637 def newmeth(self, start):
1638 return start + self.year + self.month + self.second
1639
1640 args = 2003, 4, 14, 12, 13, 41
1641
1642 dt1 = self.theclass(*args)
1643 dt2 = C(*args, **{'extra': 7})
1644
1645 self.assertEqual(dt2.__class__, C)
1646 self.assertEqual(dt2.theAnswer, 42)
1647 self.assertEqual(dt2.extra, 7)
1648 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1649 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1650 dt1.second - 7)
1651
Tim Peters604c0132004-06-07 23:04:33 +00001652class SubclassTime(time):
1653 sub_var = 1
1654
Collin Winterc2898c52007-04-25 17:29:52 +00001655class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001656
1657 theclass = time
1658
1659 def test_basic_attributes(self):
1660 t = self.theclass(12, 0)
1661 self.assertEqual(t.hour, 12)
1662 self.assertEqual(t.minute, 0)
1663 self.assertEqual(t.second, 0)
1664 self.assertEqual(t.microsecond, 0)
1665
1666 def test_basic_attributes_nonzero(self):
1667 # Make sure all attributes are non-zero so bugs in
1668 # bit-shifting access show up.
1669 t = self.theclass(12, 59, 59, 8000)
1670 self.assertEqual(t.hour, 12)
1671 self.assertEqual(t.minute, 59)
1672 self.assertEqual(t.second, 59)
1673 self.assertEqual(t.microsecond, 8000)
1674
1675 def test_roundtrip(self):
1676 t = self.theclass(1, 2, 3, 4)
1677
1678 # Verify t -> string -> time identity.
1679 s = repr(t)
1680 self.failUnless(s.startswith('datetime.'))
1681 s = s[9:]
1682 t2 = eval(s)
1683 self.assertEqual(t, t2)
1684
1685 # Verify identity via reconstructing from pieces.
1686 t2 = self.theclass(t.hour, t.minute, t.second,
1687 t.microsecond)
1688 self.assertEqual(t, t2)
1689
1690 def test_comparing(self):
1691 args = [1, 2, 3, 4]
1692 t1 = self.theclass(*args)
1693 t2 = self.theclass(*args)
1694 self.failUnless(t1 == t2)
1695 self.failUnless(t1 <= t2)
1696 self.failUnless(t1 >= t2)
1697 self.failUnless(not t1 != t2)
1698 self.failUnless(not t1 < t2)
1699 self.failUnless(not t1 > t2)
1700 self.assertEqual(cmp(t1, t2), 0)
1701 self.assertEqual(cmp(t2, t1), 0)
1702
1703 for i in range(len(args)):
1704 newargs = args[:]
1705 newargs[i] = args[i] + 1
1706 t2 = self.theclass(*newargs) # this is larger than t1
1707 self.failUnless(t1 < t2)
1708 self.failUnless(t2 > t1)
1709 self.failUnless(t1 <= t2)
1710 self.failUnless(t2 >= t1)
1711 self.failUnless(t1 != t2)
1712 self.failUnless(t2 != t1)
1713 self.failUnless(not t1 == t2)
1714 self.failUnless(not t2 == t1)
1715 self.failUnless(not t1 > t2)
1716 self.failUnless(not t2 < t1)
1717 self.failUnless(not t1 >= t2)
1718 self.failUnless(not t2 <= t1)
1719 self.assertEqual(cmp(t1, t2), -1)
1720 self.assertEqual(cmp(t2, t1), 1)
1721
Tim Peters68124bb2003-02-08 03:46:31 +00001722 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001723 self.assertEqual(t1 == badarg, False)
1724 self.assertEqual(t1 != badarg, True)
1725 self.assertEqual(badarg == t1, False)
1726 self.assertEqual(badarg != t1, True)
1727
Tim Peters2a799bf2002-12-16 20:18:38 +00001728 self.assertRaises(TypeError, lambda: t1 <= badarg)
1729 self.assertRaises(TypeError, lambda: t1 < badarg)
1730 self.assertRaises(TypeError, lambda: t1 > badarg)
1731 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001732 self.assertRaises(TypeError, lambda: badarg <= t1)
1733 self.assertRaises(TypeError, lambda: badarg < t1)
1734 self.assertRaises(TypeError, lambda: badarg > t1)
1735 self.assertRaises(TypeError, lambda: badarg >= t1)
1736
1737 def test_bad_constructor_arguments(self):
1738 # bad hours
1739 self.theclass(0, 0) # no exception
1740 self.theclass(23, 0) # no exception
1741 self.assertRaises(ValueError, self.theclass, -1, 0)
1742 self.assertRaises(ValueError, self.theclass, 24, 0)
1743 # bad minutes
1744 self.theclass(23, 0) # no exception
1745 self.theclass(23, 59) # no exception
1746 self.assertRaises(ValueError, self.theclass, 23, -1)
1747 self.assertRaises(ValueError, self.theclass, 23, 60)
1748 # bad seconds
1749 self.theclass(23, 59, 0) # no exception
1750 self.theclass(23, 59, 59) # no exception
1751 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1752 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1753 # bad microseconds
1754 self.theclass(23, 59, 59, 0) # no exception
1755 self.theclass(23, 59, 59, 999999) # no exception
1756 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1757 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1758
1759 def test_hash_equality(self):
1760 d = self.theclass(23, 30, 17)
1761 e = self.theclass(23, 30, 17)
1762 self.assertEqual(d, e)
1763 self.assertEqual(hash(d), hash(e))
1764
1765 dic = {d: 1}
1766 dic[e] = 2
1767 self.assertEqual(len(dic), 1)
1768 self.assertEqual(dic[d], 2)
1769 self.assertEqual(dic[e], 2)
1770
1771 d = self.theclass(0, 5, 17)
1772 e = self.theclass(0, 5, 17)
1773 self.assertEqual(d, e)
1774 self.assertEqual(hash(d), hash(e))
1775
1776 dic = {d: 1}
1777 dic[e] = 2
1778 self.assertEqual(len(dic), 1)
1779 self.assertEqual(dic[d], 2)
1780 self.assertEqual(dic[e], 2)
1781
1782 def test_isoformat(self):
1783 t = self.theclass(4, 5, 1, 123)
1784 self.assertEqual(t.isoformat(), "04:05:01.000123")
1785 self.assertEqual(t.isoformat(), str(t))
1786
1787 t = self.theclass()
1788 self.assertEqual(t.isoformat(), "00:00:00")
1789 self.assertEqual(t.isoformat(), str(t))
1790
1791 t = self.theclass(microsecond=1)
1792 self.assertEqual(t.isoformat(), "00:00:00.000001")
1793 self.assertEqual(t.isoformat(), str(t))
1794
1795 t = self.theclass(microsecond=10)
1796 self.assertEqual(t.isoformat(), "00:00:00.000010")
1797 self.assertEqual(t.isoformat(), str(t))
1798
1799 t = self.theclass(microsecond=100)
1800 self.assertEqual(t.isoformat(), "00:00:00.000100")
1801 self.assertEqual(t.isoformat(), str(t))
1802
1803 t = self.theclass(microsecond=1000)
1804 self.assertEqual(t.isoformat(), "00:00:00.001000")
1805 self.assertEqual(t.isoformat(), str(t))
1806
1807 t = self.theclass(microsecond=10000)
1808 self.assertEqual(t.isoformat(), "00:00:00.010000")
1809 self.assertEqual(t.isoformat(), str(t))
1810
1811 t = self.theclass(microsecond=100000)
1812 self.assertEqual(t.isoformat(), "00:00:00.100000")
1813 self.assertEqual(t.isoformat(), str(t))
1814
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001815 def test_1653736(self):
1816 # verify it doesn't accept extra keyword arguments
1817 t = self.theclass(second=1)
1818 self.assertRaises(TypeError, t.isoformat, foo=3)
1819
Tim Peters2a799bf2002-12-16 20:18:38 +00001820 def test_strftime(self):
1821 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001822 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001823 # A naive object replaces %z and %Z with empty strings.
1824 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1825
Eric Smitha9f7d622008-02-17 19:46:49 +00001826 def test_format(self):
1827 t = self.theclass(1, 2, 3, 4)
1828 self.assertEqual(t.__format__(''), str(t))
1829
1830 # check that a derived class's __str__() gets called
1831 class A(self.theclass):
1832 def __str__(self):
1833 return 'A'
1834 a = A(1, 2, 3, 4)
1835 self.assertEqual(a.__format__(''), 'A')
1836
1837 # check that a derived class's strftime gets called
1838 class B(self.theclass):
1839 def strftime(self, format_spec):
1840 return 'B'
1841 b = B(1, 2, 3, 4)
1842 self.assertEqual(b.__format__(''), str(t))
1843
1844 for fmt in ['%H %M %S',
1845 ]:
1846 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1847 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1848 self.assertEqual(b.__format__(fmt), 'B')
1849
Tim Peters2a799bf2002-12-16 20:18:38 +00001850 def test_str(self):
1851 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1852 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1853 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1854 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1855 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1856
1857 def test_repr(self):
1858 name = 'datetime.' + self.theclass.__name__
1859 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1860 "%s(1, 2, 3, 4)" % name)
1861 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1862 "%s(10, 2, 3, 4000)" % name)
1863 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1864 "%s(0, 2, 3, 400000)" % name)
1865 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1866 "%s(12, 2, 3)" % name)
1867 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1868 "%s(23, 15)" % name)
1869
1870 def test_resolution_info(self):
1871 self.assert_(isinstance(self.theclass.min, self.theclass))
1872 self.assert_(isinstance(self.theclass.max, self.theclass))
1873 self.assert_(isinstance(self.theclass.resolution, timedelta))
1874 self.assert_(self.theclass.max > self.theclass.min)
1875
1876 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001877 args = 20, 59, 16, 64**2
1878 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001879 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001880 green = pickler.dumps(orig, proto)
1881 derived = unpickler.loads(green)
1882 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001883
Tim Peters604c0132004-06-07 23:04:33 +00001884 def test_pickling_subclass_time(self):
1885 args = 20, 59, 16, 64**2
1886 orig = SubclassTime(*args)
1887 for pickler, unpickler, proto in pickle_choices:
1888 green = pickler.dumps(orig, proto)
1889 derived = unpickler.loads(green)
1890 self.assertEqual(orig, derived)
1891
Tim Peters2a799bf2002-12-16 20:18:38 +00001892 def test_bool(self):
1893 cls = self.theclass
1894 self.failUnless(cls(1))
1895 self.failUnless(cls(0, 1))
1896 self.failUnless(cls(0, 0, 1))
1897 self.failUnless(cls(0, 0, 0, 1))
1898 self.failUnless(not cls(0))
1899 self.failUnless(not cls())
1900
Tim Peters12bf3392002-12-24 05:41:27 +00001901 def test_replace(self):
1902 cls = self.theclass
1903 args = [1, 2, 3, 4]
1904 base = cls(*args)
1905 self.assertEqual(base, base.replace())
1906
1907 i = 0
1908 for name, newval in (("hour", 5),
1909 ("minute", 6),
1910 ("second", 7),
1911 ("microsecond", 8)):
1912 newargs = args[:]
1913 newargs[i] = newval
1914 expected = cls(*newargs)
1915 got = base.replace(**{name: newval})
1916 self.assertEqual(expected, got)
1917 i += 1
1918
1919 # Out of bounds.
1920 base = cls(1)
1921 self.assertRaises(ValueError, base.replace, hour=24)
1922 self.assertRaises(ValueError, base.replace, minute=-1)
1923 self.assertRaises(ValueError, base.replace, second=100)
1924 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1925
Tim Petersa98924a2003-05-17 05:55:19 +00001926 def test_subclass_time(self):
1927
1928 class C(self.theclass):
1929 theAnswer = 42
1930
1931 def __new__(cls, *args, **kws):
1932 temp = kws.copy()
1933 extra = temp.pop('extra')
1934 result = self.theclass.__new__(cls, *args, **temp)
1935 result.extra = extra
1936 return result
1937
1938 def newmeth(self, start):
1939 return start + self.hour + self.second
1940
1941 args = 4, 5, 6
1942
1943 dt1 = self.theclass(*args)
1944 dt2 = C(*args, **{'extra': 7})
1945
1946 self.assertEqual(dt2.__class__, C)
1947 self.assertEqual(dt2.theAnswer, 42)
1948 self.assertEqual(dt2.extra, 7)
1949 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1950 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1951
Armin Rigof4afb212005-11-07 07:15:48 +00001952 def test_backdoor_resistance(self):
1953 # see TestDate.test_backdoor_resistance().
1954 base = '2:59.0'
1955 for hour_byte in ' ', '9', chr(24), '\xff':
1956 self.assertRaises(TypeError, self.theclass,
1957 hour_byte + base[1:])
1958
Tim Peters855fe882002-12-22 03:43:39 +00001959# A mixin for classes with a tzinfo= argument. Subclasses must define
1960# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001961# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001962class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001963
Tim Petersbad8ff02002-12-30 20:52:32 +00001964 def test_argument_passing(self):
1965 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001966 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001967 class introspective(tzinfo):
1968 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001969 def utcoffset(self, dt):
1970 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001971 dst = utcoffset
1972
1973 obj = cls(1, 2, 3, tzinfo=introspective())
1974
Tim Peters0bf60bd2003-01-08 20:40:01 +00001975 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001976 self.assertEqual(obj.tzname(), expected)
1977
Tim Peters0bf60bd2003-01-08 20:40:01 +00001978 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001979 self.assertEqual(obj.utcoffset(), expected)
1980 self.assertEqual(obj.dst(), expected)
1981
Tim Peters855fe882002-12-22 03:43:39 +00001982 def test_bad_tzinfo_classes(self):
1983 cls = self.theclass
1984 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001985
Tim Peters855fe882002-12-22 03:43:39 +00001986 class NiceTry(object):
1987 def __init__(self): pass
1988 def utcoffset(self, dt): pass
1989 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1990
1991 class BetterTry(tzinfo):
1992 def __init__(self): pass
1993 def utcoffset(self, dt): pass
1994 b = BetterTry()
1995 t = cls(1, 1, 1, tzinfo=b)
1996 self.failUnless(t.tzinfo is b)
1997
1998 def test_utc_offset_out_of_bounds(self):
1999 class Edgy(tzinfo):
2000 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002001 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002002 def utcoffset(self, dt):
2003 return self.offset
2004
2005 cls = self.theclass
2006 for offset, legit in ((-1440, False),
2007 (-1439, True),
2008 (1439, True),
2009 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002010 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002011 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002012 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002013 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002014 else:
2015 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002016 if legit:
2017 aofs = abs(offset)
2018 h, m = divmod(aofs, 60)
2019 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002020 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002021 t = t.timetz()
2022 self.assertEqual(str(t), "01:02:03" + tag)
2023 else:
2024 self.assertRaises(ValueError, str, t)
2025
2026 def test_tzinfo_classes(self):
2027 cls = self.theclass
2028 class C1(tzinfo):
2029 def utcoffset(self, dt): return None
2030 def dst(self, dt): return None
2031 def tzname(self, dt): return None
2032 for t in (cls(1, 1, 1),
2033 cls(1, 1, 1, tzinfo=None),
2034 cls(1, 1, 1, tzinfo=C1())):
2035 self.failUnless(t.utcoffset() is None)
2036 self.failUnless(t.dst() is None)
2037 self.failUnless(t.tzname() is None)
2038
Tim Peters855fe882002-12-22 03:43:39 +00002039 class C3(tzinfo):
2040 def utcoffset(self, dt): return timedelta(minutes=-1439)
2041 def dst(self, dt): return timedelta(minutes=1439)
2042 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002043 t = cls(1, 1, 1, tzinfo=C3())
2044 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2045 self.assertEqual(t.dst(), timedelta(minutes=1439))
2046 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002047
2048 # Wrong types.
2049 class C4(tzinfo):
2050 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002051 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002052 def tzname(self, dt): return 0
2053 t = cls(1, 1, 1, tzinfo=C4())
2054 self.assertRaises(TypeError, t.utcoffset)
2055 self.assertRaises(TypeError, t.dst)
2056 self.assertRaises(TypeError, t.tzname)
2057
2058 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002059 class C6(tzinfo):
2060 def utcoffset(self, dt): return timedelta(hours=-24)
2061 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002062 t = cls(1, 1, 1, tzinfo=C6())
2063 self.assertRaises(ValueError, t.utcoffset)
2064 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002065
2066 # Not a whole number of minutes.
2067 class C7(tzinfo):
2068 def utcoffset(self, dt): return timedelta(seconds=61)
2069 def dst(self, dt): return timedelta(microseconds=-81)
2070 t = cls(1, 1, 1, tzinfo=C7())
2071 self.assertRaises(ValueError, t.utcoffset)
2072 self.assertRaises(ValueError, t.dst)
2073
Tim Peters4c0db782002-12-26 05:01:19 +00002074 def test_aware_compare(self):
2075 cls = self.theclass
2076
Tim Peters60c76e42002-12-27 00:41:11 +00002077 # Ensure that utcoffset() gets ignored if the comparands have
2078 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002079 class OperandDependentOffset(tzinfo):
2080 def utcoffset(self, t):
2081 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002082 # d0 and d1 equal after adjustment
2083 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002084 else:
Tim Peters397301e2003-01-02 21:28:08 +00002085 # d2 off in the weeds
2086 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002087
2088 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2089 d0 = base.replace(minute=3)
2090 d1 = base.replace(minute=9)
2091 d2 = base.replace(minute=11)
2092 for x in d0, d1, d2:
2093 for y in d0, d1, d2:
2094 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002095 expected = cmp(x.minute, y.minute)
2096 self.assertEqual(got, expected)
2097
2098 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002099 # Note that a time can't actually have an operand-depedent offset,
2100 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2101 # so skip this test for time.
2102 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002103 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2104 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2105 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2106 for x in d0, d1, d2:
2107 for y in d0, d1, d2:
2108 got = cmp(x, y)
2109 if (x is d0 or x is d1) and (y is d0 or y is d1):
2110 expected = 0
2111 elif x is y is d2:
2112 expected = 0
2113 elif x is d2:
2114 expected = -1
2115 else:
2116 assert y is d2
2117 expected = 1
2118 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002119
Tim Peters855fe882002-12-22 03:43:39 +00002120
Tim Peters0bf60bd2003-01-08 20:40:01 +00002121# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002122class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002123 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002124
2125 def test_empty(self):
2126 t = self.theclass()
2127 self.assertEqual(t.hour, 0)
2128 self.assertEqual(t.minute, 0)
2129 self.assertEqual(t.second, 0)
2130 self.assertEqual(t.microsecond, 0)
2131 self.failUnless(t.tzinfo is None)
2132
Tim Peters2a799bf2002-12-16 20:18:38 +00002133 def test_zones(self):
2134 est = FixedOffset(-300, "EST", 1)
2135 utc = FixedOffset(0, "UTC", -2)
2136 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002137 t1 = time( 7, 47, tzinfo=est)
2138 t2 = time(12, 47, tzinfo=utc)
2139 t3 = time(13, 47, tzinfo=met)
2140 t4 = time(microsecond=40)
2141 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002142
2143 self.assertEqual(t1.tzinfo, est)
2144 self.assertEqual(t2.tzinfo, utc)
2145 self.assertEqual(t3.tzinfo, met)
2146 self.failUnless(t4.tzinfo is None)
2147 self.assertEqual(t5.tzinfo, utc)
2148
Tim Peters855fe882002-12-22 03:43:39 +00002149 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2150 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2151 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002152 self.failUnless(t4.utcoffset() is None)
2153 self.assertRaises(TypeError, t1.utcoffset, "no args")
2154
2155 self.assertEqual(t1.tzname(), "EST")
2156 self.assertEqual(t2.tzname(), "UTC")
2157 self.assertEqual(t3.tzname(), "MET")
2158 self.failUnless(t4.tzname() is None)
2159 self.assertRaises(TypeError, t1.tzname, "no args")
2160
Tim Peters855fe882002-12-22 03:43:39 +00002161 self.assertEqual(t1.dst(), timedelta(minutes=1))
2162 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2163 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002164 self.failUnless(t4.dst() is None)
2165 self.assertRaises(TypeError, t1.dst, "no args")
2166
2167 self.assertEqual(hash(t1), hash(t2))
2168 self.assertEqual(hash(t1), hash(t3))
2169 self.assertEqual(hash(t2), hash(t3))
2170
2171 self.assertEqual(t1, t2)
2172 self.assertEqual(t1, t3)
2173 self.assertEqual(t2, t3)
2174 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2175 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2176 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2177
2178 self.assertEqual(str(t1), "07:47:00-05:00")
2179 self.assertEqual(str(t2), "12:47:00+00:00")
2180 self.assertEqual(str(t3), "13:47:00+01:00")
2181 self.assertEqual(str(t4), "00:00:00.000040")
2182 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2183
2184 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2185 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2186 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2187 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2188 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2189
Tim Peters0bf60bd2003-01-08 20:40:01 +00002190 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002191 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2192 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2193 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2194 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2195 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2196
2197 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2198 "07:47:00 %Z=EST %z=-0500")
2199 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2200 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2201
2202 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002203 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002204 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2205 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2206
Tim Petersb92bb712002-12-21 17:44:07 +00002207 # Check that an invalid tzname result raises an exception.
2208 class Badtzname(tzinfo):
2209 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002210 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002211 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2212 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002213
2214 def test_hash_edge_cases(self):
2215 # Offsets that overflow a basic time.
2216 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2217 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2218 self.assertEqual(hash(t1), hash(t2))
2219
2220 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2221 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2222 self.assertEqual(hash(t1), hash(t2))
2223
Tim Peters2a799bf2002-12-16 20:18:38 +00002224 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002225 # Try one without a tzinfo.
2226 args = 20, 59, 16, 64**2
2227 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002228 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002229 green = pickler.dumps(orig, proto)
2230 derived = unpickler.loads(green)
2231 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002232
2233 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002234 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002235 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002236 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002237 green = pickler.dumps(orig, proto)
2238 derived = unpickler.loads(green)
2239 self.assertEqual(orig, derived)
2240 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2241 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2242 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002243
2244 def test_more_bool(self):
2245 # Test cases with non-None tzinfo.
2246 cls = self.theclass
2247
2248 t = cls(0, tzinfo=FixedOffset(-300, ""))
2249 self.failUnless(t)
2250
2251 t = cls(5, tzinfo=FixedOffset(-300, ""))
2252 self.failUnless(t)
2253
2254 t = cls(5, tzinfo=FixedOffset(300, ""))
2255 self.failUnless(not t)
2256
2257 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2258 self.failUnless(not t)
2259
2260 # Mostly ensuring this doesn't overflow internally.
2261 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2262 self.failUnless(t)
2263
2264 # But this should yield a value error -- the utcoffset is bogus.
2265 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2266 self.assertRaises(ValueError, lambda: bool(t))
2267
2268 # Likewise.
2269 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2270 self.assertRaises(ValueError, lambda: bool(t))
2271
Tim Peters12bf3392002-12-24 05:41:27 +00002272 def test_replace(self):
2273 cls = self.theclass
2274 z100 = FixedOffset(100, "+100")
2275 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2276 args = [1, 2, 3, 4, z100]
2277 base = cls(*args)
2278 self.assertEqual(base, base.replace())
2279
2280 i = 0
2281 for name, newval in (("hour", 5),
2282 ("minute", 6),
2283 ("second", 7),
2284 ("microsecond", 8),
2285 ("tzinfo", zm200)):
2286 newargs = args[:]
2287 newargs[i] = newval
2288 expected = cls(*newargs)
2289 got = base.replace(**{name: newval})
2290 self.assertEqual(expected, got)
2291 i += 1
2292
2293 # Ensure we can get rid of a tzinfo.
2294 self.assertEqual(base.tzname(), "+100")
2295 base2 = base.replace(tzinfo=None)
2296 self.failUnless(base2.tzinfo is None)
2297 self.failUnless(base2.tzname() is None)
2298
2299 # Ensure we can add one.
2300 base3 = base2.replace(tzinfo=z100)
2301 self.assertEqual(base, base3)
2302 self.failUnless(base.tzinfo is base3.tzinfo)
2303
2304 # Out of bounds.
2305 base = cls(1)
2306 self.assertRaises(ValueError, base.replace, hour=24)
2307 self.assertRaises(ValueError, base.replace, minute=-1)
2308 self.assertRaises(ValueError, base.replace, second=100)
2309 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2310
Tim Peters60c76e42002-12-27 00:41:11 +00002311 def test_mixed_compare(self):
2312 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002313 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002314 self.assertEqual(t1, t2)
2315 t2 = t2.replace(tzinfo=None)
2316 self.assertEqual(t1, t2)
2317 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2318 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002319 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2320 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002321
Tim Peters0bf60bd2003-01-08 20:40:01 +00002322 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002323 class Varies(tzinfo):
2324 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002325 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002326 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002327 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002328 return self.offset
2329
2330 v = Varies()
2331 t1 = t2.replace(tzinfo=v)
2332 t2 = t2.replace(tzinfo=v)
2333 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2334 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2335 self.assertEqual(t1, t2)
2336
2337 # But if they're not identical, it isn't ignored.
2338 t2 = t2.replace(tzinfo=Varies())
2339 self.failUnless(t1 < t2) # t1's offset counter still going up
2340
Tim Petersa98924a2003-05-17 05:55:19 +00002341 def test_subclass_timetz(self):
2342
2343 class C(self.theclass):
2344 theAnswer = 42
2345
2346 def __new__(cls, *args, **kws):
2347 temp = kws.copy()
2348 extra = temp.pop('extra')
2349 result = self.theclass.__new__(cls, *args, **temp)
2350 result.extra = extra
2351 return result
2352
2353 def newmeth(self, start):
2354 return start + self.hour + self.second
2355
2356 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2357
2358 dt1 = self.theclass(*args)
2359 dt2 = C(*args, **{'extra': 7})
2360
2361 self.assertEqual(dt2.__class__, C)
2362 self.assertEqual(dt2.theAnswer, 42)
2363 self.assertEqual(dt2.extra, 7)
2364 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2365 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2366
Tim Peters4c0db782002-12-26 05:01:19 +00002367
Tim Peters0bf60bd2003-01-08 20:40:01 +00002368# Testing datetime objects with a non-None tzinfo.
2369
Collin Winterc2898c52007-04-25 17:29:52 +00002370class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002371 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002372
2373 def test_trivial(self):
2374 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2375 self.assertEqual(dt.year, 1)
2376 self.assertEqual(dt.month, 2)
2377 self.assertEqual(dt.day, 3)
2378 self.assertEqual(dt.hour, 4)
2379 self.assertEqual(dt.minute, 5)
2380 self.assertEqual(dt.second, 6)
2381 self.assertEqual(dt.microsecond, 7)
2382 self.assertEqual(dt.tzinfo, None)
2383
2384 def test_even_more_compare(self):
2385 # The test_compare() and test_more_compare() inherited from TestDate
2386 # and TestDateTime covered non-tzinfo cases.
2387
2388 # Smallest possible after UTC adjustment.
2389 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2390 # Largest possible after UTC adjustment.
2391 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2392 tzinfo=FixedOffset(-1439, ""))
2393
2394 # Make sure those compare correctly, and w/o overflow.
2395 self.failUnless(t1 < t2)
2396 self.failUnless(t1 != t2)
2397 self.failUnless(t2 > t1)
2398
2399 self.failUnless(t1 == t1)
2400 self.failUnless(t2 == t2)
2401
2402 # Equal afer adjustment.
2403 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2404 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2405 self.assertEqual(t1, t2)
2406
2407 # Change t1 not to subtract a minute, and t1 should be larger.
2408 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2409 self.failUnless(t1 > t2)
2410
2411 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2412 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2413 self.failUnless(t1 < t2)
2414
2415 # Back to the original t1, but make seconds resolve it.
2416 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2417 second=1)
2418 self.failUnless(t1 > t2)
2419
2420 # Likewise, but make microseconds resolve it.
2421 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2422 microsecond=1)
2423 self.failUnless(t1 > t2)
2424
2425 # Make t2 naive and it should fail.
2426 t2 = self.theclass.min
2427 self.assertRaises(TypeError, lambda: t1 == t2)
2428 self.assertEqual(t2, t2)
2429
2430 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2431 class Naive(tzinfo):
2432 def utcoffset(self, dt): return None
2433 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2434 self.assertRaises(TypeError, lambda: t1 == t2)
2435 self.assertEqual(t2, t2)
2436
2437 # OTOH, it's OK to compare two of these mixing the two ways of being
2438 # naive.
2439 t1 = self.theclass(5, 6, 7)
2440 self.assertEqual(t1, t2)
2441
2442 # Try a bogus uctoffset.
2443 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002444 def utcoffset(self, dt):
2445 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002446 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2447 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002448 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002449
Tim Peters2a799bf2002-12-16 20:18:38 +00002450 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002451 # Try one without a tzinfo.
2452 args = 6, 7, 23, 20, 59, 1, 64**2
2453 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002454 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002455 green = pickler.dumps(orig, proto)
2456 derived = unpickler.loads(green)
2457 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002458
2459 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002460 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002461 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002462 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002463 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002464 green = pickler.dumps(orig, proto)
2465 derived = unpickler.loads(green)
2466 self.assertEqual(orig, derived)
2467 self.failUnless(isinstance(derived.tzinfo,
2468 PicklableFixedOffset))
2469 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2470 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002471
2472 def test_extreme_hashes(self):
2473 # If an attempt is made to hash these via subtracting the offset
2474 # then hashing a datetime object, OverflowError results. The
2475 # Python implementation used to blow up here.
2476 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2477 hash(t)
2478 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2479 tzinfo=FixedOffset(-1439, ""))
2480 hash(t)
2481
2482 # OTOH, an OOB offset should blow up.
2483 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2484 self.assertRaises(ValueError, hash, t)
2485
2486 def test_zones(self):
2487 est = FixedOffset(-300, "EST")
2488 utc = FixedOffset(0, "UTC")
2489 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002490 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2491 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2492 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002493 self.assertEqual(t1.tzinfo, est)
2494 self.assertEqual(t2.tzinfo, utc)
2495 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002496 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2497 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2498 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002499 self.assertEqual(t1.tzname(), "EST")
2500 self.assertEqual(t2.tzname(), "UTC")
2501 self.assertEqual(t3.tzname(), "MET")
2502 self.assertEqual(hash(t1), hash(t2))
2503 self.assertEqual(hash(t1), hash(t3))
2504 self.assertEqual(hash(t2), hash(t3))
2505 self.assertEqual(t1, t2)
2506 self.assertEqual(t1, t3)
2507 self.assertEqual(t2, t3)
2508 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2509 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2510 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002511 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002512 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2513 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2514 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2515
2516 def test_combine(self):
2517 met = FixedOffset(60, "MET")
2518 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002519 tz = time(18, 45, 3, 1234, tzinfo=met)
2520 dt = datetime.combine(d, tz)
2521 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002522 tzinfo=met))
2523
2524 def test_extract(self):
2525 met = FixedOffset(60, "MET")
2526 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2527 self.assertEqual(dt.date(), date(2002, 3, 4))
2528 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002529 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002530
2531 def test_tz_aware_arithmetic(self):
2532 import random
2533
2534 now = self.theclass.now()
2535 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002536 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002537 nowaware = self.theclass.combine(now.date(), timeaware)
2538 self.failUnless(nowaware.tzinfo is tz55)
2539 self.assertEqual(nowaware.timetz(), timeaware)
2540
2541 # Can't mix aware and non-aware.
2542 self.assertRaises(TypeError, lambda: now - nowaware)
2543 self.assertRaises(TypeError, lambda: nowaware - now)
2544
Tim Peters0bf60bd2003-01-08 20:40:01 +00002545 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002546 self.assertRaises(TypeError, lambda: now + nowaware)
2547 self.assertRaises(TypeError, lambda: nowaware + now)
2548 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2549
2550 # Subtracting should yield 0.
2551 self.assertEqual(now - now, timedelta(0))
2552 self.assertEqual(nowaware - nowaware, timedelta(0))
2553
2554 # Adding a delta should preserve tzinfo.
2555 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2556 nowawareplus = nowaware + delta
2557 self.failUnless(nowaware.tzinfo is tz55)
2558 nowawareplus2 = delta + nowaware
2559 self.failUnless(nowawareplus2.tzinfo is tz55)
2560 self.assertEqual(nowawareplus, nowawareplus2)
2561
2562 # that - delta should be what we started with, and that - what we
2563 # started with should be delta.
2564 diff = nowawareplus - delta
2565 self.failUnless(diff.tzinfo is tz55)
2566 self.assertEqual(nowaware, diff)
2567 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2568 self.assertEqual(nowawareplus - nowaware, delta)
2569
2570 # Make up a random timezone.
2571 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002572 # Attach it to nowawareplus.
2573 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002574 self.failUnless(nowawareplus.tzinfo is tzr)
2575 # Make sure the difference takes the timezone adjustments into account.
2576 got = nowaware - nowawareplus
2577 # Expected: (nowaware base - nowaware offset) -
2578 # (nowawareplus base - nowawareplus offset) =
2579 # (nowaware base - nowawareplus base) +
2580 # (nowawareplus offset - nowaware offset) =
2581 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002582 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002583 self.assertEqual(got, expected)
2584
2585 # Try max possible difference.
2586 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2587 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2588 tzinfo=FixedOffset(-1439, "max"))
2589 maxdiff = max - min
2590 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2591 timedelta(minutes=2*1439))
2592
2593 def test_tzinfo_now(self):
2594 meth = self.theclass.now
2595 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2596 base = meth()
2597 # Try with and without naming the keyword.
2598 off42 = FixedOffset(42, "42")
2599 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002600 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002601 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002602 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002603 # Bad argument with and w/o naming the keyword.
2604 self.assertRaises(TypeError, meth, 16)
2605 self.assertRaises(TypeError, meth, tzinfo=16)
2606 # Bad keyword name.
2607 self.assertRaises(TypeError, meth, tinfo=off42)
2608 # Too many args.
2609 self.assertRaises(TypeError, meth, off42, off42)
2610
Tim Peters10cadce2003-01-23 19:58:02 +00002611 # We don't know which time zone we're in, and don't have a tzinfo
2612 # class to represent it, so seeing whether a tz argument actually
2613 # does a conversion is tricky.
2614 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2615 utc = FixedOffset(0, "utc", 0)
2616 for dummy in range(3):
2617 now = datetime.now(weirdtz)
2618 self.failUnless(now.tzinfo is weirdtz)
2619 utcnow = datetime.utcnow().replace(tzinfo=utc)
2620 now2 = utcnow.astimezone(weirdtz)
2621 if abs(now - now2) < timedelta(seconds=30):
2622 break
2623 # Else the code is broken, or more than 30 seconds passed between
2624 # calls; assuming the latter, just try again.
2625 else:
2626 # Three strikes and we're out.
2627 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2628
Tim Peters2a799bf2002-12-16 20:18:38 +00002629 def test_tzinfo_fromtimestamp(self):
2630 import time
2631 meth = self.theclass.fromtimestamp
2632 ts = time.time()
2633 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2634 base = meth(ts)
2635 # Try with and without naming the keyword.
2636 off42 = FixedOffset(42, "42")
2637 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002638 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002639 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002640 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002641 # Bad argument with and w/o naming the keyword.
2642 self.assertRaises(TypeError, meth, ts, 16)
2643 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2644 # Bad keyword name.
2645 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2646 # Too many args.
2647 self.assertRaises(TypeError, meth, ts, off42, off42)
2648 # Too few args.
2649 self.assertRaises(TypeError, meth)
2650
Tim Peters2a44a8d2003-01-23 20:53:10 +00002651 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002652 timestamp = 1000000000
2653 utcdatetime = datetime.utcfromtimestamp(timestamp)
2654 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2655 # But on some flavor of Mac, it's nowhere near that. So we can't have
2656 # any idea here what time that actually is, we can only test that
2657 # relative changes match.
2658 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2659 tz = FixedOffset(utcoffset, "tz", 0)
2660 expected = utcdatetime + utcoffset
2661 got = datetime.fromtimestamp(timestamp, tz)
2662 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002663
Tim Peters2a799bf2002-12-16 20:18:38 +00002664 def test_tzinfo_utcnow(self):
2665 meth = self.theclass.utcnow
2666 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2667 base = meth()
2668 # Try with and without naming the keyword; for whatever reason,
2669 # utcnow() doesn't accept a tzinfo argument.
2670 off42 = FixedOffset(42, "42")
2671 self.assertRaises(TypeError, meth, off42)
2672 self.assertRaises(TypeError, meth, tzinfo=off42)
2673
2674 def test_tzinfo_utcfromtimestamp(self):
2675 import time
2676 meth = self.theclass.utcfromtimestamp
2677 ts = time.time()
2678 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2679 base = meth(ts)
2680 # Try with and without naming the keyword; for whatever reason,
2681 # utcfromtimestamp() doesn't accept a tzinfo argument.
2682 off42 = FixedOffset(42, "42")
2683 self.assertRaises(TypeError, meth, ts, off42)
2684 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2685
2686 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002687 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002688 # DST flag.
2689 class DST(tzinfo):
2690 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002691 if isinstance(dstvalue, int):
2692 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002693 self.dstvalue = dstvalue
2694 def dst(self, dt):
2695 return self.dstvalue
2696
2697 cls = self.theclass
2698 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2699 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2700 t = d.timetuple()
2701 self.assertEqual(1, t.tm_year)
2702 self.assertEqual(1, t.tm_mon)
2703 self.assertEqual(1, t.tm_mday)
2704 self.assertEqual(10, t.tm_hour)
2705 self.assertEqual(20, t.tm_min)
2706 self.assertEqual(30, t.tm_sec)
2707 self.assertEqual(0, t.tm_wday)
2708 self.assertEqual(1, t.tm_yday)
2709 self.assertEqual(flag, t.tm_isdst)
2710
2711 # dst() returns wrong type.
2712 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2713
2714 # dst() at the edge.
2715 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2716 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2717
2718 # dst() out of range.
2719 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2720 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2721
2722 def test_utctimetuple(self):
2723 class DST(tzinfo):
2724 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002725 if isinstance(dstvalue, int):
2726 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002727 self.dstvalue = dstvalue
2728 def dst(self, dt):
2729 return self.dstvalue
2730
2731 cls = self.theclass
2732 # This can't work: DST didn't implement utcoffset.
2733 self.assertRaises(NotImplementedError,
2734 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2735
2736 class UOFS(DST):
2737 def __init__(self, uofs, dofs=None):
2738 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002739 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002740 def utcoffset(self, dt):
2741 return self.uofs
2742
2743 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2744 # in effect for a UTC time.
2745 for dstvalue in -33, 33, 0, None:
2746 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2747 t = d.utctimetuple()
2748 self.assertEqual(d.year, t.tm_year)
2749 self.assertEqual(d.month, t.tm_mon)
2750 self.assertEqual(d.day, t.tm_mday)
2751 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2752 self.assertEqual(13, t.tm_min)
2753 self.assertEqual(d.second, t.tm_sec)
2754 self.assertEqual(d.weekday(), t.tm_wday)
2755 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2756 t.tm_yday)
2757 self.assertEqual(0, t.tm_isdst)
2758
2759 # At the edges, UTC adjustment can normalize into years out-of-range
2760 # for a datetime object. Ensure that a correct timetuple is
2761 # created anyway.
2762 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2763 # That goes back 1 minute less than a full day.
2764 t = tiny.utctimetuple()
2765 self.assertEqual(t.tm_year, MINYEAR-1)
2766 self.assertEqual(t.tm_mon, 12)
2767 self.assertEqual(t.tm_mday, 31)
2768 self.assertEqual(t.tm_hour, 0)
2769 self.assertEqual(t.tm_min, 1)
2770 self.assertEqual(t.tm_sec, 37)
2771 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2772 self.assertEqual(t.tm_isdst, 0)
2773
2774 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2775 # That goes forward 1 minute less than a full day.
2776 t = huge.utctimetuple()
2777 self.assertEqual(t.tm_year, MAXYEAR+1)
2778 self.assertEqual(t.tm_mon, 1)
2779 self.assertEqual(t.tm_mday, 1)
2780 self.assertEqual(t.tm_hour, 23)
2781 self.assertEqual(t.tm_min, 58)
2782 self.assertEqual(t.tm_sec, 37)
2783 self.assertEqual(t.tm_yday, 1)
2784 self.assertEqual(t.tm_isdst, 0)
2785
2786 def test_tzinfo_isoformat(self):
2787 zero = FixedOffset(0, "+00:00")
2788 plus = FixedOffset(220, "+03:40")
2789 minus = FixedOffset(-231, "-03:51")
2790 unknown = FixedOffset(None, "")
2791
2792 cls = self.theclass
2793 datestr = '0001-02-03'
2794 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002795 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002796 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2797 timestr = '04:05:59' + (us and '.987001' or '')
2798 ofsstr = ofs is not None and d.tzname() or ''
2799 tailstr = timestr + ofsstr
2800 iso = d.isoformat()
2801 self.assertEqual(iso, datestr + 'T' + tailstr)
2802 self.assertEqual(iso, d.isoformat('T'))
2803 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2804 self.assertEqual(str(d), datestr + ' ' + tailstr)
2805
Tim Peters12bf3392002-12-24 05:41:27 +00002806 def test_replace(self):
2807 cls = self.theclass
2808 z100 = FixedOffset(100, "+100")
2809 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2810 args = [1, 2, 3, 4, 5, 6, 7, z100]
2811 base = cls(*args)
2812 self.assertEqual(base, base.replace())
2813
2814 i = 0
2815 for name, newval in (("year", 2),
2816 ("month", 3),
2817 ("day", 4),
2818 ("hour", 5),
2819 ("minute", 6),
2820 ("second", 7),
2821 ("microsecond", 8),
2822 ("tzinfo", zm200)):
2823 newargs = args[:]
2824 newargs[i] = newval
2825 expected = cls(*newargs)
2826 got = base.replace(**{name: newval})
2827 self.assertEqual(expected, got)
2828 i += 1
2829
2830 # Ensure we can get rid of a tzinfo.
2831 self.assertEqual(base.tzname(), "+100")
2832 base2 = base.replace(tzinfo=None)
2833 self.failUnless(base2.tzinfo is None)
2834 self.failUnless(base2.tzname() is None)
2835
2836 # Ensure we can add one.
2837 base3 = base2.replace(tzinfo=z100)
2838 self.assertEqual(base, base3)
2839 self.failUnless(base.tzinfo is base3.tzinfo)
2840
2841 # Out of bounds.
2842 base = cls(2000, 2, 29)
2843 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002844
Tim Peters80475bb2002-12-25 07:40:55 +00002845 def test_more_astimezone(self):
2846 # The inherited test_astimezone covered some trivial and error cases.
2847 fnone = FixedOffset(None, "None")
2848 f44m = FixedOffset(44, "44")
2849 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2850
Tim Peters10cadce2003-01-23 19:58:02 +00002851 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002852 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002853 # Replacing with degenerate tzinfo raises an exception.
2854 self.assertRaises(ValueError, dt.astimezone, fnone)
2855 # Ditto with None tz.
2856 self.assertRaises(TypeError, dt.astimezone, None)
2857 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002858 x = dt.astimezone(dt.tzinfo)
2859 self.failUnless(x.tzinfo is f44m)
2860 self.assertEqual(x.date(), dt.date())
2861 self.assertEqual(x.time(), dt.time())
2862
2863 # Replacing with different tzinfo does adjust.
2864 got = dt.astimezone(fm5h)
2865 self.failUnless(got.tzinfo is fm5h)
2866 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2867 expected = dt - dt.utcoffset() # in effect, convert to UTC
2868 expected += fm5h.utcoffset(dt) # and from there to local time
2869 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2870 self.assertEqual(got.date(), expected.date())
2871 self.assertEqual(got.time(), expected.time())
2872 self.assertEqual(got.timetz(), expected.timetz())
2873 self.failUnless(got.tzinfo is expected.tzinfo)
2874 self.assertEqual(got, expected)
2875
Tim Peters4c0db782002-12-26 05:01:19 +00002876 def test_aware_subtract(self):
2877 cls = self.theclass
2878
Tim Peters60c76e42002-12-27 00:41:11 +00002879 # Ensure that utcoffset() is ignored when the operands have the
2880 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002881 class OperandDependentOffset(tzinfo):
2882 def utcoffset(self, t):
2883 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002884 # d0 and d1 equal after adjustment
2885 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002886 else:
Tim Peters397301e2003-01-02 21:28:08 +00002887 # d2 off in the weeds
2888 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002889
2890 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2891 d0 = base.replace(minute=3)
2892 d1 = base.replace(minute=9)
2893 d2 = base.replace(minute=11)
2894 for x in d0, d1, d2:
2895 for y in d0, d1, d2:
2896 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002897 expected = timedelta(minutes=x.minute - y.minute)
2898 self.assertEqual(got, expected)
2899
2900 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2901 # ignored.
2902 base = cls(8, 9, 10, 11, 12, 13, 14)
2903 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2904 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2905 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2906 for x in d0, d1, d2:
2907 for y in d0, d1, d2:
2908 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002909 if (x is d0 or x is d1) and (y is d0 or y is d1):
2910 expected = timedelta(0)
2911 elif x is y is d2:
2912 expected = timedelta(0)
2913 elif x is d2:
2914 expected = timedelta(minutes=(11-59)-0)
2915 else:
2916 assert y is d2
2917 expected = timedelta(minutes=0-(11-59))
2918 self.assertEqual(got, expected)
2919
Tim Peters60c76e42002-12-27 00:41:11 +00002920 def test_mixed_compare(self):
2921 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002922 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002923 self.assertEqual(t1, t2)
2924 t2 = t2.replace(tzinfo=None)
2925 self.assertEqual(t1, t2)
2926 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2927 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002928 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2929 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002930
Tim Peters0bf60bd2003-01-08 20:40:01 +00002931 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002932 class Varies(tzinfo):
2933 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002934 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002935 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002936 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002937 return self.offset
2938
2939 v = Varies()
2940 t1 = t2.replace(tzinfo=v)
2941 t2 = t2.replace(tzinfo=v)
2942 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2943 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2944 self.assertEqual(t1, t2)
2945
2946 # But if they're not identical, it isn't ignored.
2947 t2 = t2.replace(tzinfo=Varies())
2948 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002949
Tim Petersa98924a2003-05-17 05:55:19 +00002950 def test_subclass_datetimetz(self):
2951
2952 class C(self.theclass):
2953 theAnswer = 42
2954
2955 def __new__(cls, *args, **kws):
2956 temp = kws.copy()
2957 extra = temp.pop('extra')
2958 result = self.theclass.__new__(cls, *args, **temp)
2959 result.extra = extra
2960 return result
2961
2962 def newmeth(self, start):
2963 return start + self.hour + self.year
2964
2965 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2966
2967 dt1 = self.theclass(*args)
2968 dt2 = C(*args, **{'extra': 7})
2969
2970 self.assertEqual(dt2.__class__, C)
2971 self.assertEqual(dt2.theAnswer, 42)
2972 self.assertEqual(dt2.extra, 7)
2973 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2974 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2975
Tim Peters621818b2002-12-29 23:44:49 +00002976# Pain to set up DST-aware tzinfo classes.
2977
2978def first_sunday_on_or_after(dt):
2979 days_to_go = 6 - dt.weekday()
2980 if days_to_go:
2981 dt += timedelta(days_to_go)
2982 return dt
2983
2984ZERO = timedelta(0)
2985HOUR = timedelta(hours=1)
2986DAY = timedelta(days=1)
2987# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2988DSTSTART = datetime(1, 4, 1, 2)
2989# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002990# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2991# being standard time on that day, there is no spelling in local time of
2992# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2993DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002994
2995class USTimeZone(tzinfo):
2996
2997 def __init__(self, hours, reprname, stdname, dstname):
2998 self.stdoffset = timedelta(hours=hours)
2999 self.reprname = reprname
3000 self.stdname = stdname
3001 self.dstname = dstname
3002
3003 def __repr__(self):
3004 return self.reprname
3005
3006 def tzname(self, dt):
3007 if self.dst(dt):
3008 return self.dstname
3009 else:
3010 return self.stdname
3011
3012 def utcoffset(self, dt):
3013 return self.stdoffset + self.dst(dt)
3014
3015 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003016 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003017 # An exception instead may be sensible here, in one or more of
3018 # the cases.
3019 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003020 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003021
3022 # Find first Sunday in April.
3023 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3024 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3025
3026 # Find last Sunday in October.
3027 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3028 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3029
Tim Peters621818b2002-12-29 23:44:49 +00003030 # Can't compare naive to aware objects, so strip the timezone from
3031 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003032 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003033 return HOUR
3034 else:
3035 return ZERO
3036
Tim Peters521fc152002-12-31 17:36:56 +00003037Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3038Central = USTimeZone(-6, "Central", "CST", "CDT")
3039Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3040Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003041utc_real = FixedOffset(0, "UTC", 0)
3042# For better test coverage, we want another flavor of UTC that's west of
3043# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003044utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003045
3046class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003047 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003048 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003049 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003050
Tim Peters0bf60bd2003-01-08 20:40:01 +00003051 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003052
Tim Peters521fc152002-12-31 17:36:56 +00003053 # Check a time that's inside DST.
3054 def checkinside(self, dt, tz, utc, dston, dstoff):
3055 self.assertEqual(dt.dst(), HOUR)
3056
3057 # Conversion to our own timezone is always an identity.
3058 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003059
3060 asutc = dt.astimezone(utc)
3061 there_and_back = asutc.astimezone(tz)
3062
3063 # Conversion to UTC and back isn't always an identity here,
3064 # because there are redundant spellings (in local time) of
3065 # UTC time when DST begins: the clock jumps from 1:59:59
3066 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3067 # make sense then. The classes above treat 2:MM:SS as
3068 # daylight time then (it's "after 2am"), really an alias
3069 # for 1:MM:SS standard time. The latter form is what
3070 # conversion back from UTC produces.
3071 if dt.date() == dston.date() and dt.hour == 2:
3072 # We're in the redundant hour, and coming back from
3073 # UTC gives the 1:MM:SS standard-time spelling.
3074 self.assertEqual(there_and_back + HOUR, dt)
3075 # Although during was considered to be in daylight
3076 # time, there_and_back is not.
3077 self.assertEqual(there_and_back.dst(), ZERO)
3078 # They're the same times in UTC.
3079 self.assertEqual(there_and_back.astimezone(utc),
3080 dt.astimezone(utc))
3081 else:
3082 # We're not in the redundant hour.
3083 self.assertEqual(dt, there_and_back)
3084
Tim Peters327098a2003-01-20 22:54:38 +00003085 # Because we have a redundant spelling when DST begins, there is
3086 # (unforunately) an hour when DST ends that can't be spelled at all in
3087 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3088 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3089 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3090 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3091 # expressed in local time. Nevertheless, we want conversion back
3092 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003093 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003094 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003095 if dt.date() == dstoff.date() and dt.hour == 0:
3096 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003097 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003098 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3099 nexthour_utc += HOUR
3100 nexthour_tz = nexthour_utc.astimezone(tz)
3101 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003102 else:
Tim Peters327098a2003-01-20 22:54:38 +00003103 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003104
3105 # Check a time that's outside DST.
3106 def checkoutside(self, dt, tz, utc):
3107 self.assertEqual(dt.dst(), ZERO)
3108
3109 # Conversion to our own timezone is always an identity.
3110 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003111
3112 # Converting to UTC and back is an identity too.
3113 asutc = dt.astimezone(utc)
3114 there_and_back = asutc.astimezone(tz)
3115 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003116
Tim Peters1024bf82002-12-30 17:09:40 +00003117 def convert_between_tz_and_utc(self, tz, utc):
3118 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003119 # Because 1:MM on the day DST ends is taken as being standard time,
3120 # there is no spelling in tz for the last hour of daylight time.
3121 # For purposes of the test, the last hour of DST is 0:MM, which is
3122 # taken as being daylight time (and 1:MM is taken as being standard
3123 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003124 dstoff = self.dstoff.replace(tzinfo=tz)
3125 for delta in (timedelta(weeks=13),
3126 DAY,
3127 HOUR,
3128 timedelta(minutes=1),
3129 timedelta(microseconds=1)):
3130
Tim Peters521fc152002-12-31 17:36:56 +00003131 self.checkinside(dston, tz, utc, dston, dstoff)
3132 for during in dston + delta, dstoff - delta:
3133 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003134
Tim Peters521fc152002-12-31 17:36:56 +00003135 self.checkoutside(dstoff, tz, utc)
3136 for outside in dston - delta, dstoff + delta:
3137 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003138
Tim Peters621818b2002-12-29 23:44:49 +00003139 def test_easy(self):
3140 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003141 self.convert_between_tz_and_utc(Eastern, utc_real)
3142 self.convert_between_tz_and_utc(Pacific, utc_real)
3143 self.convert_between_tz_and_utc(Eastern, utc_fake)
3144 self.convert_between_tz_and_utc(Pacific, utc_fake)
3145 # The next is really dancing near the edge. It works because
3146 # Pacific and Eastern are far enough apart that their "problem
3147 # hours" don't overlap.
3148 self.convert_between_tz_and_utc(Eastern, Pacific)
3149 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003150 # OTOH, these fail! Don't enable them. The difficulty is that
3151 # the edge case tests assume that every hour is representable in
3152 # the "utc" class. This is always true for a fixed-offset tzinfo
3153 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3154 # For these adjacent DST-aware time zones, the range of time offsets
3155 # tested ends up creating hours in the one that aren't representable
3156 # in the other. For the same reason, we would see failures in the
3157 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3158 # offset deltas in convert_between_tz_and_utc().
3159 #
3160 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3161 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003162
Tim Petersf3615152003-01-01 21:51:37 +00003163 def test_tricky(self):
3164 # 22:00 on day before daylight starts.
3165 fourback = self.dston - timedelta(hours=4)
3166 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003167 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003168 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3169 # 2", we should get the 3 spelling.
3170 # If we plug 22:00 the day before into Eastern, it "looks like std
3171 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3172 # to 22:00 lands on 2:00, which makes no sense in local time (the
3173 # local clock jumps from 1 to 3). The point here is to make sure we
3174 # get the 3 spelling.
3175 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003176 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003177 self.assertEqual(expected, got)
3178
3179 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3180 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003181 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003182 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3183 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3184 # spelling.
3185 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003186 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003187 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003188
Tim Petersadf64202003-01-04 06:03:15 +00003189 # Now on the day DST ends, we want "repeat an hour" behavior.
3190 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3191 # EST 23:MM 0:MM 1:MM 2:MM
3192 # EDT 0:MM 1:MM 2:MM 3:MM
3193 # wall 0:MM 1:MM 1:MM 2:MM against these
3194 for utc in utc_real, utc_fake:
3195 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003196 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003197 # Convert that to UTC.
3198 first_std_hour -= tz.utcoffset(None)
3199 # Adjust for possibly fake UTC.
3200 asutc = first_std_hour + utc.utcoffset(None)
3201 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3202 # tz=Eastern.
3203 asutcbase = asutc.replace(tzinfo=utc)
3204 for tzhour in (0, 1, 1, 2):
3205 expectedbase = self.dstoff.replace(hour=tzhour)
3206 for minute in 0, 30, 59:
3207 expected = expectedbase.replace(minute=minute)
3208 asutc = asutcbase.replace(minute=minute)
3209 astz = asutc.astimezone(tz)
3210 self.assertEqual(astz.replace(tzinfo=None), expected)
3211 asutcbase += HOUR
3212
3213
Tim Peters710fb152003-01-02 19:35:54 +00003214 def test_bogus_dst(self):
3215 class ok(tzinfo):
3216 def utcoffset(self, dt): return HOUR
3217 def dst(self, dt): return HOUR
3218
3219 now = self.theclass.now().replace(tzinfo=utc_real)
3220 # Doesn't blow up.
3221 now.astimezone(ok())
3222
3223 # Does blow up.
3224 class notok(ok):
3225 def dst(self, dt): return None
3226 self.assertRaises(ValueError, now.astimezone, notok())
3227
Tim Peters52dcce22003-01-23 16:36:11 +00003228 def test_fromutc(self):
3229 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3230 now = datetime.utcnow().replace(tzinfo=utc_real)
3231 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3232 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3233 enow = Eastern.fromutc(now) # doesn't blow up
3234 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3235 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3236 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3237
3238 # Always converts UTC to standard time.
3239 class FauxUSTimeZone(USTimeZone):
3240 def fromutc(self, dt):
3241 return dt + self.stdoffset
3242 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3243
3244 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3245 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3246 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3247
3248 # Check around DST start.
3249 start = self.dston.replace(hour=4, tzinfo=Eastern)
3250 fstart = start.replace(tzinfo=FEastern)
3251 for wall in 23, 0, 1, 3, 4, 5:
3252 expected = start.replace(hour=wall)
3253 if wall == 23:
3254 expected -= timedelta(days=1)
3255 got = Eastern.fromutc(start)
3256 self.assertEqual(expected, got)
3257
3258 expected = fstart + FEastern.stdoffset
3259 got = FEastern.fromutc(fstart)
3260 self.assertEqual(expected, got)
3261
3262 # Ensure astimezone() calls fromutc() too.
3263 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3264 self.assertEqual(expected, got)
3265
3266 start += HOUR
3267 fstart += HOUR
3268
3269 # Check around DST end.
3270 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3271 fstart = start.replace(tzinfo=FEastern)
3272 for wall in 0, 1, 1, 2, 3, 4:
3273 expected = start.replace(hour=wall)
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
Tim Peters710fb152003-01-02 19:35:54 +00003288
Tim Peters528ca532004-09-16 01:30:50 +00003289#############################################################################
3290# oddballs
3291
3292class Oddballs(unittest.TestCase):
3293
3294 def test_bug_1028306(self):
3295 # Trying to compare a date to a datetime should act like a mixed-
3296 # type comparison, despite that datetime is a subclass of date.
3297 as_date = date.today()
3298 as_datetime = datetime.combine(as_date, time())
3299 self.assert_(as_date != as_datetime)
3300 self.assert_(as_datetime != as_date)
3301 self.assert_(not as_date == as_datetime)
3302 self.assert_(not as_datetime == as_date)
3303 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3304 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3305 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3306 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3307 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3308 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3309 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3310 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3311
3312 # Neverthelss, comparison should work with the base-class (date)
3313 # projection if use of a date method is forced.
3314 self.assert_(as_date.__eq__(as_datetime))
3315 different_day = (as_date.day + 1) % 20 + 1
3316 self.assert_(not as_date.__eq__(as_datetime.replace(day=
3317 different_day)))
3318
3319 # And date should compare with other subclasses of date. If a
3320 # subclass wants to stop this, it's up to the subclass to do so.
3321 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3322 self.assertEqual(as_date, date_sc)
3323 self.assertEqual(date_sc, as_date)
3324
3325 # Ditto for datetimes.
3326 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3327 as_date.day, 0, 0, 0)
3328 self.assertEqual(as_datetime, datetime_sc)
3329 self.assertEqual(datetime_sc, as_datetime)
3330
Tim Peters2a799bf2002-12-16 20:18:38 +00003331def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003332 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003333
3334if __name__ == "__main__":
3335 test_main()