blob: 765bdafad5cbe45c73418cd661585dd627062517 [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
6import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00008import unittest
Guido van Rossumbf12cdb2006-08-17 20:24:18 +00009try:
10 import cPickle
11except ImportError:
12 cPickle = None
Tim Peters2a799bf2002-12-16 20:18:38 +000013
14from test import test_support
15
16from datetime import MINYEAR, MAXYEAR
17from datetime import timedelta
18from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000019from datetime import time
20from datetime import date, datetime
21
Tim Peters35ad6412003-02-05 04:08:07 +000022pickle_choices = [(pickler, unpickler, proto)
23 for pickler in pickle, cPickle
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000024 if pickler is not None
Tim Peters35ad6412003-02-05 04:08:07 +000025 for unpickler in pickle, cPickle
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000026 if unpickler is not None
Tim Peters35ad6412003-02-05 04:08:07 +000027 for proto in range(3)]
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000028if cPickle is None:
29 assert len(pickle_choices) == 3
30else:
31 assert len(pickle_choices) == 2*2*3
Guido van Rossum177e41a2003-01-30 22:06:23 +000032
Tim Peters68124bb2003-02-08 03:46:31 +000033# An arbitrary collection of objects of non-datetime types, for testing
34# mixed-type comparisons.
35OTHERSTUFF = (10, 10L, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000036
Tim Peters2a799bf2002-12-16 20:18:38 +000037
38#############################################################################
39# module tests
40
41class TestModule(unittest.TestCase):
42
43 def test_constants(self):
44 import datetime
45 self.assertEqual(datetime.MINYEAR, 1)
46 self.assertEqual(datetime.MAXYEAR, 9999)
47
48#############################################################################
49# tzinfo tests
50
51class FixedOffset(tzinfo):
52 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000053 if isinstance(offset, int):
54 offset = timedelta(minutes=offset)
55 if isinstance(dstoffset, int):
56 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000057 self.__offset = offset
58 self.__name = name
59 self.__dstoffset = dstoffset
60 def __repr__(self):
61 return self.__name.lower()
62 def utcoffset(self, dt):
63 return self.__offset
64 def tzname(self, dt):
65 return self.__name
66 def dst(self, dt):
67 return self.__dstoffset
68
Tim Petersfb8472c2002-12-21 05:04:42 +000069class PicklableFixedOffset(FixedOffset):
70 def __init__(self, offset=None, name=None, dstoffset=None):
71 FixedOffset.__init__(self, offset, name, dstoffset)
72
Tim Peters2a799bf2002-12-16 20:18:38 +000073class TestTZInfo(unittest.TestCase):
74
75 def test_non_abstractness(self):
76 # In order to allow subclasses to get pickled, the C implementation
77 # wasn't able to get away with having __init__ raise
78 # NotImplementedError.
79 useless = tzinfo()
80 dt = datetime.max
81 self.assertRaises(NotImplementedError, useless.tzname, dt)
82 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
83 self.assertRaises(NotImplementedError, useless.dst, dt)
84
85 def test_subclass_must_override(self):
86 class NotEnough(tzinfo):
87 def __init__(self, offset, name):
88 self.__offset = offset
89 self.__name = name
90 self.failUnless(issubclass(NotEnough, tzinfo))
91 ne = NotEnough(3, "NotByALongShot")
92 self.failUnless(isinstance(ne, tzinfo))
93
94 dt = datetime.now()
95 self.assertRaises(NotImplementedError, ne.tzname, dt)
96 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
97 self.assertRaises(NotImplementedError, ne.dst, dt)
98
99 def test_normal(self):
100 fo = FixedOffset(3, "Three")
101 self.failUnless(isinstance(fo, tzinfo))
102 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000103 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000104 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000105 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000106
107 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000108 # There's no point to pickling tzinfo objects on their own (they
109 # carry no data), but they need to be picklable anyway else
110 # concrete subclasses can't be pickled.
111 orig = tzinfo.__new__(tzinfo)
112 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000113 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000114 green = pickler.dumps(orig, proto)
115 derived = unpickler.loads(green)
116 self.failUnless(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000117
118 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000119 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000120 offset = timedelta(minutes=-300)
121 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000122 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000123 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000124 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000125 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000126 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000127 green = pickler.dumps(orig, proto)
128 derived = unpickler.loads(green)
129 self.failUnless(isinstance(derived, tzinfo))
130 self.failUnless(type(derived) is PicklableFixedOffset)
131 self.assertEqual(derived.utcoffset(None), offset)
132 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000133
134#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000135# Base clase for testing a particular aspect of timedelta, time, date and
136# datetime comparisons.
137
138class HarmlessMixedComparison(unittest.TestCase):
139 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
140
141 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
142 # legit constructor.
143
144 def test_harmless_mixed_comparison(self):
145 me = self.theclass(1, 1, 1)
146
147 self.failIf(me == ())
148 self.failUnless(me != ())
149 self.failIf(() == me)
150 self.failUnless(() != me)
151
152 self.failUnless(me in [1, 20L, [], me])
153 self.failIf(me not in [1, 20L, [], me])
154
155 self.failUnless([] in [me, 1, 20L, []])
156 self.failIf([] not in [me, 1, 20L, []])
157
158 def test_harmful_mixed_comparison(self):
159 me = self.theclass(1, 1, 1)
160
161 self.assertRaises(TypeError, lambda: me < ())
162 self.assertRaises(TypeError, lambda: me <= ())
163 self.assertRaises(TypeError, lambda: me > ())
164 self.assertRaises(TypeError, lambda: me >= ())
165
166 self.assertRaises(TypeError, lambda: () < me)
167 self.assertRaises(TypeError, lambda: () <= me)
168 self.assertRaises(TypeError, lambda: () > me)
169 self.assertRaises(TypeError, lambda: () >= me)
170
171 self.assertRaises(TypeError, cmp, (), me)
172 self.assertRaises(TypeError, cmp, me, ())
173
174#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000175# timedelta tests
176
Tim Peters07534a62003-02-07 22:50:28 +0000177class TestTimeDelta(HarmlessMixedComparison):
178
179 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000180
181 def test_constructor(self):
182 eq = self.assertEqual
183 td = timedelta
184
185 # Check keyword args to constructor
186 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
187 milliseconds=0, microseconds=0))
188 eq(td(1), td(days=1))
189 eq(td(0, 1), td(seconds=1))
190 eq(td(0, 0, 1), td(microseconds=1))
191 eq(td(weeks=1), td(days=7))
192 eq(td(days=1), td(hours=24))
193 eq(td(hours=1), td(minutes=60))
194 eq(td(minutes=1), td(seconds=60))
195 eq(td(seconds=1), td(milliseconds=1000))
196 eq(td(milliseconds=1), td(microseconds=1000))
197
198 # Check float args to constructor
199 eq(td(weeks=1.0/7), td(days=1))
200 eq(td(days=1.0/24), td(hours=1))
201 eq(td(hours=1.0/60), td(minutes=1))
202 eq(td(minutes=1.0/60), td(seconds=1))
203 eq(td(seconds=0.001), td(milliseconds=1))
204 eq(td(milliseconds=0.001), td(microseconds=1))
205
206 def test_computations(self):
207 eq = self.assertEqual
208 td = timedelta
209
210 a = td(7) # One week
211 b = td(0, 60) # One minute
212 c = td(0, 0, 1000) # One millisecond
213 eq(a+b+c, td(7, 60, 1000))
214 eq(a-b, td(6, 24*3600 - 60))
215 eq(-a, td(-7))
216 eq(+a, td(7))
217 eq(-b, td(-1, 24*3600 - 60))
218 eq(-c, td(-1, 24*3600 - 1, 999000))
219 eq(abs(a), a)
220 eq(abs(-a), a)
221 eq(td(6, 24*3600), a)
222 eq(td(0, 0, 60*1000000), b)
223 eq(a*10, td(70))
224 eq(a*10, 10*a)
225 eq(a*10L, 10*a)
226 eq(b*10, td(0, 600))
227 eq(10*b, td(0, 600))
228 eq(b*10L, td(0, 600))
229 eq(c*10, td(0, 0, 10000))
230 eq(10*c, td(0, 0, 10000))
231 eq(c*10L, td(0, 0, 10000))
232 eq(a*-1, -a)
233 eq(b*-2, -b-b)
234 eq(c*-2, -c+-c)
235 eq(b*(60*24), (b*60)*24)
236 eq(b*(60*24), (60*b)*24)
237 eq(c*1000, td(0, 1))
238 eq(1000*c, td(0, 1))
239 eq(a//7, td(1))
240 eq(b//10, td(0, 6))
241 eq(c//1000, td(0, 0, 1))
242 eq(a//10, td(0, 7*24*360))
243 eq(a//3600000, td(0, 0, 7*24*1000))
244
245 def test_disallowed_computations(self):
246 a = timedelta(42)
247
248 # Add/sub ints, longs, floats should be illegal
249 for i in 1, 1L, 1.0:
250 self.assertRaises(TypeError, lambda: a+i)
251 self.assertRaises(TypeError, lambda: a-i)
252 self.assertRaises(TypeError, lambda: i+a)
253 self.assertRaises(TypeError, lambda: i-a)
254
255 # Mul/div by float isn't supported.
256 x = 2.3
257 self.assertRaises(TypeError, lambda: a*x)
258 self.assertRaises(TypeError, lambda: x*a)
259 self.assertRaises(TypeError, lambda: a/x)
260 self.assertRaises(TypeError, lambda: x/a)
261 self.assertRaises(TypeError, lambda: a // x)
262 self.assertRaises(TypeError, lambda: x // a)
263
264 # Divison of int by timedelta doesn't make sense.
265 # Division by zero doesn't make sense.
266 for zero in 0, 0L:
267 self.assertRaises(TypeError, lambda: zero // a)
268 self.assertRaises(ZeroDivisionError, lambda: a // zero)
269
270 def test_basic_attributes(self):
271 days, seconds, us = 1, 7, 31
272 td = timedelta(days, seconds, us)
273 self.assertEqual(td.days, days)
274 self.assertEqual(td.seconds, seconds)
275 self.assertEqual(td.microseconds, us)
276
277 def test_carries(self):
278 t1 = timedelta(days=100,
279 weeks=-7,
280 hours=-24*(100-49),
281 minutes=-3,
282 seconds=12,
283 microseconds=(3*60 - 12) * 1e6 + 1)
284 t2 = timedelta(microseconds=1)
285 self.assertEqual(t1, t2)
286
287 def test_hash_equality(self):
288 t1 = timedelta(days=100,
289 weeks=-7,
290 hours=-24*(100-49),
291 minutes=-3,
292 seconds=12,
293 microseconds=(3*60 - 12) * 1000000)
294 t2 = timedelta()
295 self.assertEqual(hash(t1), hash(t2))
296
297 t1 += timedelta(weeks=7)
298 t2 += timedelta(days=7*7)
299 self.assertEqual(t1, t2)
300 self.assertEqual(hash(t1), hash(t2))
301
302 d = {t1: 1}
303 d[t2] = 2
304 self.assertEqual(len(d), 1)
305 self.assertEqual(d[t1], 2)
306
307 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000308 args = 12, 34, 56
309 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000310 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000311 green = pickler.dumps(orig, proto)
312 derived = unpickler.loads(green)
313 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000314
315 def test_compare(self):
316 t1 = timedelta(2, 3, 4)
317 t2 = timedelta(2, 3, 4)
318 self.failUnless(t1 == t2)
319 self.failUnless(t1 <= t2)
320 self.failUnless(t1 >= t2)
321 self.failUnless(not t1 != t2)
322 self.failUnless(not t1 < t2)
323 self.failUnless(not t1 > t2)
324 self.assertEqual(cmp(t1, t2), 0)
325 self.assertEqual(cmp(t2, t1), 0)
326
327 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
328 t2 = timedelta(*args) # this is larger than t1
329 self.failUnless(t1 < t2)
330 self.failUnless(t2 > t1)
331 self.failUnless(t1 <= t2)
332 self.failUnless(t2 >= t1)
333 self.failUnless(t1 != t2)
334 self.failUnless(t2 != t1)
335 self.failUnless(not t1 == t2)
336 self.failUnless(not t2 == t1)
337 self.failUnless(not t1 > t2)
338 self.failUnless(not t2 < t1)
339 self.failUnless(not t1 >= t2)
340 self.failUnless(not t2 <= t1)
341 self.assertEqual(cmp(t1, t2), -1)
342 self.assertEqual(cmp(t2, t1), 1)
343
Tim Peters68124bb2003-02-08 03:46:31 +0000344 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000345 self.assertEqual(t1 == badarg, False)
346 self.assertEqual(t1 != badarg, True)
347 self.assertEqual(badarg == t1, False)
348 self.assertEqual(badarg != t1, True)
349
Tim Peters2a799bf2002-12-16 20:18:38 +0000350 self.assertRaises(TypeError, lambda: t1 <= badarg)
351 self.assertRaises(TypeError, lambda: t1 < badarg)
352 self.assertRaises(TypeError, lambda: t1 > badarg)
353 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000354 self.assertRaises(TypeError, lambda: badarg <= t1)
355 self.assertRaises(TypeError, lambda: badarg < t1)
356 self.assertRaises(TypeError, lambda: badarg > t1)
357 self.assertRaises(TypeError, lambda: badarg >= t1)
358
359 def test_str(self):
360 td = timedelta
361 eq = self.assertEqual
362
363 eq(str(td(1)), "1 day, 0:00:00")
364 eq(str(td(-1)), "-1 day, 0:00:00")
365 eq(str(td(2)), "2 days, 0:00:00")
366 eq(str(td(-2)), "-2 days, 0:00:00")
367
368 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
369 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
370 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
371 "-210 days, 23:12:34")
372
373 eq(str(td(milliseconds=1)), "0:00:00.001000")
374 eq(str(td(microseconds=3)), "0:00:00.000003")
375
376 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
377 microseconds=999999)),
378 "999999999 days, 23:59:59.999999")
379
380 def test_roundtrip(self):
381 for td in (timedelta(days=999999999, hours=23, minutes=59,
382 seconds=59, microseconds=999999),
383 timedelta(days=-999999999),
384 timedelta(days=1, seconds=2, microseconds=3)):
385
386 # Verify td -> string -> td identity.
387 s = repr(td)
388 self.failUnless(s.startswith('datetime.'))
389 s = s[9:]
390 td2 = eval(s)
391 self.assertEqual(td, td2)
392
393 # Verify identity via reconstructing from pieces.
394 td2 = timedelta(td.days, td.seconds, td.microseconds)
395 self.assertEqual(td, td2)
396
397 def test_resolution_info(self):
398 self.assert_(isinstance(timedelta.min, timedelta))
399 self.assert_(isinstance(timedelta.max, timedelta))
400 self.assert_(isinstance(timedelta.resolution, timedelta))
401 self.assert_(timedelta.max > timedelta.min)
402 self.assertEqual(timedelta.min, timedelta(-999999999))
403 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
404 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
405
406 def test_overflow(self):
407 tiny = timedelta.resolution
408
409 td = timedelta.min + tiny
410 td -= tiny # no problem
411 self.assertRaises(OverflowError, td.__sub__, tiny)
412 self.assertRaises(OverflowError, td.__add__, -tiny)
413
414 td = timedelta.max - tiny
415 td += tiny # no problem
416 self.assertRaises(OverflowError, td.__add__, tiny)
417 self.assertRaises(OverflowError, td.__sub__, -tiny)
418
419 self.assertRaises(OverflowError, lambda: -timedelta.max)
420
421 def test_microsecond_rounding(self):
422 td = timedelta
423 eq = self.assertEqual
424
425 # Single-field rounding.
426 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
427 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
428 eq(td(milliseconds=0.6/1000), td(microseconds=1))
429 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
430
431 # Rounding due to contributions from more than one field.
432 us_per_hour = 3600e6
433 us_per_day = us_per_hour * 24
434 eq(td(days=.4/us_per_day), td(0))
435 eq(td(hours=.2/us_per_hour), td(0))
436 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
437
438 eq(td(days=-.4/us_per_day), td(0))
439 eq(td(hours=-.2/us_per_hour), td(0))
440 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
441
442 def test_massive_normalization(self):
443 td = timedelta(microseconds=-1)
444 self.assertEqual((td.days, td.seconds, td.microseconds),
445 (-1, 24*3600-1, 999999))
446
447 def test_bool(self):
448 self.failUnless(timedelta(1))
449 self.failUnless(timedelta(0, 1))
450 self.failUnless(timedelta(0, 0, 1))
451 self.failUnless(timedelta(microseconds=1))
452 self.failUnless(not timedelta(0))
453
Tim Petersb0c854d2003-05-17 15:57:00 +0000454 def test_subclass_timedelta(self):
455
456 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000457 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000458 def from_td(td):
459 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000460
461 def as_hours(self):
462 sum = (self.days * 24 +
463 self.seconds / 3600.0 +
464 self.microseconds / 3600e6)
465 return round(sum)
466
467 t1 = T(days=1)
468 self.assert_(type(t1) is T)
469 self.assertEqual(t1.as_hours(), 24)
470
471 t2 = T(days=-1, seconds=-3600)
472 self.assert_(type(t2) is T)
473 self.assertEqual(t2.as_hours(), -25)
474
475 t3 = t1 + t2
476 self.assert_(type(t3) is timedelta)
477 t4 = T.from_td(t3)
478 self.assert_(type(t4) is T)
479 self.assertEqual(t3.days, t4.days)
480 self.assertEqual(t3.seconds, t4.seconds)
481 self.assertEqual(t3.microseconds, t4.microseconds)
482 self.assertEqual(str(t3), str(t4))
483 self.assertEqual(t4.as_hours(), -1)
484
Tim Peters2a799bf2002-12-16 20:18:38 +0000485#############################################################################
486# date tests
487
488class TestDateOnly(unittest.TestCase):
489 # Tests here won't pass if also run on datetime objects, so don't
490 # subclass this to test datetimes too.
491
492 def test_delta_non_days_ignored(self):
493 dt = date(2000, 1, 2)
494 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
495 microseconds=5)
496 days = timedelta(delta.days)
497 self.assertEqual(days, timedelta(1))
498
499 dt2 = dt + delta
500 self.assertEqual(dt2, dt + days)
501
502 dt2 = delta + dt
503 self.assertEqual(dt2, dt + days)
504
505 dt2 = dt - delta
506 self.assertEqual(dt2, dt - days)
507
508 delta = -delta
509 days = timedelta(delta.days)
510 self.assertEqual(days, timedelta(-2))
511
512 dt2 = dt + delta
513 self.assertEqual(dt2, dt + days)
514
515 dt2 = delta + dt
516 self.assertEqual(dt2, dt + days)
517
518 dt2 = dt - delta
519 self.assertEqual(dt2, dt - days)
520
Tim Peters604c0132004-06-07 23:04:33 +0000521class SubclassDate(date):
522 sub_var = 1
523
Tim Peters07534a62003-02-07 22:50:28 +0000524class TestDate(HarmlessMixedComparison):
Tim Peters2a799bf2002-12-16 20:18:38 +0000525 # Tests here should pass for both dates and datetimes, except for a
526 # few tests that TestDateTime overrides.
527
528 theclass = date
529
530 def test_basic_attributes(self):
531 dt = self.theclass(2002, 3, 1)
532 self.assertEqual(dt.year, 2002)
533 self.assertEqual(dt.month, 3)
534 self.assertEqual(dt.day, 1)
535
536 def test_roundtrip(self):
537 for dt in (self.theclass(1, 2, 3),
538 self.theclass.today()):
539 # Verify dt -> string -> date identity.
540 s = repr(dt)
541 self.failUnless(s.startswith('datetime.'))
542 s = s[9:]
543 dt2 = eval(s)
544 self.assertEqual(dt, dt2)
545
546 # Verify identity via reconstructing from pieces.
547 dt2 = self.theclass(dt.year, dt.month, dt.day)
548 self.assertEqual(dt, dt2)
549
550 def test_ordinal_conversions(self):
551 # Check some fixed values.
552 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
553 (1, 12, 31, 365),
554 (2, 1, 1, 366),
555 # first example from "Calendrical Calculations"
556 (1945, 11, 12, 710347)]:
557 d = self.theclass(y, m, d)
558 self.assertEqual(n, d.toordinal())
559 fromord = self.theclass.fromordinal(n)
560 self.assertEqual(d, fromord)
561 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000562 # if we're checking something fancier than a date, verify
563 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000564 self.assertEqual(fromord.hour, 0)
565 self.assertEqual(fromord.minute, 0)
566 self.assertEqual(fromord.second, 0)
567 self.assertEqual(fromord.microsecond, 0)
568
Tim Peters0bf60bd2003-01-08 20:40:01 +0000569 # Check first and last days of year spottily across the whole
570 # range of years supported.
571 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000572 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
573 d = self.theclass(year, 1, 1)
574 n = d.toordinal()
575 d2 = self.theclass.fromordinal(n)
576 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000577 # Verify that moving back a day gets to the end of year-1.
578 if year > 1:
579 d = self.theclass.fromordinal(n-1)
580 d2 = self.theclass(year-1, 12, 31)
581 self.assertEqual(d, d2)
582 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000583
584 # Test every day in a leap-year and a non-leap year.
585 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
586 for year, isleap in (2000, True), (2002, False):
587 n = self.theclass(year, 1, 1).toordinal()
588 for month, maxday in zip(range(1, 13), dim):
589 if month == 2 and isleap:
590 maxday += 1
591 for day in range(1, maxday+1):
592 d = self.theclass(year, month, day)
593 self.assertEqual(d.toordinal(), n)
594 self.assertEqual(d, self.theclass.fromordinal(n))
595 n += 1
596
597 def test_extreme_ordinals(self):
598 a = self.theclass.min
599 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
600 aord = a.toordinal()
601 b = a.fromordinal(aord)
602 self.assertEqual(a, b)
603
604 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
605
606 b = a + timedelta(days=1)
607 self.assertEqual(b.toordinal(), aord + 1)
608 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
609
610 a = self.theclass.max
611 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
612 aord = a.toordinal()
613 b = a.fromordinal(aord)
614 self.assertEqual(a, b)
615
616 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
617
618 b = a - timedelta(days=1)
619 self.assertEqual(b.toordinal(), aord - 1)
620 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
621
622 def test_bad_constructor_arguments(self):
623 # bad years
624 self.theclass(MINYEAR, 1, 1) # no exception
625 self.theclass(MAXYEAR, 1, 1) # no exception
626 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
627 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
628 # bad months
629 self.theclass(2000, 1, 1) # no exception
630 self.theclass(2000, 12, 1) # no exception
631 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
632 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
633 # bad days
634 self.theclass(2000, 2, 29) # no exception
635 self.theclass(2004, 2, 29) # no exception
636 self.theclass(2400, 2, 29) # no exception
637 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
638 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
639 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
640 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
641 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
642 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
643
644 def test_hash_equality(self):
645 d = self.theclass(2000, 12, 31)
646 # same thing
647 e = self.theclass(2000, 12, 31)
648 self.assertEqual(d, e)
649 self.assertEqual(hash(d), hash(e))
650
651 dic = {d: 1}
652 dic[e] = 2
653 self.assertEqual(len(dic), 1)
654 self.assertEqual(dic[d], 2)
655 self.assertEqual(dic[e], 2)
656
657 d = self.theclass(2001, 1, 1)
658 # same thing
659 e = self.theclass(2001, 1, 1)
660 self.assertEqual(d, e)
661 self.assertEqual(hash(d), hash(e))
662
663 dic = {d: 1}
664 dic[e] = 2
665 self.assertEqual(len(dic), 1)
666 self.assertEqual(dic[d], 2)
667 self.assertEqual(dic[e], 2)
668
669 def test_computations(self):
670 a = self.theclass(2002, 1, 31)
671 b = self.theclass(1956, 1, 31)
672
673 diff = a-b
674 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
675 self.assertEqual(diff.seconds, 0)
676 self.assertEqual(diff.microseconds, 0)
677
678 day = timedelta(1)
679 week = timedelta(7)
680 a = self.theclass(2002, 3, 2)
681 self.assertEqual(a + day, self.theclass(2002, 3, 3))
682 self.assertEqual(day + a, self.theclass(2002, 3, 3))
683 self.assertEqual(a - day, self.theclass(2002, 3, 1))
684 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
685 self.assertEqual(a + week, self.theclass(2002, 3, 9))
686 self.assertEqual(a - week, self.theclass(2002, 2, 23))
687 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
688 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
689 self.assertEqual((a + week) - a, week)
690 self.assertEqual((a + day) - a, day)
691 self.assertEqual((a - week) - a, -week)
692 self.assertEqual((a - day) - a, -day)
693 self.assertEqual(a - (a + week), -week)
694 self.assertEqual(a - (a + day), -day)
695 self.assertEqual(a - (a - week), week)
696 self.assertEqual(a - (a - day), day)
697
698 # Add/sub ints, longs, floats should be illegal
699 for i in 1, 1L, 1.0:
700 self.assertRaises(TypeError, lambda: a+i)
701 self.assertRaises(TypeError, lambda: a-i)
702 self.assertRaises(TypeError, lambda: i+a)
703 self.assertRaises(TypeError, lambda: i-a)
704
705 # delta - date is senseless.
706 self.assertRaises(TypeError, lambda: day - a)
707 # mixing date and (delta or date) via * or // is senseless
708 self.assertRaises(TypeError, lambda: day * a)
709 self.assertRaises(TypeError, lambda: a * day)
710 self.assertRaises(TypeError, lambda: day // a)
711 self.assertRaises(TypeError, lambda: a // day)
712 self.assertRaises(TypeError, lambda: a * a)
713 self.assertRaises(TypeError, lambda: a // a)
714 # date + date is senseless
715 self.assertRaises(TypeError, lambda: a + a)
716
717 def test_overflow(self):
718 tiny = self.theclass.resolution
719
720 dt = self.theclass.min + tiny
721 dt -= tiny # no problem
722 self.assertRaises(OverflowError, dt.__sub__, tiny)
723 self.assertRaises(OverflowError, dt.__add__, -tiny)
724
725 dt = self.theclass.max - tiny
726 dt += tiny # no problem
727 self.assertRaises(OverflowError, dt.__add__, tiny)
728 self.assertRaises(OverflowError, dt.__sub__, -tiny)
729
730 def test_fromtimestamp(self):
731 import time
732
733 # Try an arbitrary fixed value.
734 year, month, day = 1999, 9, 19
735 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
736 d = self.theclass.fromtimestamp(ts)
737 self.assertEqual(d.year, year)
738 self.assertEqual(d.month, month)
739 self.assertEqual(d.day, day)
740
Tim Peters1b6f7a92004-06-20 02:50:16 +0000741 def test_insane_fromtimestamp(self):
742 # It's possible that some platform maps time_t to double,
743 # and that this test will fail there. This test should
744 # exempt such platforms (provided they return reasonable
745 # results!).
746 for insane in -1e200, 1e200:
747 self.assertRaises(ValueError, self.theclass.fromtimestamp,
748 insane)
749
Tim Peters2a799bf2002-12-16 20:18:38 +0000750 def test_today(self):
751 import time
752
753 # We claim that today() is like fromtimestamp(time.time()), so
754 # prove it.
755 for dummy in range(3):
756 today = self.theclass.today()
757 ts = time.time()
758 todayagain = self.theclass.fromtimestamp(ts)
759 if today == todayagain:
760 break
761 # There are several legit reasons that could fail:
762 # 1. It recently became midnight, between the today() and the
763 # time() calls.
764 # 2. The platform time() has such fine resolution that we'll
765 # never get the same value twice.
766 # 3. The platform time() has poor resolution, and we just
767 # happened to call today() right before a resolution quantum
768 # boundary.
769 # 4. The system clock got fiddled between calls.
770 # In any case, wait a little while and try again.
771 time.sleep(0.1)
772
773 # It worked or it didn't. If it didn't, assume it's reason #2, and
774 # let the test pass if they're within half a second of each other.
775 self.failUnless(today == todayagain or
776 abs(todayagain - today) < timedelta(seconds=0.5))
777
778 def test_weekday(self):
779 for i in range(7):
780 # March 4, 2002 is a Monday
781 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
782 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
783 # January 2, 1956 is a Monday
784 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
785 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
786
787 def test_isocalendar(self):
788 # Check examples from
789 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
790 for i in range(7):
791 d = self.theclass(2003, 12, 22+i)
792 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
793 d = self.theclass(2003, 12, 29) + timedelta(i)
794 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
795 d = self.theclass(2004, 1, 5+i)
796 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
797 d = self.theclass(2009, 12, 21+i)
798 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
799 d = self.theclass(2009, 12, 28) + timedelta(i)
800 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
801 d = self.theclass(2010, 1, 4+i)
802 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
803
804 def test_iso_long_years(self):
805 # Calculate long ISO years and compare to table from
806 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
807 ISO_LONG_YEARS_TABLE = """
808 4 32 60 88
809 9 37 65 93
810 15 43 71 99
811 20 48 76
812 26 54 82
813
814 105 133 161 189
815 111 139 167 195
816 116 144 172
817 122 150 178
818 128 156 184
819
820 201 229 257 285
821 207 235 263 291
822 212 240 268 296
823 218 246 274
824 224 252 280
825
826 303 331 359 387
827 308 336 364 392
828 314 342 370 398
829 320 348 376
830 325 353 381
831 """
832 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
833 iso_long_years.sort()
834 L = []
835 for i in range(400):
836 d = self.theclass(2000+i, 12, 31)
837 d1 = self.theclass(1600+i, 12, 31)
838 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
839 if d.isocalendar()[1] == 53:
840 L.append(i)
841 self.assertEqual(L, iso_long_years)
842
843 def test_isoformat(self):
844 t = self.theclass(2, 3, 2)
845 self.assertEqual(t.isoformat(), "0002-03-02")
846
847 def test_ctime(self):
848 t = self.theclass(2002, 3, 2)
849 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
850
851 def test_strftime(self):
852 t = self.theclass(2005, 3, 2)
853 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000854 self.assertEqual(t.strftime(""), "") # SF bug #761337
Tim Peters2a799bf2002-12-16 20:18:38 +0000855
856 self.assertRaises(TypeError, t.strftime) # needs an arg
857 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
858 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
859
860 # A naive object replaces %z and %Z w/ empty strings.
861 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
862
863 def test_resolution_info(self):
864 self.assert_(isinstance(self.theclass.min, self.theclass))
865 self.assert_(isinstance(self.theclass.max, self.theclass))
866 self.assert_(isinstance(self.theclass.resolution, timedelta))
867 self.assert_(self.theclass.max > self.theclass.min)
868
869 def test_extreme_timedelta(self):
870 big = self.theclass.max - self.theclass.min
871 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
872 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
873 # n == 315537897599999999 ~= 2**58.13
874 justasbig = timedelta(0, 0, n)
875 self.assertEqual(big, justasbig)
876 self.assertEqual(self.theclass.min + big, self.theclass.max)
877 self.assertEqual(self.theclass.max - big, self.theclass.min)
878
879 def test_timetuple(self):
880 for i in range(7):
881 # January 2, 1956 is a Monday (0)
882 d = self.theclass(1956, 1, 2+i)
883 t = d.timetuple()
884 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
885 # February 1, 1956 is a Wednesday (2)
886 d = self.theclass(1956, 2, 1+i)
887 t = d.timetuple()
888 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
889 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
890 # of the year.
891 d = self.theclass(1956, 3, 1+i)
892 t = d.timetuple()
893 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
894 self.assertEqual(t.tm_year, 1956)
895 self.assertEqual(t.tm_mon, 3)
896 self.assertEqual(t.tm_mday, 1+i)
897 self.assertEqual(t.tm_hour, 0)
898 self.assertEqual(t.tm_min, 0)
899 self.assertEqual(t.tm_sec, 0)
900 self.assertEqual(t.tm_wday, (3+i)%7)
901 self.assertEqual(t.tm_yday, 61+i)
902 self.assertEqual(t.tm_isdst, -1)
903
904 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000905 args = 6, 7, 23
906 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000907 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000908 green = pickler.dumps(orig, proto)
909 derived = unpickler.loads(green)
910 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000911
912 def test_compare(self):
913 t1 = self.theclass(2, 3, 4)
914 t2 = self.theclass(2, 3, 4)
915 self.failUnless(t1 == t2)
916 self.failUnless(t1 <= t2)
917 self.failUnless(t1 >= t2)
918 self.failUnless(not t1 != t2)
919 self.failUnless(not t1 < t2)
920 self.failUnless(not t1 > t2)
921 self.assertEqual(cmp(t1, t2), 0)
922 self.assertEqual(cmp(t2, t1), 0)
923
924 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
925 t2 = self.theclass(*args) # this is larger than t1
926 self.failUnless(t1 < t2)
927 self.failUnless(t2 > t1)
928 self.failUnless(t1 <= t2)
929 self.failUnless(t2 >= t1)
930 self.failUnless(t1 != t2)
931 self.failUnless(t2 != t1)
932 self.failUnless(not t1 == t2)
933 self.failUnless(not t2 == t1)
934 self.failUnless(not t1 > t2)
935 self.failUnless(not t2 < t1)
936 self.failUnless(not t1 >= t2)
937 self.failUnless(not t2 <= t1)
938 self.assertEqual(cmp(t1, t2), -1)
939 self.assertEqual(cmp(t2, t1), 1)
940
Tim Peters68124bb2003-02-08 03:46:31 +0000941 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000942 self.assertEqual(t1 == badarg, False)
943 self.assertEqual(t1 != badarg, True)
944 self.assertEqual(badarg == t1, False)
945 self.assertEqual(badarg != t1, True)
946
Tim Peters2a799bf2002-12-16 20:18:38 +0000947 self.assertRaises(TypeError, lambda: t1 < badarg)
948 self.assertRaises(TypeError, lambda: t1 > badarg)
949 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000950 self.assertRaises(TypeError, lambda: badarg <= t1)
951 self.assertRaises(TypeError, lambda: badarg < t1)
952 self.assertRaises(TypeError, lambda: badarg > t1)
953 self.assertRaises(TypeError, lambda: badarg >= t1)
954
Tim Peters8d81a012003-01-24 22:36:34 +0000955 def test_mixed_compare(self):
956 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000957
958 # Our class can be compared for equality to other classes
959 self.assertEqual(our == 1, False)
960 self.assertEqual(1 == our, False)
961 self.assertEqual(our != 1, True)
962 self.assertEqual(1 != our, True)
963
964 # But the ordering is undefined
965 self.assertRaises(TypeError, lambda: our < 1)
966 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000967 self.assertRaises(TypeError, cmp, our, 1)
968 self.assertRaises(TypeError, cmp, 1, our)
969
Guido van Rossum19960592006-08-24 17:29:38 +0000970 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +0000971
Guido van Rossum19960592006-08-24 17:29:38 +0000972 class SomeClass:
973 pass
974
975 their = SomeClass()
976 self.assertEqual(our == their, False)
977 self.assertEqual(their == our, False)
978 self.assertEqual(our != their, True)
979 self.assertEqual(their != our, True)
980 self.assertRaises(TypeError, lambda: our < their)
981 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000982 self.assertRaises(TypeError, cmp, our, their)
Guido van Rossum19960592006-08-24 17:29:38 +0000983 self.assertRaises(TypeError, cmp, their, our)
Tim Peters8d81a012003-01-24 22:36:34 +0000984
Guido van Rossum19960592006-08-24 17:29:38 +0000985 # However, if the other class explicitly defines ordering
986 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +0000987
Guido van Rossum19960592006-08-24 17:29:38 +0000988 class LargerThanAnything:
989 def __lt__(self, other):
990 return False
991 def __le__(self, other):
992 return isinstance(other, LargerThanAnything)
993 def __eq__(self, other):
994 return isinstance(other, LargerThanAnything)
995 def __ne__(self, other):
996 return not isinstance(other, LargerThanAnything)
997 def __gt__(self, other):
998 return not isinstance(other, LargerThanAnything)
999 def __ge__(self, other):
1000 return True
1001
1002 their = LargerThanAnything()
1003 self.assertEqual(our == their, False)
1004 self.assertEqual(their == our, False)
1005 self.assertEqual(our != their, True)
1006 self.assertEqual(their != our, True)
1007 self.assertEqual(our < their, True)
1008 self.assertEqual(their < our, False)
1009 self.assertEqual(cmp(our, their), -1)
1010 self.assertEqual(cmp(their, our), 1)
Tim Peters8d81a012003-01-24 22:36:34 +00001011
Tim Peters2a799bf2002-12-16 20:18:38 +00001012 def test_bool(self):
1013 # All dates are considered true.
1014 self.failUnless(self.theclass.min)
1015 self.failUnless(self.theclass.max)
1016
Tim Petersd6844152002-12-22 20:58:42 +00001017 def test_srftime_out_of_range(self):
1018 # For nasty technical reasons, we can't handle years before 1900.
1019 cls = self.theclass
1020 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1021 for y in 1, 49, 51, 99, 100, 1000, 1899:
1022 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001023
1024 def test_replace(self):
1025 cls = self.theclass
1026 args = [1, 2, 3]
1027 base = cls(*args)
1028 self.assertEqual(base, base.replace())
1029
1030 i = 0
1031 for name, newval in (("year", 2),
1032 ("month", 3),
1033 ("day", 4)):
1034 newargs = args[:]
1035 newargs[i] = newval
1036 expected = cls(*newargs)
1037 got = base.replace(**{name: newval})
1038 self.assertEqual(expected, got)
1039 i += 1
1040
1041 # Out of bounds.
1042 base = cls(2000, 2, 29)
1043 self.assertRaises(ValueError, base.replace, year=2001)
1044
Tim Petersa98924a2003-05-17 05:55:19 +00001045 def test_subclass_date(self):
1046
1047 class C(self.theclass):
1048 theAnswer = 42
1049
1050 def __new__(cls, *args, **kws):
1051 temp = kws.copy()
1052 extra = temp.pop('extra')
1053 result = self.theclass.__new__(cls, *args, **temp)
1054 result.extra = extra
1055 return result
1056
1057 def newmeth(self, start):
1058 return start + self.year + self.month
1059
1060 args = 2003, 4, 14
1061
1062 dt1 = self.theclass(*args)
1063 dt2 = C(*args, **{'extra': 7})
1064
1065 self.assertEqual(dt2.__class__, C)
1066 self.assertEqual(dt2.theAnswer, 42)
1067 self.assertEqual(dt2.extra, 7)
1068 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1069 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1070
Tim Peters604c0132004-06-07 23:04:33 +00001071 def test_pickling_subclass_date(self):
1072
1073 args = 6, 7, 23
1074 orig = SubclassDate(*args)
1075 for pickler, unpickler, proto in pickle_choices:
1076 green = pickler.dumps(orig, proto)
1077 derived = unpickler.loads(green)
1078 self.assertEqual(orig, derived)
1079
Tim Peters3f606292004-03-21 23:38:41 +00001080 def test_backdoor_resistance(self):
1081 # For fast unpickling, the constructor accepts a pickle string.
1082 # This is a low-overhead backdoor. A user can (by intent or
1083 # mistake) pass a string directly, which (if it's the right length)
1084 # will get treated like a pickle, and bypass the normal sanity
1085 # checks in the constructor. This can create insane objects.
1086 # The constructor doesn't want to burn the time to validate all
1087 # fields, but does check the month field. This stops, e.g.,
1088 # datetime.datetime('1995-03-25') from yielding an insane object.
1089 base = '1995-03-25'
1090 if not issubclass(self.theclass, datetime):
1091 base = base[:4]
1092 for month_byte in '9', chr(0), chr(13), '\xff':
1093 self.assertRaises(TypeError, self.theclass,
1094 base[:2] + month_byte + base[3:])
1095 for ord_byte in range(1, 13):
1096 # This shouldn't blow up because of the month byte alone. If
1097 # the implementation changes to do more-careful checking, it may
1098 # blow up because other fields are insane.
1099 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001100
Tim Peters2a799bf2002-12-16 20:18:38 +00001101#############################################################################
1102# datetime tests
1103
Tim Peters604c0132004-06-07 23:04:33 +00001104class SubclassDatetime(datetime):
1105 sub_var = 1
1106
Tim Peters2a799bf2002-12-16 20:18:38 +00001107class TestDateTime(TestDate):
1108
1109 theclass = datetime
1110
1111 def test_basic_attributes(self):
1112 dt = self.theclass(2002, 3, 1, 12, 0)
1113 self.assertEqual(dt.year, 2002)
1114 self.assertEqual(dt.month, 3)
1115 self.assertEqual(dt.day, 1)
1116 self.assertEqual(dt.hour, 12)
1117 self.assertEqual(dt.minute, 0)
1118 self.assertEqual(dt.second, 0)
1119 self.assertEqual(dt.microsecond, 0)
1120
1121 def test_basic_attributes_nonzero(self):
1122 # Make sure all attributes are non-zero so bugs in
1123 # bit-shifting access show up.
1124 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1125 self.assertEqual(dt.year, 2002)
1126 self.assertEqual(dt.month, 3)
1127 self.assertEqual(dt.day, 1)
1128 self.assertEqual(dt.hour, 12)
1129 self.assertEqual(dt.minute, 59)
1130 self.assertEqual(dt.second, 59)
1131 self.assertEqual(dt.microsecond, 8000)
1132
1133 def test_roundtrip(self):
1134 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1135 self.theclass.now()):
1136 # Verify dt -> string -> datetime identity.
1137 s = repr(dt)
1138 self.failUnless(s.startswith('datetime.'))
1139 s = s[9:]
1140 dt2 = eval(s)
1141 self.assertEqual(dt, dt2)
1142
1143 # Verify identity via reconstructing from pieces.
1144 dt2 = self.theclass(dt.year, dt.month, dt.day,
1145 dt.hour, dt.minute, dt.second,
1146 dt.microsecond)
1147 self.assertEqual(dt, dt2)
1148
1149 def test_isoformat(self):
1150 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1151 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1152 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1153 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1154 # str is ISO format with the separator forced to a blank.
1155 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1156
1157 t = self.theclass(2, 3, 2)
1158 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1159 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1160 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1161 # str is ISO format with the separator forced to a blank.
1162 self.assertEqual(str(t), "0002-03-02 00:00:00")
1163
1164 def test_more_ctime(self):
1165 # Test fields that TestDate doesn't touch.
1166 import time
1167
1168 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1169 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1170 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1171 # out. The difference is that t.ctime() produces " 2" for the day,
1172 # but platform ctime() produces "02" for the day. According to
1173 # C99, t.ctime() is correct here.
1174 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1175
1176 # So test a case where that difference doesn't matter.
1177 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1178 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1179
1180 def test_tz_independent_comparing(self):
1181 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1182 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1183 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1184 self.assertEqual(dt1, dt3)
1185 self.assert_(dt2 > dt3)
1186
1187 # Make sure comparison doesn't forget microseconds, and isn't done
1188 # via comparing a float timestamp (an IEEE double doesn't have enough
1189 # precision to span microsecond resolution across years 1 thru 9999,
1190 # so comparing via timestamp necessarily calls some distinct values
1191 # equal).
1192 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1193 us = timedelta(microseconds=1)
1194 dt2 = dt1 + us
1195 self.assertEqual(dt2 - dt1, us)
1196 self.assert_(dt1 < dt2)
1197
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001198 def test_strftime_with_bad_tzname_replace(self):
1199 # verify ok if tzinfo.tzname().replace() returns a non-string
1200 class MyTzInfo(FixedOffset):
1201 def tzname(self, dt):
1202 class MyStr(str):
1203 def replace(self, *args):
1204 return None
1205 return MyStr('name')
1206 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1207 self.assertRaises(TypeError, t.strftime, '%Z')
1208
Tim Peters2a799bf2002-12-16 20:18:38 +00001209 def test_bad_constructor_arguments(self):
1210 # bad years
1211 self.theclass(MINYEAR, 1, 1) # no exception
1212 self.theclass(MAXYEAR, 1, 1) # no exception
1213 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1214 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1215 # bad months
1216 self.theclass(2000, 1, 1) # no exception
1217 self.theclass(2000, 12, 1) # no exception
1218 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1219 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1220 # bad days
1221 self.theclass(2000, 2, 29) # no exception
1222 self.theclass(2004, 2, 29) # no exception
1223 self.theclass(2400, 2, 29) # no exception
1224 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1225 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1226 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1227 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1228 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1229 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1230 # bad hours
1231 self.theclass(2000, 1, 31, 0) # no exception
1232 self.theclass(2000, 1, 31, 23) # no exception
1233 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1234 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1235 # bad minutes
1236 self.theclass(2000, 1, 31, 23, 0) # no exception
1237 self.theclass(2000, 1, 31, 23, 59) # no exception
1238 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1239 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1240 # bad seconds
1241 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1242 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1243 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1244 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1245 # bad microseconds
1246 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1247 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1248 self.assertRaises(ValueError, self.theclass,
1249 2000, 1, 31, 23, 59, 59, -1)
1250 self.assertRaises(ValueError, self.theclass,
1251 2000, 1, 31, 23, 59, 59,
1252 1000000)
1253
1254 def test_hash_equality(self):
1255 d = self.theclass(2000, 12, 31, 23, 30, 17)
1256 e = self.theclass(2000, 12, 31, 23, 30, 17)
1257 self.assertEqual(d, e)
1258 self.assertEqual(hash(d), hash(e))
1259
1260 dic = {d: 1}
1261 dic[e] = 2
1262 self.assertEqual(len(dic), 1)
1263 self.assertEqual(dic[d], 2)
1264 self.assertEqual(dic[e], 2)
1265
1266 d = self.theclass(2001, 1, 1, 0, 5, 17)
1267 e = self.theclass(2001, 1, 1, 0, 5, 17)
1268 self.assertEqual(d, e)
1269 self.assertEqual(hash(d), hash(e))
1270
1271 dic = {d: 1}
1272 dic[e] = 2
1273 self.assertEqual(len(dic), 1)
1274 self.assertEqual(dic[d], 2)
1275 self.assertEqual(dic[e], 2)
1276
1277 def test_computations(self):
1278 a = self.theclass(2002, 1, 31)
1279 b = self.theclass(1956, 1, 31)
1280 diff = a-b
1281 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1282 self.assertEqual(diff.seconds, 0)
1283 self.assertEqual(diff.microseconds, 0)
1284 a = self.theclass(2002, 3, 2, 17, 6)
1285 millisec = timedelta(0, 0, 1000)
1286 hour = timedelta(0, 3600)
1287 day = timedelta(1)
1288 week = timedelta(7)
1289 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1290 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1291 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1292 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1293 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1294 self.assertEqual(a - hour, a + -hour)
1295 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1296 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1297 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1298 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1299 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1300 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1301 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1302 self.assertEqual((a + week) - a, week)
1303 self.assertEqual((a + day) - a, day)
1304 self.assertEqual((a + hour) - a, hour)
1305 self.assertEqual((a + millisec) - a, millisec)
1306 self.assertEqual((a - week) - a, -week)
1307 self.assertEqual((a - day) - a, -day)
1308 self.assertEqual((a - hour) - a, -hour)
1309 self.assertEqual((a - millisec) - a, -millisec)
1310 self.assertEqual(a - (a + week), -week)
1311 self.assertEqual(a - (a + day), -day)
1312 self.assertEqual(a - (a + hour), -hour)
1313 self.assertEqual(a - (a + millisec), -millisec)
1314 self.assertEqual(a - (a - week), week)
1315 self.assertEqual(a - (a - day), day)
1316 self.assertEqual(a - (a - hour), hour)
1317 self.assertEqual(a - (a - millisec), millisec)
1318 self.assertEqual(a + (week + day + hour + millisec),
1319 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1320 self.assertEqual(a + (week + day + hour + millisec),
1321 (((a + week) + day) + hour) + millisec)
1322 self.assertEqual(a - (week + day + hour + millisec),
1323 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1324 self.assertEqual(a - (week + day + hour + millisec),
1325 (((a - week) - day) - hour) - millisec)
1326 # Add/sub ints, longs, floats should be illegal
1327 for i in 1, 1L, 1.0:
1328 self.assertRaises(TypeError, lambda: a+i)
1329 self.assertRaises(TypeError, lambda: a-i)
1330 self.assertRaises(TypeError, lambda: i+a)
1331 self.assertRaises(TypeError, lambda: i-a)
1332
1333 # delta - datetime is senseless.
1334 self.assertRaises(TypeError, lambda: day - a)
1335 # mixing datetime and (delta or datetime) via * or // is senseless
1336 self.assertRaises(TypeError, lambda: day * a)
1337 self.assertRaises(TypeError, lambda: a * day)
1338 self.assertRaises(TypeError, lambda: day // a)
1339 self.assertRaises(TypeError, lambda: a // day)
1340 self.assertRaises(TypeError, lambda: a * a)
1341 self.assertRaises(TypeError, lambda: a // a)
1342 # datetime + datetime is senseless
1343 self.assertRaises(TypeError, lambda: a + a)
1344
1345 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001346 args = 6, 7, 23, 20, 59, 1, 64**2
1347 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001348 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001349 green = pickler.dumps(orig, proto)
1350 derived = unpickler.loads(green)
1351 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001352
Guido van Rossum275666f2003-02-07 21:49:01 +00001353 def test_more_pickling(self):
1354 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1355 s = pickle.dumps(a)
1356 b = pickle.loads(s)
1357 self.assertEqual(b.year, 2003)
1358 self.assertEqual(b.month, 2)
1359 self.assertEqual(b.day, 7)
1360
Tim Peters604c0132004-06-07 23:04:33 +00001361 def test_pickling_subclass_datetime(self):
1362 args = 6, 7, 23, 20, 59, 1, 64**2
1363 orig = SubclassDatetime(*args)
1364 for pickler, unpickler, proto in pickle_choices:
1365 green = pickler.dumps(orig, proto)
1366 derived = unpickler.loads(green)
1367 self.assertEqual(orig, derived)
1368
Tim Peters2a799bf2002-12-16 20:18:38 +00001369 def test_more_compare(self):
1370 # The test_compare() inherited from TestDate covers the error cases.
1371 # We just want to test lexicographic ordering on the members datetime
1372 # has that date lacks.
1373 args = [2000, 11, 29, 20, 58, 16, 999998]
1374 t1 = self.theclass(*args)
1375 t2 = self.theclass(*args)
1376 self.failUnless(t1 == t2)
1377 self.failUnless(t1 <= t2)
1378 self.failUnless(t1 >= t2)
1379 self.failUnless(not t1 != t2)
1380 self.failUnless(not t1 < t2)
1381 self.failUnless(not t1 > t2)
1382 self.assertEqual(cmp(t1, t2), 0)
1383 self.assertEqual(cmp(t2, t1), 0)
1384
1385 for i in range(len(args)):
1386 newargs = args[:]
1387 newargs[i] = args[i] + 1
1388 t2 = self.theclass(*newargs) # this is larger than t1
1389 self.failUnless(t1 < t2)
1390 self.failUnless(t2 > t1)
1391 self.failUnless(t1 <= t2)
1392 self.failUnless(t2 >= t1)
1393 self.failUnless(t1 != t2)
1394 self.failUnless(t2 != t1)
1395 self.failUnless(not t1 == t2)
1396 self.failUnless(not t2 == t1)
1397 self.failUnless(not t1 > t2)
1398 self.failUnless(not t2 < t1)
1399 self.failUnless(not t1 >= t2)
1400 self.failUnless(not t2 <= t1)
1401 self.assertEqual(cmp(t1, t2), -1)
1402 self.assertEqual(cmp(t2, t1), 1)
1403
1404
1405 # A helper for timestamp constructor tests.
1406 def verify_field_equality(self, expected, got):
1407 self.assertEqual(expected.tm_year, got.year)
1408 self.assertEqual(expected.tm_mon, got.month)
1409 self.assertEqual(expected.tm_mday, got.day)
1410 self.assertEqual(expected.tm_hour, got.hour)
1411 self.assertEqual(expected.tm_min, got.minute)
1412 self.assertEqual(expected.tm_sec, got.second)
1413
1414 def test_fromtimestamp(self):
1415 import time
1416
1417 ts = time.time()
1418 expected = time.localtime(ts)
1419 got = self.theclass.fromtimestamp(ts)
1420 self.verify_field_equality(expected, got)
1421
1422 def test_utcfromtimestamp(self):
1423 import time
1424
1425 ts = time.time()
1426 expected = time.gmtime(ts)
1427 got = self.theclass.utcfromtimestamp(ts)
1428 self.verify_field_equality(expected, got)
1429
Thomas Wouters477c8d52006-05-27 19:21:47 +00001430 def test_microsecond_rounding(self):
1431 # Test whether fromtimestamp "rounds up" floats that are less
1432 # than one microsecond smaller than an integer.
1433 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1434 self.theclass.fromtimestamp(1))
1435
Tim Peters1b6f7a92004-06-20 02:50:16 +00001436 def test_insane_fromtimestamp(self):
1437 # It's possible that some platform maps time_t to double,
1438 # and that this test will fail there. This test should
1439 # exempt such platforms (provided they return reasonable
1440 # results!).
1441 for insane in -1e200, 1e200:
1442 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1443 insane)
1444
1445 def test_insane_utcfromtimestamp(self):
1446 # It's possible that some platform maps time_t to double,
1447 # and that this test will fail there. This test should
1448 # exempt such platforms (provided they return reasonable
1449 # results!).
1450 for insane in -1e200, 1e200:
1451 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1452 insane)
1453
Tim Peters2a799bf2002-12-16 20:18:38 +00001454 def test_utcnow(self):
1455 import time
1456
1457 # Call it a success if utcnow() and utcfromtimestamp() are within
1458 # a second of each other.
1459 tolerance = timedelta(seconds=1)
1460 for dummy in range(3):
1461 from_now = self.theclass.utcnow()
1462 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1463 if abs(from_timestamp - from_now) <= tolerance:
1464 break
1465 # Else try again a few times.
1466 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1467
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001468 def test_strptime(self):
1469 import time
1470
1471 string = '2004-12-01 13:02:47'
1472 format = '%Y-%m-%d %H:%M:%S'
1473 expected = self.theclass(*(time.strptime(string, format)[0:6]))
1474 got = self.theclass.strptime(string, format)
1475 self.assertEqual(expected, got)
1476
Tim Peters2a799bf2002-12-16 20:18:38 +00001477 def test_more_timetuple(self):
1478 # This tests fields beyond those tested by the TestDate.test_timetuple.
1479 t = self.theclass(2004, 12, 31, 6, 22, 33)
1480 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1481 self.assertEqual(t.timetuple(),
1482 (t.year, t.month, t.day,
1483 t.hour, t.minute, t.second,
1484 t.weekday(),
1485 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1486 -1))
1487 tt = t.timetuple()
1488 self.assertEqual(tt.tm_year, t.year)
1489 self.assertEqual(tt.tm_mon, t.month)
1490 self.assertEqual(tt.tm_mday, t.day)
1491 self.assertEqual(tt.tm_hour, t.hour)
1492 self.assertEqual(tt.tm_min, t.minute)
1493 self.assertEqual(tt.tm_sec, t.second)
1494 self.assertEqual(tt.tm_wday, t.weekday())
1495 self.assertEqual(tt.tm_yday, t.toordinal() -
1496 date(t.year, 1, 1).toordinal() + 1)
1497 self.assertEqual(tt.tm_isdst, -1)
1498
1499 def test_more_strftime(self):
1500 # This tests fields beyond those tested by the TestDate.test_strftime.
1501 t = self.theclass(2004, 12, 31, 6, 22, 33)
1502 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1503 "12 31 04 33 22 06 366")
1504
1505 def test_extract(self):
1506 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1507 self.assertEqual(dt.date(), date(2002, 3, 4))
1508 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1509
1510 def test_combine(self):
1511 d = date(2002, 3, 4)
1512 t = time(18, 45, 3, 1234)
1513 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1514 combine = self.theclass.combine
1515 dt = combine(d, t)
1516 self.assertEqual(dt, expected)
1517
1518 dt = combine(time=t, date=d)
1519 self.assertEqual(dt, expected)
1520
1521 self.assertEqual(d, dt.date())
1522 self.assertEqual(t, dt.time())
1523 self.assertEqual(dt, combine(dt.date(), dt.time()))
1524
1525 self.assertRaises(TypeError, combine) # need an arg
1526 self.assertRaises(TypeError, combine, d) # need two args
1527 self.assertRaises(TypeError, combine, t, d) # args reversed
1528 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1529 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1530
Tim Peters12bf3392002-12-24 05:41:27 +00001531 def test_replace(self):
1532 cls = self.theclass
1533 args = [1, 2, 3, 4, 5, 6, 7]
1534 base = cls(*args)
1535 self.assertEqual(base, base.replace())
1536
1537 i = 0
1538 for name, newval in (("year", 2),
1539 ("month", 3),
1540 ("day", 4),
1541 ("hour", 5),
1542 ("minute", 6),
1543 ("second", 7),
1544 ("microsecond", 8)):
1545 newargs = args[:]
1546 newargs[i] = newval
1547 expected = cls(*newargs)
1548 got = base.replace(**{name: newval})
1549 self.assertEqual(expected, got)
1550 i += 1
1551
1552 # Out of bounds.
1553 base = cls(2000, 2, 29)
1554 self.assertRaises(ValueError, base.replace, year=2001)
1555
Tim Peters80475bb2002-12-25 07:40:55 +00001556 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001557 # Pretty boring! The TZ test is more interesting here. astimezone()
1558 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001559 dt = self.theclass.now()
1560 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001561 self.assertRaises(TypeError, dt.astimezone) # not enough args
1562 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1563 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001564 self.assertRaises(ValueError, dt.astimezone, f) # naive
1565 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001566
Tim Peters52dcce22003-01-23 16:36:11 +00001567 class Bogus(tzinfo):
1568 def utcoffset(self, dt): return None
1569 def dst(self, dt): return timedelta(0)
1570 bog = Bogus()
1571 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1572
1573 class AlsoBogus(tzinfo):
1574 def utcoffset(self, dt): return timedelta(0)
1575 def dst(self, dt): return None
1576 alsobog = AlsoBogus()
1577 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001578
Tim Petersa98924a2003-05-17 05:55:19 +00001579 def test_subclass_datetime(self):
1580
1581 class C(self.theclass):
1582 theAnswer = 42
1583
1584 def __new__(cls, *args, **kws):
1585 temp = kws.copy()
1586 extra = temp.pop('extra')
1587 result = self.theclass.__new__(cls, *args, **temp)
1588 result.extra = extra
1589 return result
1590
1591 def newmeth(self, start):
1592 return start + self.year + self.month + self.second
1593
1594 args = 2003, 4, 14, 12, 13, 41
1595
1596 dt1 = self.theclass(*args)
1597 dt2 = C(*args, **{'extra': 7})
1598
1599 self.assertEqual(dt2.__class__, C)
1600 self.assertEqual(dt2.theAnswer, 42)
1601 self.assertEqual(dt2.extra, 7)
1602 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1603 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1604 dt1.second - 7)
1605
Tim Peters604c0132004-06-07 23:04:33 +00001606class SubclassTime(time):
1607 sub_var = 1
1608
Tim Peters07534a62003-02-07 22:50:28 +00001609class TestTime(HarmlessMixedComparison):
Tim Peters2a799bf2002-12-16 20:18:38 +00001610
1611 theclass = time
1612
1613 def test_basic_attributes(self):
1614 t = self.theclass(12, 0)
1615 self.assertEqual(t.hour, 12)
1616 self.assertEqual(t.minute, 0)
1617 self.assertEqual(t.second, 0)
1618 self.assertEqual(t.microsecond, 0)
1619
1620 def test_basic_attributes_nonzero(self):
1621 # Make sure all attributes are non-zero so bugs in
1622 # bit-shifting access show up.
1623 t = self.theclass(12, 59, 59, 8000)
1624 self.assertEqual(t.hour, 12)
1625 self.assertEqual(t.minute, 59)
1626 self.assertEqual(t.second, 59)
1627 self.assertEqual(t.microsecond, 8000)
1628
1629 def test_roundtrip(self):
1630 t = self.theclass(1, 2, 3, 4)
1631
1632 # Verify t -> string -> time identity.
1633 s = repr(t)
1634 self.failUnless(s.startswith('datetime.'))
1635 s = s[9:]
1636 t2 = eval(s)
1637 self.assertEqual(t, t2)
1638
1639 # Verify identity via reconstructing from pieces.
1640 t2 = self.theclass(t.hour, t.minute, t.second,
1641 t.microsecond)
1642 self.assertEqual(t, t2)
1643
1644 def test_comparing(self):
1645 args = [1, 2, 3, 4]
1646 t1 = self.theclass(*args)
1647 t2 = self.theclass(*args)
1648 self.failUnless(t1 == t2)
1649 self.failUnless(t1 <= t2)
1650 self.failUnless(t1 >= t2)
1651 self.failUnless(not t1 != t2)
1652 self.failUnless(not t1 < t2)
1653 self.failUnless(not t1 > t2)
1654 self.assertEqual(cmp(t1, t2), 0)
1655 self.assertEqual(cmp(t2, t1), 0)
1656
1657 for i in range(len(args)):
1658 newargs = args[:]
1659 newargs[i] = args[i] + 1
1660 t2 = self.theclass(*newargs) # this is larger than t1
1661 self.failUnless(t1 < t2)
1662 self.failUnless(t2 > t1)
1663 self.failUnless(t1 <= t2)
1664 self.failUnless(t2 >= t1)
1665 self.failUnless(t1 != t2)
1666 self.failUnless(t2 != t1)
1667 self.failUnless(not t1 == t2)
1668 self.failUnless(not t2 == t1)
1669 self.failUnless(not t1 > t2)
1670 self.failUnless(not t2 < t1)
1671 self.failUnless(not t1 >= t2)
1672 self.failUnless(not t2 <= t1)
1673 self.assertEqual(cmp(t1, t2), -1)
1674 self.assertEqual(cmp(t2, t1), 1)
1675
Tim Peters68124bb2003-02-08 03:46:31 +00001676 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001677 self.assertEqual(t1 == badarg, False)
1678 self.assertEqual(t1 != badarg, True)
1679 self.assertEqual(badarg == t1, False)
1680 self.assertEqual(badarg != t1, True)
1681
Tim Peters2a799bf2002-12-16 20:18:38 +00001682 self.assertRaises(TypeError, lambda: t1 <= badarg)
1683 self.assertRaises(TypeError, lambda: t1 < badarg)
1684 self.assertRaises(TypeError, lambda: t1 > badarg)
1685 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001686 self.assertRaises(TypeError, lambda: badarg <= t1)
1687 self.assertRaises(TypeError, lambda: badarg < t1)
1688 self.assertRaises(TypeError, lambda: badarg > t1)
1689 self.assertRaises(TypeError, lambda: badarg >= t1)
1690
1691 def test_bad_constructor_arguments(self):
1692 # bad hours
1693 self.theclass(0, 0) # no exception
1694 self.theclass(23, 0) # no exception
1695 self.assertRaises(ValueError, self.theclass, -1, 0)
1696 self.assertRaises(ValueError, self.theclass, 24, 0)
1697 # bad minutes
1698 self.theclass(23, 0) # no exception
1699 self.theclass(23, 59) # no exception
1700 self.assertRaises(ValueError, self.theclass, 23, -1)
1701 self.assertRaises(ValueError, self.theclass, 23, 60)
1702 # bad seconds
1703 self.theclass(23, 59, 0) # no exception
1704 self.theclass(23, 59, 59) # no exception
1705 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1706 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1707 # bad microseconds
1708 self.theclass(23, 59, 59, 0) # no exception
1709 self.theclass(23, 59, 59, 999999) # no exception
1710 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1711 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1712
1713 def test_hash_equality(self):
1714 d = self.theclass(23, 30, 17)
1715 e = self.theclass(23, 30, 17)
1716 self.assertEqual(d, e)
1717 self.assertEqual(hash(d), hash(e))
1718
1719 dic = {d: 1}
1720 dic[e] = 2
1721 self.assertEqual(len(dic), 1)
1722 self.assertEqual(dic[d], 2)
1723 self.assertEqual(dic[e], 2)
1724
1725 d = self.theclass(0, 5, 17)
1726 e = self.theclass(0, 5, 17)
1727 self.assertEqual(d, e)
1728 self.assertEqual(hash(d), hash(e))
1729
1730 dic = {d: 1}
1731 dic[e] = 2
1732 self.assertEqual(len(dic), 1)
1733 self.assertEqual(dic[d], 2)
1734 self.assertEqual(dic[e], 2)
1735
1736 def test_isoformat(self):
1737 t = self.theclass(4, 5, 1, 123)
1738 self.assertEqual(t.isoformat(), "04:05:01.000123")
1739 self.assertEqual(t.isoformat(), str(t))
1740
1741 t = self.theclass()
1742 self.assertEqual(t.isoformat(), "00:00:00")
1743 self.assertEqual(t.isoformat(), str(t))
1744
1745 t = self.theclass(microsecond=1)
1746 self.assertEqual(t.isoformat(), "00:00:00.000001")
1747 self.assertEqual(t.isoformat(), str(t))
1748
1749 t = self.theclass(microsecond=10)
1750 self.assertEqual(t.isoformat(), "00:00:00.000010")
1751 self.assertEqual(t.isoformat(), str(t))
1752
1753 t = self.theclass(microsecond=100)
1754 self.assertEqual(t.isoformat(), "00:00:00.000100")
1755 self.assertEqual(t.isoformat(), str(t))
1756
1757 t = self.theclass(microsecond=1000)
1758 self.assertEqual(t.isoformat(), "00:00:00.001000")
1759 self.assertEqual(t.isoformat(), str(t))
1760
1761 t = self.theclass(microsecond=10000)
1762 self.assertEqual(t.isoformat(), "00:00:00.010000")
1763 self.assertEqual(t.isoformat(), str(t))
1764
1765 t = self.theclass(microsecond=100000)
1766 self.assertEqual(t.isoformat(), "00:00:00.100000")
1767 self.assertEqual(t.isoformat(), str(t))
1768
1769 def test_strftime(self):
1770 t = self.theclass(1, 2, 3, 4)
1771 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1772 # A naive object replaces %z and %Z with empty strings.
1773 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1774
1775 def test_str(self):
1776 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1777 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1778 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1779 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1780 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1781
1782 def test_repr(self):
1783 name = 'datetime.' + self.theclass.__name__
1784 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1785 "%s(1, 2, 3, 4)" % name)
1786 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1787 "%s(10, 2, 3, 4000)" % name)
1788 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1789 "%s(0, 2, 3, 400000)" % name)
1790 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1791 "%s(12, 2, 3)" % name)
1792 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1793 "%s(23, 15)" % name)
1794
1795 def test_resolution_info(self):
1796 self.assert_(isinstance(self.theclass.min, self.theclass))
1797 self.assert_(isinstance(self.theclass.max, self.theclass))
1798 self.assert_(isinstance(self.theclass.resolution, timedelta))
1799 self.assert_(self.theclass.max > self.theclass.min)
1800
1801 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001802 args = 20, 59, 16, 64**2
1803 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001804 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001805 green = pickler.dumps(orig, proto)
1806 derived = unpickler.loads(green)
1807 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001808
Tim Peters604c0132004-06-07 23:04:33 +00001809 def test_pickling_subclass_time(self):
1810 args = 20, 59, 16, 64**2
1811 orig = SubclassTime(*args)
1812 for pickler, unpickler, proto in pickle_choices:
1813 green = pickler.dumps(orig, proto)
1814 derived = unpickler.loads(green)
1815 self.assertEqual(orig, derived)
1816
Tim Peters2a799bf2002-12-16 20:18:38 +00001817 def test_bool(self):
1818 cls = self.theclass
1819 self.failUnless(cls(1))
1820 self.failUnless(cls(0, 1))
1821 self.failUnless(cls(0, 0, 1))
1822 self.failUnless(cls(0, 0, 0, 1))
1823 self.failUnless(not cls(0))
1824 self.failUnless(not cls())
1825
Tim Peters12bf3392002-12-24 05:41:27 +00001826 def test_replace(self):
1827 cls = self.theclass
1828 args = [1, 2, 3, 4]
1829 base = cls(*args)
1830 self.assertEqual(base, base.replace())
1831
1832 i = 0
1833 for name, newval in (("hour", 5),
1834 ("minute", 6),
1835 ("second", 7),
1836 ("microsecond", 8)):
1837 newargs = args[:]
1838 newargs[i] = newval
1839 expected = cls(*newargs)
1840 got = base.replace(**{name: newval})
1841 self.assertEqual(expected, got)
1842 i += 1
1843
1844 # Out of bounds.
1845 base = cls(1)
1846 self.assertRaises(ValueError, base.replace, hour=24)
1847 self.assertRaises(ValueError, base.replace, minute=-1)
1848 self.assertRaises(ValueError, base.replace, second=100)
1849 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1850
Tim Petersa98924a2003-05-17 05:55:19 +00001851 def test_subclass_time(self):
1852
1853 class C(self.theclass):
1854 theAnswer = 42
1855
1856 def __new__(cls, *args, **kws):
1857 temp = kws.copy()
1858 extra = temp.pop('extra')
1859 result = self.theclass.__new__(cls, *args, **temp)
1860 result.extra = extra
1861 return result
1862
1863 def newmeth(self, start):
1864 return start + self.hour + self.second
1865
1866 args = 4, 5, 6
1867
1868 dt1 = self.theclass(*args)
1869 dt2 = C(*args, **{'extra': 7})
1870
1871 self.assertEqual(dt2.__class__, C)
1872 self.assertEqual(dt2.theAnswer, 42)
1873 self.assertEqual(dt2.extra, 7)
1874 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1875 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1876
Armin Rigof4afb212005-11-07 07:15:48 +00001877 def test_backdoor_resistance(self):
1878 # see TestDate.test_backdoor_resistance().
1879 base = '2:59.0'
1880 for hour_byte in ' ', '9', chr(24), '\xff':
1881 self.assertRaises(TypeError, self.theclass,
1882 hour_byte + base[1:])
1883
Tim Peters855fe882002-12-22 03:43:39 +00001884# A mixin for classes with a tzinfo= argument. Subclasses must define
1885# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001886# must be legit (which is true for time and datetime).
Tim Peters855fe882002-12-22 03:43:39 +00001887class TZInfoBase(unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001888
Tim Petersbad8ff02002-12-30 20:52:32 +00001889 def test_argument_passing(self):
1890 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001891 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001892 class introspective(tzinfo):
1893 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001894 def utcoffset(self, dt):
1895 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001896 dst = utcoffset
1897
1898 obj = cls(1, 2, 3, tzinfo=introspective())
1899
Tim Peters0bf60bd2003-01-08 20:40:01 +00001900 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001901 self.assertEqual(obj.tzname(), expected)
1902
Tim Peters0bf60bd2003-01-08 20:40:01 +00001903 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001904 self.assertEqual(obj.utcoffset(), expected)
1905 self.assertEqual(obj.dst(), expected)
1906
Tim Peters855fe882002-12-22 03:43:39 +00001907 def test_bad_tzinfo_classes(self):
1908 cls = self.theclass
1909 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001910
Tim Peters855fe882002-12-22 03:43:39 +00001911 class NiceTry(object):
1912 def __init__(self): pass
1913 def utcoffset(self, dt): pass
1914 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1915
1916 class BetterTry(tzinfo):
1917 def __init__(self): pass
1918 def utcoffset(self, dt): pass
1919 b = BetterTry()
1920 t = cls(1, 1, 1, tzinfo=b)
1921 self.failUnless(t.tzinfo is b)
1922
1923 def test_utc_offset_out_of_bounds(self):
1924 class Edgy(tzinfo):
1925 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00001926 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00001927 def utcoffset(self, dt):
1928 return self.offset
1929
1930 cls = self.theclass
1931 for offset, legit in ((-1440, False),
1932 (-1439, True),
1933 (1439, True),
1934 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001935 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00001936 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001937 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00001938 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001939 else:
1940 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00001941 if legit:
1942 aofs = abs(offset)
1943 h, m = divmod(aofs, 60)
1944 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001945 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00001946 t = t.timetz()
1947 self.assertEqual(str(t), "01:02:03" + tag)
1948 else:
1949 self.assertRaises(ValueError, str, t)
1950
1951 def test_tzinfo_classes(self):
1952 cls = self.theclass
1953 class C1(tzinfo):
1954 def utcoffset(self, dt): return None
1955 def dst(self, dt): return None
1956 def tzname(self, dt): return None
1957 for t in (cls(1, 1, 1),
1958 cls(1, 1, 1, tzinfo=None),
1959 cls(1, 1, 1, tzinfo=C1())):
1960 self.failUnless(t.utcoffset() is None)
1961 self.failUnless(t.dst() is None)
1962 self.failUnless(t.tzname() is None)
1963
Tim Peters855fe882002-12-22 03:43:39 +00001964 class C3(tzinfo):
1965 def utcoffset(self, dt): return timedelta(minutes=-1439)
1966 def dst(self, dt): return timedelta(minutes=1439)
1967 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001968 t = cls(1, 1, 1, tzinfo=C3())
1969 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
1970 self.assertEqual(t.dst(), timedelta(minutes=1439))
1971 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00001972
1973 # Wrong types.
1974 class C4(tzinfo):
1975 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001976 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00001977 def tzname(self, dt): return 0
1978 t = cls(1, 1, 1, tzinfo=C4())
1979 self.assertRaises(TypeError, t.utcoffset)
1980 self.assertRaises(TypeError, t.dst)
1981 self.assertRaises(TypeError, t.tzname)
1982
1983 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00001984 class C6(tzinfo):
1985 def utcoffset(self, dt): return timedelta(hours=-24)
1986 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00001987 t = cls(1, 1, 1, tzinfo=C6())
1988 self.assertRaises(ValueError, t.utcoffset)
1989 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00001990
1991 # Not a whole number of minutes.
1992 class C7(tzinfo):
1993 def utcoffset(self, dt): return timedelta(seconds=61)
1994 def dst(self, dt): return timedelta(microseconds=-81)
1995 t = cls(1, 1, 1, tzinfo=C7())
1996 self.assertRaises(ValueError, t.utcoffset)
1997 self.assertRaises(ValueError, t.dst)
1998
Tim Peters4c0db782002-12-26 05:01:19 +00001999 def test_aware_compare(self):
2000 cls = self.theclass
2001
Tim Peters60c76e42002-12-27 00:41:11 +00002002 # Ensure that utcoffset() gets ignored if the comparands have
2003 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002004 class OperandDependentOffset(tzinfo):
2005 def utcoffset(self, t):
2006 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002007 # d0 and d1 equal after adjustment
2008 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002009 else:
Tim Peters397301e2003-01-02 21:28:08 +00002010 # d2 off in the weeds
2011 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002012
2013 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2014 d0 = base.replace(minute=3)
2015 d1 = base.replace(minute=9)
2016 d2 = base.replace(minute=11)
2017 for x in d0, d1, d2:
2018 for y in d0, d1, d2:
2019 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002020 expected = cmp(x.minute, y.minute)
2021 self.assertEqual(got, expected)
2022
2023 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002024 # Note that a time can't actually have an operand-depedent offset,
2025 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2026 # so skip this test for time.
2027 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002028 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2029 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2030 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2031 for x in d0, d1, d2:
2032 for y in d0, d1, d2:
2033 got = cmp(x, y)
2034 if (x is d0 or x is d1) and (y is d0 or y is d1):
2035 expected = 0
2036 elif x is y is d2:
2037 expected = 0
2038 elif x is d2:
2039 expected = -1
2040 else:
2041 assert y is d2
2042 expected = 1
2043 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002044
Tim Peters855fe882002-12-22 03:43:39 +00002045
Tim Peters0bf60bd2003-01-08 20:40:01 +00002046# Testing time objects with a non-None tzinfo.
Tim Peters855fe882002-12-22 03:43:39 +00002047class TestTimeTZ(TestTime, TZInfoBase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002048 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002049
2050 def test_empty(self):
2051 t = self.theclass()
2052 self.assertEqual(t.hour, 0)
2053 self.assertEqual(t.minute, 0)
2054 self.assertEqual(t.second, 0)
2055 self.assertEqual(t.microsecond, 0)
2056 self.failUnless(t.tzinfo is None)
2057
Tim Peters2a799bf2002-12-16 20:18:38 +00002058 def test_zones(self):
2059 est = FixedOffset(-300, "EST", 1)
2060 utc = FixedOffset(0, "UTC", -2)
2061 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002062 t1 = time( 7, 47, tzinfo=est)
2063 t2 = time(12, 47, tzinfo=utc)
2064 t3 = time(13, 47, tzinfo=met)
2065 t4 = time(microsecond=40)
2066 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002067
2068 self.assertEqual(t1.tzinfo, est)
2069 self.assertEqual(t2.tzinfo, utc)
2070 self.assertEqual(t3.tzinfo, met)
2071 self.failUnless(t4.tzinfo is None)
2072 self.assertEqual(t5.tzinfo, utc)
2073
Tim Peters855fe882002-12-22 03:43:39 +00002074 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2075 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2076 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002077 self.failUnless(t4.utcoffset() is None)
2078 self.assertRaises(TypeError, t1.utcoffset, "no args")
2079
2080 self.assertEqual(t1.tzname(), "EST")
2081 self.assertEqual(t2.tzname(), "UTC")
2082 self.assertEqual(t3.tzname(), "MET")
2083 self.failUnless(t4.tzname() is None)
2084 self.assertRaises(TypeError, t1.tzname, "no args")
2085
Tim Peters855fe882002-12-22 03:43:39 +00002086 self.assertEqual(t1.dst(), timedelta(minutes=1))
2087 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2088 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002089 self.failUnless(t4.dst() is None)
2090 self.assertRaises(TypeError, t1.dst, "no args")
2091
2092 self.assertEqual(hash(t1), hash(t2))
2093 self.assertEqual(hash(t1), hash(t3))
2094 self.assertEqual(hash(t2), hash(t3))
2095
2096 self.assertEqual(t1, t2)
2097 self.assertEqual(t1, t3)
2098 self.assertEqual(t2, t3)
2099 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2100 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2101 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2102
2103 self.assertEqual(str(t1), "07:47:00-05:00")
2104 self.assertEqual(str(t2), "12:47:00+00:00")
2105 self.assertEqual(str(t3), "13:47:00+01:00")
2106 self.assertEqual(str(t4), "00:00:00.000040")
2107 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2108
2109 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2110 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2111 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2112 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2113 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2114
Tim Peters0bf60bd2003-01-08 20:40:01 +00002115 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002116 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2117 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2118 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2119 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2120 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2121
2122 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2123 "07:47:00 %Z=EST %z=-0500")
2124 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2125 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2126
2127 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002128 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002129 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2130 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2131
Tim Petersb92bb712002-12-21 17:44:07 +00002132 # Check that an invalid tzname result raises an exception.
2133 class Badtzname(tzinfo):
2134 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002135 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002136 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2137 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002138
2139 def test_hash_edge_cases(self):
2140 # Offsets that overflow a basic time.
2141 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2142 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2143 self.assertEqual(hash(t1), hash(t2))
2144
2145 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2146 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2147 self.assertEqual(hash(t1), hash(t2))
2148
Tim Peters2a799bf2002-12-16 20:18:38 +00002149 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002150 # Try one without a tzinfo.
2151 args = 20, 59, 16, 64**2
2152 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002153 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002154 green = pickler.dumps(orig, proto)
2155 derived = unpickler.loads(green)
2156 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002157
2158 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002159 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002160 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002161 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002162 green = pickler.dumps(orig, proto)
2163 derived = unpickler.loads(green)
2164 self.assertEqual(orig, derived)
2165 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2166 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2167 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002168
2169 def test_more_bool(self):
2170 # Test cases with non-None tzinfo.
2171 cls = self.theclass
2172
2173 t = cls(0, tzinfo=FixedOffset(-300, ""))
2174 self.failUnless(t)
2175
2176 t = cls(5, tzinfo=FixedOffset(-300, ""))
2177 self.failUnless(t)
2178
2179 t = cls(5, tzinfo=FixedOffset(300, ""))
2180 self.failUnless(not t)
2181
2182 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2183 self.failUnless(not t)
2184
2185 # Mostly ensuring this doesn't overflow internally.
2186 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2187 self.failUnless(t)
2188
2189 # But this should yield a value error -- the utcoffset is bogus.
2190 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2191 self.assertRaises(ValueError, lambda: bool(t))
2192
2193 # Likewise.
2194 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2195 self.assertRaises(ValueError, lambda: bool(t))
2196
Tim Peters12bf3392002-12-24 05:41:27 +00002197 def test_replace(self):
2198 cls = self.theclass
2199 z100 = FixedOffset(100, "+100")
2200 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2201 args = [1, 2, 3, 4, z100]
2202 base = cls(*args)
2203 self.assertEqual(base, base.replace())
2204
2205 i = 0
2206 for name, newval in (("hour", 5),
2207 ("minute", 6),
2208 ("second", 7),
2209 ("microsecond", 8),
2210 ("tzinfo", zm200)):
2211 newargs = args[:]
2212 newargs[i] = newval
2213 expected = cls(*newargs)
2214 got = base.replace(**{name: newval})
2215 self.assertEqual(expected, got)
2216 i += 1
2217
2218 # Ensure we can get rid of a tzinfo.
2219 self.assertEqual(base.tzname(), "+100")
2220 base2 = base.replace(tzinfo=None)
2221 self.failUnless(base2.tzinfo is None)
2222 self.failUnless(base2.tzname() is None)
2223
2224 # Ensure we can add one.
2225 base3 = base2.replace(tzinfo=z100)
2226 self.assertEqual(base, base3)
2227 self.failUnless(base.tzinfo is base3.tzinfo)
2228
2229 # Out of bounds.
2230 base = cls(1)
2231 self.assertRaises(ValueError, base.replace, hour=24)
2232 self.assertRaises(ValueError, base.replace, minute=-1)
2233 self.assertRaises(ValueError, base.replace, second=100)
2234 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2235
Tim Peters60c76e42002-12-27 00:41:11 +00002236 def test_mixed_compare(self):
2237 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002238 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002239 self.assertEqual(t1, t2)
2240 t2 = t2.replace(tzinfo=None)
2241 self.assertEqual(t1, t2)
2242 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2243 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002244 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2245 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002246
Tim Peters0bf60bd2003-01-08 20:40:01 +00002247 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002248 class Varies(tzinfo):
2249 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002250 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002251 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002252 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002253 return self.offset
2254
2255 v = Varies()
2256 t1 = t2.replace(tzinfo=v)
2257 t2 = t2.replace(tzinfo=v)
2258 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2259 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2260 self.assertEqual(t1, t2)
2261
2262 # But if they're not identical, it isn't ignored.
2263 t2 = t2.replace(tzinfo=Varies())
2264 self.failUnless(t1 < t2) # t1's offset counter still going up
2265
Tim Petersa98924a2003-05-17 05:55:19 +00002266 def test_subclass_timetz(self):
2267
2268 class C(self.theclass):
2269 theAnswer = 42
2270
2271 def __new__(cls, *args, **kws):
2272 temp = kws.copy()
2273 extra = temp.pop('extra')
2274 result = self.theclass.__new__(cls, *args, **temp)
2275 result.extra = extra
2276 return result
2277
2278 def newmeth(self, start):
2279 return start + self.hour + self.second
2280
2281 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2282
2283 dt1 = self.theclass(*args)
2284 dt2 = C(*args, **{'extra': 7})
2285
2286 self.assertEqual(dt2.__class__, C)
2287 self.assertEqual(dt2.theAnswer, 42)
2288 self.assertEqual(dt2.extra, 7)
2289 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2290 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2291
Tim Peters4c0db782002-12-26 05:01:19 +00002292
Tim Peters0bf60bd2003-01-08 20:40:01 +00002293# Testing datetime objects with a non-None tzinfo.
2294
Tim Peters855fe882002-12-22 03:43:39 +00002295class TestDateTimeTZ(TestDateTime, TZInfoBase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002296 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002297
2298 def test_trivial(self):
2299 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2300 self.assertEqual(dt.year, 1)
2301 self.assertEqual(dt.month, 2)
2302 self.assertEqual(dt.day, 3)
2303 self.assertEqual(dt.hour, 4)
2304 self.assertEqual(dt.minute, 5)
2305 self.assertEqual(dt.second, 6)
2306 self.assertEqual(dt.microsecond, 7)
2307 self.assertEqual(dt.tzinfo, None)
2308
2309 def test_even_more_compare(self):
2310 # The test_compare() and test_more_compare() inherited from TestDate
2311 # and TestDateTime covered non-tzinfo cases.
2312
2313 # Smallest possible after UTC adjustment.
2314 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2315 # Largest possible after UTC adjustment.
2316 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2317 tzinfo=FixedOffset(-1439, ""))
2318
2319 # Make sure those compare correctly, and w/o overflow.
2320 self.failUnless(t1 < t2)
2321 self.failUnless(t1 != t2)
2322 self.failUnless(t2 > t1)
2323
2324 self.failUnless(t1 == t1)
2325 self.failUnless(t2 == t2)
2326
2327 # Equal afer adjustment.
2328 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2329 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2330 self.assertEqual(t1, t2)
2331
2332 # Change t1 not to subtract a minute, and t1 should be larger.
2333 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2334 self.failUnless(t1 > t2)
2335
2336 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2337 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2338 self.failUnless(t1 < t2)
2339
2340 # Back to the original t1, but make seconds resolve it.
2341 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2342 second=1)
2343 self.failUnless(t1 > t2)
2344
2345 # Likewise, but make microseconds resolve it.
2346 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2347 microsecond=1)
2348 self.failUnless(t1 > t2)
2349
2350 # Make t2 naive and it should fail.
2351 t2 = self.theclass.min
2352 self.assertRaises(TypeError, lambda: t1 == t2)
2353 self.assertEqual(t2, t2)
2354
2355 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2356 class Naive(tzinfo):
2357 def utcoffset(self, dt): return None
2358 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2359 self.assertRaises(TypeError, lambda: t1 == t2)
2360 self.assertEqual(t2, t2)
2361
2362 # OTOH, it's OK to compare two of these mixing the two ways of being
2363 # naive.
2364 t1 = self.theclass(5, 6, 7)
2365 self.assertEqual(t1, t2)
2366
2367 # Try a bogus uctoffset.
2368 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002369 def utcoffset(self, dt):
2370 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002371 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2372 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002373 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002374
Tim Peters2a799bf2002-12-16 20:18:38 +00002375 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002376 # Try one without a tzinfo.
2377 args = 6, 7, 23, 20, 59, 1, 64**2
2378 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002379 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002380 green = pickler.dumps(orig, proto)
2381 derived = unpickler.loads(green)
2382 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002383
2384 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002385 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002386 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002387 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002388 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002389 green = pickler.dumps(orig, proto)
2390 derived = unpickler.loads(green)
2391 self.assertEqual(orig, derived)
2392 self.failUnless(isinstance(derived.tzinfo,
2393 PicklableFixedOffset))
2394 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2395 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002396
2397 def test_extreme_hashes(self):
2398 # If an attempt is made to hash these via subtracting the offset
2399 # then hashing a datetime object, OverflowError results. The
2400 # Python implementation used to blow up here.
2401 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2402 hash(t)
2403 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2404 tzinfo=FixedOffset(-1439, ""))
2405 hash(t)
2406
2407 # OTOH, an OOB offset should blow up.
2408 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2409 self.assertRaises(ValueError, hash, t)
2410
2411 def test_zones(self):
2412 est = FixedOffset(-300, "EST")
2413 utc = FixedOffset(0, "UTC")
2414 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002415 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2416 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2417 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002418 self.assertEqual(t1.tzinfo, est)
2419 self.assertEqual(t2.tzinfo, utc)
2420 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002421 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2422 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2423 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002424 self.assertEqual(t1.tzname(), "EST")
2425 self.assertEqual(t2.tzname(), "UTC")
2426 self.assertEqual(t3.tzname(), "MET")
2427 self.assertEqual(hash(t1), hash(t2))
2428 self.assertEqual(hash(t1), hash(t3))
2429 self.assertEqual(hash(t2), hash(t3))
2430 self.assertEqual(t1, t2)
2431 self.assertEqual(t1, t3)
2432 self.assertEqual(t2, t3)
2433 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2434 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2435 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002436 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002437 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2438 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2439 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2440
2441 def test_combine(self):
2442 met = FixedOffset(60, "MET")
2443 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002444 tz = time(18, 45, 3, 1234, tzinfo=met)
2445 dt = datetime.combine(d, tz)
2446 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002447 tzinfo=met))
2448
2449 def test_extract(self):
2450 met = FixedOffset(60, "MET")
2451 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2452 self.assertEqual(dt.date(), date(2002, 3, 4))
2453 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002454 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002455
2456 def test_tz_aware_arithmetic(self):
2457 import random
2458
2459 now = self.theclass.now()
2460 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002461 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002462 nowaware = self.theclass.combine(now.date(), timeaware)
2463 self.failUnless(nowaware.tzinfo is tz55)
2464 self.assertEqual(nowaware.timetz(), timeaware)
2465
2466 # Can't mix aware and non-aware.
2467 self.assertRaises(TypeError, lambda: now - nowaware)
2468 self.assertRaises(TypeError, lambda: nowaware - now)
2469
Tim Peters0bf60bd2003-01-08 20:40:01 +00002470 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002471 self.assertRaises(TypeError, lambda: now + nowaware)
2472 self.assertRaises(TypeError, lambda: nowaware + now)
2473 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2474
2475 # Subtracting should yield 0.
2476 self.assertEqual(now - now, timedelta(0))
2477 self.assertEqual(nowaware - nowaware, timedelta(0))
2478
2479 # Adding a delta should preserve tzinfo.
2480 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2481 nowawareplus = nowaware + delta
2482 self.failUnless(nowaware.tzinfo is tz55)
2483 nowawareplus2 = delta + nowaware
2484 self.failUnless(nowawareplus2.tzinfo is tz55)
2485 self.assertEqual(nowawareplus, nowawareplus2)
2486
2487 # that - delta should be what we started with, and that - what we
2488 # started with should be delta.
2489 diff = nowawareplus - delta
2490 self.failUnless(diff.tzinfo is tz55)
2491 self.assertEqual(nowaware, diff)
2492 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2493 self.assertEqual(nowawareplus - nowaware, delta)
2494
2495 # Make up a random timezone.
2496 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002497 # Attach it to nowawareplus.
2498 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002499 self.failUnless(nowawareplus.tzinfo is tzr)
2500 # Make sure the difference takes the timezone adjustments into account.
2501 got = nowaware - nowawareplus
2502 # Expected: (nowaware base - nowaware offset) -
2503 # (nowawareplus base - nowawareplus offset) =
2504 # (nowaware base - nowawareplus base) +
2505 # (nowawareplus offset - nowaware offset) =
2506 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002507 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002508 self.assertEqual(got, expected)
2509
2510 # Try max possible difference.
2511 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2512 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2513 tzinfo=FixedOffset(-1439, "max"))
2514 maxdiff = max - min
2515 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2516 timedelta(minutes=2*1439))
2517
2518 def test_tzinfo_now(self):
2519 meth = self.theclass.now
2520 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2521 base = meth()
2522 # Try with and without naming the keyword.
2523 off42 = FixedOffset(42, "42")
2524 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002525 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002526 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002527 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002528 # Bad argument with and w/o naming the keyword.
2529 self.assertRaises(TypeError, meth, 16)
2530 self.assertRaises(TypeError, meth, tzinfo=16)
2531 # Bad keyword name.
2532 self.assertRaises(TypeError, meth, tinfo=off42)
2533 # Too many args.
2534 self.assertRaises(TypeError, meth, off42, off42)
2535
Tim Peters10cadce2003-01-23 19:58:02 +00002536 # We don't know which time zone we're in, and don't have a tzinfo
2537 # class to represent it, so seeing whether a tz argument actually
2538 # does a conversion is tricky.
2539 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2540 utc = FixedOffset(0, "utc", 0)
2541 for dummy in range(3):
2542 now = datetime.now(weirdtz)
2543 self.failUnless(now.tzinfo is weirdtz)
2544 utcnow = datetime.utcnow().replace(tzinfo=utc)
2545 now2 = utcnow.astimezone(weirdtz)
2546 if abs(now - now2) < timedelta(seconds=30):
2547 break
2548 # Else the code is broken, or more than 30 seconds passed between
2549 # calls; assuming the latter, just try again.
2550 else:
2551 # Three strikes and we're out.
2552 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2553
Tim Peters2a799bf2002-12-16 20:18:38 +00002554 def test_tzinfo_fromtimestamp(self):
2555 import time
2556 meth = self.theclass.fromtimestamp
2557 ts = time.time()
2558 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2559 base = meth(ts)
2560 # Try with and without naming the keyword.
2561 off42 = FixedOffset(42, "42")
2562 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002563 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002564 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002565 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002566 # Bad argument with and w/o naming the keyword.
2567 self.assertRaises(TypeError, meth, ts, 16)
2568 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2569 # Bad keyword name.
2570 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2571 # Too many args.
2572 self.assertRaises(TypeError, meth, ts, off42, off42)
2573 # Too few args.
2574 self.assertRaises(TypeError, meth)
2575
Tim Peters2a44a8d2003-01-23 20:53:10 +00002576 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002577 timestamp = 1000000000
2578 utcdatetime = datetime.utcfromtimestamp(timestamp)
2579 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2580 # But on some flavor of Mac, it's nowhere near that. So we can't have
2581 # any idea here what time that actually is, we can only test that
2582 # relative changes match.
2583 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2584 tz = FixedOffset(utcoffset, "tz", 0)
2585 expected = utcdatetime + utcoffset
2586 got = datetime.fromtimestamp(timestamp, tz)
2587 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002588
Tim Peters2a799bf2002-12-16 20:18:38 +00002589 def test_tzinfo_utcnow(self):
2590 meth = self.theclass.utcnow
2591 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2592 base = meth()
2593 # Try with and without naming the keyword; for whatever reason,
2594 # utcnow() doesn't accept a tzinfo argument.
2595 off42 = FixedOffset(42, "42")
2596 self.assertRaises(TypeError, meth, off42)
2597 self.assertRaises(TypeError, meth, tzinfo=off42)
2598
2599 def test_tzinfo_utcfromtimestamp(self):
2600 import time
2601 meth = self.theclass.utcfromtimestamp
2602 ts = time.time()
2603 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2604 base = meth(ts)
2605 # Try with and without naming the keyword; for whatever reason,
2606 # utcfromtimestamp() doesn't accept a tzinfo argument.
2607 off42 = FixedOffset(42, "42")
2608 self.assertRaises(TypeError, meth, ts, off42)
2609 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2610
2611 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002612 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002613 # DST flag.
2614 class DST(tzinfo):
2615 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002616 if isinstance(dstvalue, int):
2617 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002618 self.dstvalue = dstvalue
2619 def dst(self, dt):
2620 return self.dstvalue
2621
2622 cls = self.theclass
2623 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2624 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2625 t = d.timetuple()
2626 self.assertEqual(1, t.tm_year)
2627 self.assertEqual(1, t.tm_mon)
2628 self.assertEqual(1, t.tm_mday)
2629 self.assertEqual(10, t.tm_hour)
2630 self.assertEqual(20, t.tm_min)
2631 self.assertEqual(30, t.tm_sec)
2632 self.assertEqual(0, t.tm_wday)
2633 self.assertEqual(1, t.tm_yday)
2634 self.assertEqual(flag, t.tm_isdst)
2635
2636 # dst() returns wrong type.
2637 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2638
2639 # dst() at the edge.
2640 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2641 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2642
2643 # dst() out of range.
2644 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2645 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2646
2647 def test_utctimetuple(self):
2648 class DST(tzinfo):
2649 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002650 if isinstance(dstvalue, int):
2651 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002652 self.dstvalue = dstvalue
2653 def dst(self, dt):
2654 return self.dstvalue
2655
2656 cls = self.theclass
2657 # This can't work: DST didn't implement utcoffset.
2658 self.assertRaises(NotImplementedError,
2659 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2660
2661 class UOFS(DST):
2662 def __init__(self, uofs, dofs=None):
2663 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002664 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002665 def utcoffset(self, dt):
2666 return self.uofs
2667
2668 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2669 # in effect for a UTC time.
2670 for dstvalue in -33, 33, 0, None:
2671 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2672 t = d.utctimetuple()
2673 self.assertEqual(d.year, t.tm_year)
2674 self.assertEqual(d.month, t.tm_mon)
2675 self.assertEqual(d.day, t.tm_mday)
2676 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2677 self.assertEqual(13, t.tm_min)
2678 self.assertEqual(d.second, t.tm_sec)
2679 self.assertEqual(d.weekday(), t.tm_wday)
2680 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2681 t.tm_yday)
2682 self.assertEqual(0, t.tm_isdst)
2683
2684 # At the edges, UTC adjustment can normalize into years out-of-range
2685 # for a datetime object. Ensure that a correct timetuple is
2686 # created anyway.
2687 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2688 # That goes back 1 minute less than a full day.
2689 t = tiny.utctimetuple()
2690 self.assertEqual(t.tm_year, MINYEAR-1)
2691 self.assertEqual(t.tm_mon, 12)
2692 self.assertEqual(t.tm_mday, 31)
2693 self.assertEqual(t.tm_hour, 0)
2694 self.assertEqual(t.tm_min, 1)
2695 self.assertEqual(t.tm_sec, 37)
2696 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2697 self.assertEqual(t.tm_isdst, 0)
2698
2699 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2700 # That goes forward 1 minute less than a full day.
2701 t = huge.utctimetuple()
2702 self.assertEqual(t.tm_year, MAXYEAR+1)
2703 self.assertEqual(t.tm_mon, 1)
2704 self.assertEqual(t.tm_mday, 1)
2705 self.assertEqual(t.tm_hour, 23)
2706 self.assertEqual(t.tm_min, 58)
2707 self.assertEqual(t.tm_sec, 37)
2708 self.assertEqual(t.tm_yday, 1)
2709 self.assertEqual(t.tm_isdst, 0)
2710
2711 def test_tzinfo_isoformat(self):
2712 zero = FixedOffset(0, "+00:00")
2713 plus = FixedOffset(220, "+03:40")
2714 minus = FixedOffset(-231, "-03:51")
2715 unknown = FixedOffset(None, "")
2716
2717 cls = self.theclass
2718 datestr = '0001-02-03'
2719 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002720 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002721 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2722 timestr = '04:05:59' + (us and '.987001' or '')
2723 ofsstr = ofs is not None and d.tzname() or ''
2724 tailstr = timestr + ofsstr
2725 iso = d.isoformat()
2726 self.assertEqual(iso, datestr + 'T' + tailstr)
2727 self.assertEqual(iso, d.isoformat('T'))
2728 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2729 self.assertEqual(str(d), datestr + ' ' + tailstr)
2730
Tim Peters12bf3392002-12-24 05:41:27 +00002731 def test_replace(self):
2732 cls = self.theclass
2733 z100 = FixedOffset(100, "+100")
2734 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2735 args = [1, 2, 3, 4, 5, 6, 7, z100]
2736 base = cls(*args)
2737 self.assertEqual(base, base.replace())
2738
2739 i = 0
2740 for name, newval in (("year", 2),
2741 ("month", 3),
2742 ("day", 4),
2743 ("hour", 5),
2744 ("minute", 6),
2745 ("second", 7),
2746 ("microsecond", 8),
2747 ("tzinfo", zm200)):
2748 newargs = args[:]
2749 newargs[i] = newval
2750 expected = cls(*newargs)
2751 got = base.replace(**{name: newval})
2752 self.assertEqual(expected, got)
2753 i += 1
2754
2755 # Ensure we can get rid of a tzinfo.
2756 self.assertEqual(base.tzname(), "+100")
2757 base2 = base.replace(tzinfo=None)
2758 self.failUnless(base2.tzinfo is None)
2759 self.failUnless(base2.tzname() is None)
2760
2761 # Ensure we can add one.
2762 base3 = base2.replace(tzinfo=z100)
2763 self.assertEqual(base, base3)
2764 self.failUnless(base.tzinfo is base3.tzinfo)
2765
2766 # Out of bounds.
2767 base = cls(2000, 2, 29)
2768 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002769
Tim Peters80475bb2002-12-25 07:40:55 +00002770 def test_more_astimezone(self):
2771 # The inherited test_astimezone covered some trivial and error cases.
2772 fnone = FixedOffset(None, "None")
2773 f44m = FixedOffset(44, "44")
2774 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2775
Tim Peters10cadce2003-01-23 19:58:02 +00002776 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002777 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002778 # Replacing with degenerate tzinfo raises an exception.
2779 self.assertRaises(ValueError, dt.astimezone, fnone)
2780 # Ditto with None tz.
2781 self.assertRaises(TypeError, dt.astimezone, None)
2782 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002783 x = dt.astimezone(dt.tzinfo)
2784 self.failUnless(x.tzinfo is f44m)
2785 self.assertEqual(x.date(), dt.date())
2786 self.assertEqual(x.time(), dt.time())
2787
2788 # Replacing with different tzinfo does adjust.
2789 got = dt.astimezone(fm5h)
2790 self.failUnless(got.tzinfo is fm5h)
2791 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2792 expected = dt - dt.utcoffset() # in effect, convert to UTC
2793 expected += fm5h.utcoffset(dt) # and from there to local time
2794 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2795 self.assertEqual(got.date(), expected.date())
2796 self.assertEqual(got.time(), expected.time())
2797 self.assertEqual(got.timetz(), expected.timetz())
2798 self.failUnless(got.tzinfo is expected.tzinfo)
2799 self.assertEqual(got, expected)
2800
Tim Peters4c0db782002-12-26 05:01:19 +00002801 def test_aware_subtract(self):
2802 cls = self.theclass
2803
Tim Peters60c76e42002-12-27 00:41:11 +00002804 # Ensure that utcoffset() is ignored when the operands have the
2805 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002806 class OperandDependentOffset(tzinfo):
2807 def utcoffset(self, t):
2808 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002809 # d0 and d1 equal after adjustment
2810 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002811 else:
Tim Peters397301e2003-01-02 21:28:08 +00002812 # d2 off in the weeds
2813 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002814
2815 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2816 d0 = base.replace(minute=3)
2817 d1 = base.replace(minute=9)
2818 d2 = base.replace(minute=11)
2819 for x in d0, d1, d2:
2820 for y in d0, d1, d2:
2821 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002822 expected = timedelta(minutes=x.minute - y.minute)
2823 self.assertEqual(got, expected)
2824
2825 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2826 # ignored.
2827 base = cls(8, 9, 10, 11, 12, 13, 14)
2828 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2829 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2830 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2831 for x in d0, d1, d2:
2832 for y in d0, d1, d2:
2833 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002834 if (x is d0 or x is d1) and (y is d0 or y is d1):
2835 expected = timedelta(0)
2836 elif x is y is d2:
2837 expected = timedelta(0)
2838 elif x is d2:
2839 expected = timedelta(minutes=(11-59)-0)
2840 else:
2841 assert y is d2
2842 expected = timedelta(minutes=0-(11-59))
2843 self.assertEqual(got, expected)
2844
Tim Peters60c76e42002-12-27 00:41:11 +00002845 def test_mixed_compare(self):
2846 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002847 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002848 self.assertEqual(t1, t2)
2849 t2 = t2.replace(tzinfo=None)
2850 self.assertEqual(t1, t2)
2851 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2852 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002853 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2854 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002855
Tim Peters0bf60bd2003-01-08 20:40:01 +00002856 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002857 class Varies(tzinfo):
2858 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002859 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002860 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002861 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002862 return self.offset
2863
2864 v = Varies()
2865 t1 = t2.replace(tzinfo=v)
2866 t2 = t2.replace(tzinfo=v)
2867 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2868 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2869 self.assertEqual(t1, t2)
2870
2871 # But if they're not identical, it isn't ignored.
2872 t2 = t2.replace(tzinfo=Varies())
2873 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002874
Tim Petersa98924a2003-05-17 05:55:19 +00002875 def test_subclass_datetimetz(self):
2876
2877 class C(self.theclass):
2878 theAnswer = 42
2879
2880 def __new__(cls, *args, **kws):
2881 temp = kws.copy()
2882 extra = temp.pop('extra')
2883 result = self.theclass.__new__(cls, *args, **temp)
2884 result.extra = extra
2885 return result
2886
2887 def newmeth(self, start):
2888 return start + self.hour + self.year
2889
2890 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2891
2892 dt1 = self.theclass(*args)
2893 dt2 = C(*args, **{'extra': 7})
2894
2895 self.assertEqual(dt2.__class__, C)
2896 self.assertEqual(dt2.theAnswer, 42)
2897 self.assertEqual(dt2.extra, 7)
2898 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2899 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2900
Tim Peters621818b2002-12-29 23:44:49 +00002901# Pain to set up DST-aware tzinfo classes.
2902
2903def first_sunday_on_or_after(dt):
2904 days_to_go = 6 - dt.weekday()
2905 if days_to_go:
2906 dt += timedelta(days_to_go)
2907 return dt
2908
2909ZERO = timedelta(0)
2910HOUR = timedelta(hours=1)
2911DAY = timedelta(days=1)
2912# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2913DSTSTART = datetime(1, 4, 1, 2)
2914# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002915# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2916# being standard time on that day, there is no spelling in local time of
2917# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2918DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002919
2920class USTimeZone(tzinfo):
2921
2922 def __init__(self, hours, reprname, stdname, dstname):
2923 self.stdoffset = timedelta(hours=hours)
2924 self.reprname = reprname
2925 self.stdname = stdname
2926 self.dstname = dstname
2927
2928 def __repr__(self):
2929 return self.reprname
2930
2931 def tzname(self, dt):
2932 if self.dst(dt):
2933 return self.dstname
2934 else:
2935 return self.stdname
2936
2937 def utcoffset(self, dt):
2938 return self.stdoffset + self.dst(dt)
2939
2940 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00002941 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00002942 # An exception instead may be sensible here, in one or more of
2943 # the cases.
2944 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00002945 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00002946
2947 # Find first Sunday in April.
2948 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
2949 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
2950
2951 # Find last Sunday in October.
2952 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
2953 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
2954
Tim Peters621818b2002-12-29 23:44:49 +00002955 # Can't compare naive to aware objects, so strip the timezone from
2956 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00002957 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00002958 return HOUR
2959 else:
2960 return ZERO
2961
Tim Peters521fc152002-12-31 17:36:56 +00002962Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
2963Central = USTimeZone(-6, "Central", "CST", "CDT")
2964Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
2965Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00002966utc_real = FixedOffset(0, "UTC", 0)
2967# For better test coverage, we want another flavor of UTC that's west of
2968# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00002969utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00002970
2971class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00002972 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002973 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00002974 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002975
Tim Peters0bf60bd2003-01-08 20:40:01 +00002976 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00002977
Tim Peters521fc152002-12-31 17:36:56 +00002978 # Check a time that's inside DST.
2979 def checkinside(self, dt, tz, utc, dston, dstoff):
2980 self.assertEqual(dt.dst(), HOUR)
2981
2982 # Conversion to our own timezone is always an identity.
2983 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00002984
2985 asutc = dt.astimezone(utc)
2986 there_and_back = asutc.astimezone(tz)
2987
2988 # Conversion to UTC and back isn't always an identity here,
2989 # because there are redundant spellings (in local time) of
2990 # UTC time when DST begins: the clock jumps from 1:59:59
2991 # to 3:00:00, and a local time of 2:MM:SS doesn't really
2992 # make sense then. The classes above treat 2:MM:SS as
2993 # daylight time then (it's "after 2am"), really an alias
2994 # for 1:MM:SS standard time. The latter form is what
2995 # conversion back from UTC produces.
2996 if dt.date() == dston.date() and dt.hour == 2:
2997 # We're in the redundant hour, and coming back from
2998 # UTC gives the 1:MM:SS standard-time spelling.
2999 self.assertEqual(there_and_back + HOUR, dt)
3000 # Although during was considered to be in daylight
3001 # time, there_and_back is not.
3002 self.assertEqual(there_and_back.dst(), ZERO)
3003 # They're the same times in UTC.
3004 self.assertEqual(there_and_back.astimezone(utc),
3005 dt.astimezone(utc))
3006 else:
3007 # We're not in the redundant hour.
3008 self.assertEqual(dt, there_and_back)
3009
Tim Peters327098a2003-01-20 22:54:38 +00003010 # Because we have a redundant spelling when DST begins, there is
3011 # (unforunately) an hour when DST ends that can't be spelled at all in
3012 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3013 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3014 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3015 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3016 # expressed in local time. Nevertheless, we want conversion back
3017 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003018 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003019 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003020 if dt.date() == dstoff.date() and dt.hour == 0:
3021 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003022 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003023 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3024 nexthour_utc += HOUR
3025 nexthour_tz = nexthour_utc.astimezone(tz)
3026 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003027 else:
Tim Peters327098a2003-01-20 22:54:38 +00003028 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003029
3030 # Check a time that's outside DST.
3031 def checkoutside(self, dt, tz, utc):
3032 self.assertEqual(dt.dst(), ZERO)
3033
3034 # Conversion to our own timezone is always an identity.
3035 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003036
3037 # Converting to UTC and back is an identity too.
3038 asutc = dt.astimezone(utc)
3039 there_and_back = asutc.astimezone(tz)
3040 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003041
Tim Peters1024bf82002-12-30 17:09:40 +00003042 def convert_between_tz_and_utc(self, tz, utc):
3043 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003044 # Because 1:MM on the day DST ends is taken as being standard time,
3045 # there is no spelling in tz for the last hour of daylight time.
3046 # For purposes of the test, the last hour of DST is 0:MM, which is
3047 # taken as being daylight time (and 1:MM is taken as being standard
3048 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003049 dstoff = self.dstoff.replace(tzinfo=tz)
3050 for delta in (timedelta(weeks=13),
3051 DAY,
3052 HOUR,
3053 timedelta(minutes=1),
3054 timedelta(microseconds=1)):
3055
Tim Peters521fc152002-12-31 17:36:56 +00003056 self.checkinside(dston, tz, utc, dston, dstoff)
3057 for during in dston + delta, dstoff - delta:
3058 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003059
Tim Peters521fc152002-12-31 17:36:56 +00003060 self.checkoutside(dstoff, tz, utc)
3061 for outside in dston - delta, dstoff + delta:
3062 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003063
Tim Peters621818b2002-12-29 23:44:49 +00003064 def test_easy(self):
3065 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003066 self.convert_between_tz_and_utc(Eastern, utc_real)
3067 self.convert_between_tz_and_utc(Pacific, utc_real)
3068 self.convert_between_tz_and_utc(Eastern, utc_fake)
3069 self.convert_between_tz_and_utc(Pacific, utc_fake)
3070 # The next is really dancing near the edge. It works because
3071 # Pacific and Eastern are far enough apart that their "problem
3072 # hours" don't overlap.
3073 self.convert_between_tz_and_utc(Eastern, Pacific)
3074 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003075 # OTOH, these fail! Don't enable them. The difficulty is that
3076 # the edge case tests assume that every hour is representable in
3077 # the "utc" class. This is always true for a fixed-offset tzinfo
3078 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3079 # For these adjacent DST-aware time zones, the range of time offsets
3080 # tested ends up creating hours in the one that aren't representable
3081 # in the other. For the same reason, we would see failures in the
3082 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3083 # offset deltas in convert_between_tz_and_utc().
3084 #
3085 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3086 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003087
Tim Petersf3615152003-01-01 21:51:37 +00003088 def test_tricky(self):
3089 # 22:00 on day before daylight starts.
3090 fourback = self.dston - timedelta(hours=4)
3091 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003092 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003093 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3094 # 2", we should get the 3 spelling.
3095 # If we plug 22:00 the day before into Eastern, it "looks like std
3096 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3097 # to 22:00 lands on 2:00, which makes no sense in local time (the
3098 # local clock jumps from 1 to 3). The point here is to make sure we
3099 # get the 3 spelling.
3100 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003101 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003102 self.assertEqual(expected, got)
3103
3104 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3105 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003106 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003107 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3108 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3109 # spelling.
3110 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003111 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003112 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003113
Tim Petersadf64202003-01-04 06:03:15 +00003114 # Now on the day DST ends, we want "repeat an hour" behavior.
3115 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3116 # EST 23:MM 0:MM 1:MM 2:MM
3117 # EDT 0:MM 1:MM 2:MM 3:MM
3118 # wall 0:MM 1:MM 1:MM 2:MM against these
3119 for utc in utc_real, utc_fake:
3120 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003121 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003122 # Convert that to UTC.
3123 first_std_hour -= tz.utcoffset(None)
3124 # Adjust for possibly fake UTC.
3125 asutc = first_std_hour + utc.utcoffset(None)
3126 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3127 # tz=Eastern.
3128 asutcbase = asutc.replace(tzinfo=utc)
3129 for tzhour in (0, 1, 1, 2):
3130 expectedbase = self.dstoff.replace(hour=tzhour)
3131 for minute in 0, 30, 59:
3132 expected = expectedbase.replace(minute=minute)
3133 asutc = asutcbase.replace(minute=minute)
3134 astz = asutc.astimezone(tz)
3135 self.assertEqual(astz.replace(tzinfo=None), expected)
3136 asutcbase += HOUR
3137
3138
Tim Peters710fb152003-01-02 19:35:54 +00003139 def test_bogus_dst(self):
3140 class ok(tzinfo):
3141 def utcoffset(self, dt): return HOUR
3142 def dst(self, dt): return HOUR
3143
3144 now = self.theclass.now().replace(tzinfo=utc_real)
3145 # Doesn't blow up.
3146 now.astimezone(ok())
3147
3148 # Does blow up.
3149 class notok(ok):
3150 def dst(self, dt): return None
3151 self.assertRaises(ValueError, now.astimezone, notok())
3152
Tim Peters52dcce22003-01-23 16:36:11 +00003153 def test_fromutc(self):
3154 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3155 now = datetime.utcnow().replace(tzinfo=utc_real)
3156 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3157 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3158 enow = Eastern.fromutc(now) # doesn't blow up
3159 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3160 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3161 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3162
3163 # Always converts UTC to standard time.
3164 class FauxUSTimeZone(USTimeZone):
3165 def fromutc(self, dt):
3166 return dt + self.stdoffset
3167 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3168
3169 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3170 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3171 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3172
3173 # Check around DST start.
3174 start = self.dston.replace(hour=4, tzinfo=Eastern)
3175 fstart = start.replace(tzinfo=FEastern)
3176 for wall in 23, 0, 1, 3, 4, 5:
3177 expected = start.replace(hour=wall)
3178 if wall == 23:
3179 expected -= timedelta(days=1)
3180 got = Eastern.fromutc(start)
3181 self.assertEqual(expected, got)
3182
3183 expected = fstart + FEastern.stdoffset
3184 got = FEastern.fromutc(fstart)
3185 self.assertEqual(expected, got)
3186
3187 # Ensure astimezone() calls fromutc() too.
3188 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3189 self.assertEqual(expected, got)
3190
3191 start += HOUR
3192 fstart += HOUR
3193
3194 # Check around DST end.
3195 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3196 fstart = start.replace(tzinfo=FEastern)
3197 for wall in 0, 1, 1, 2, 3, 4:
3198 expected = start.replace(hour=wall)
3199 got = Eastern.fromutc(start)
3200 self.assertEqual(expected, got)
3201
3202 expected = fstart + FEastern.stdoffset
3203 got = FEastern.fromutc(fstart)
3204 self.assertEqual(expected, got)
3205
3206 # Ensure astimezone() calls fromutc() too.
3207 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3208 self.assertEqual(expected, got)
3209
3210 start += HOUR
3211 fstart += HOUR
3212
Tim Peters710fb152003-01-02 19:35:54 +00003213
Tim Peters528ca532004-09-16 01:30:50 +00003214#############################################################################
3215# oddballs
3216
3217class Oddballs(unittest.TestCase):
3218
3219 def test_bug_1028306(self):
3220 # Trying to compare a date to a datetime should act like a mixed-
3221 # type comparison, despite that datetime is a subclass of date.
3222 as_date = date.today()
3223 as_datetime = datetime.combine(as_date, time())
3224 self.assert_(as_date != as_datetime)
3225 self.assert_(as_datetime != as_date)
3226 self.assert_(not as_date == as_datetime)
3227 self.assert_(not as_datetime == as_date)
3228 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3229 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3230 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3231 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3232 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3233 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3234 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3235 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3236
3237 # Neverthelss, comparison should work with the base-class (date)
3238 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003239 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003240 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003241 as_different = as_datetime.replace(day= different_day)
3242 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003243
3244 # And date should compare with other subclasses of date. If a
3245 # subclass wants to stop this, it's up to the subclass to do so.
3246 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3247 self.assertEqual(as_date, date_sc)
3248 self.assertEqual(date_sc, as_date)
3249
3250 # Ditto for datetimes.
3251 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3252 as_date.day, 0, 0, 0)
3253 self.assertEqual(as_datetime, datetime_sc)
3254 self.assertEqual(datetime_sc, as_datetime)
3255
Tim Peterscfd4a8b2002-12-16 21:12:37 +00003256def test_suite():
Tim Peters2a799bf2002-12-16 20:18:38 +00003257 allsuites = [unittest.makeSuite(klass, 'test')
3258 for klass in (TestModule,
3259 TestTZInfo,
3260 TestTimeDelta,
3261 TestDateOnly,
3262 TestDate,
3263 TestDateTime,
3264 TestTime,
3265 TestTimeTZ,
3266 TestDateTimeTZ,
Tim Peters621818b2002-12-29 23:44:49 +00003267 TestTimezoneConversions,
Tim Peters528ca532004-09-16 01:30:50 +00003268 Oddballs,
Tim Peters2a799bf2002-12-16 20:18:38 +00003269 )
3270 ]
3271 return unittest.TestSuite(allsuites)
3272
3273def test_main():
3274 import gc
3275 import sys
3276
Tim Peterscfd4a8b2002-12-16 21:12:37 +00003277 thesuite = test_suite()
Tim Peters2a799bf2002-12-16 20:18:38 +00003278 lastrc = None
3279 while True:
3280 test_support.run_suite(thesuite)
3281 if 1: # change to 0, under a debug build, for some leak detection
3282 break
3283 gc.collect()
3284 if gc.garbage:
3285 raise SystemError("gc.garbage not empty after test run: %r" %
3286 gc.garbage)
3287 if hasattr(sys, 'gettotalrefcount'):
3288 thisrc = sys.gettotalrefcount()
3289 print >> sys.stderr, '*' * 10, 'total refs:', thisrc,
3290 if lastrc:
3291 print >> sys.stderr, 'delta:', thisrc - lastrc
3292 else:
3293 print >> sys.stderr
3294 lastrc = thisrc
3295
3296if __name__ == "__main__":
3297 test_main()