blob: f383d7be1091fd697b341dd021e3036c32c0ae15 [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
8import cPickle
Tim Peters2a799bf2002-12-16 20:18:38 +00009import unittest
10
11from test import test_support
12
13from datetime import MINYEAR, MAXYEAR
14from datetime import timedelta
15from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000016from datetime import time
17from datetime import date, datetime
18
19
Guido van Rossum177e41a2003-01-30 22:06:23 +000020pickle_choices = [
21 (pickle, pickle, 0),
22 (pickle, pickle, 1),
23 (pickle, pickle, 2),
24 (cPickle, cPickle, 0),
25 (cPickle, cPickle, 1),
26## (cPickle, cPickle, 2),
27 (pickle, cPickle, 0),
28 (pickle, cPickle, 1),
29## (pickle, cPickle, 2),
30 (cPickle, pickle, 0),
31 (cPickle, pickle, 1),
32## (cPickle, pickle, 2),
33 ]
34
35
Tim Peters0bf60bd2003-01-08 20:40:01 +000036# XXX The test suite uncovered a bug in Python 2.2.2: if x and y are
37# XXX instances of new-style classes (like date and time) that both
38# XXX define __cmp__, and x is compared to y, and one of the __cmp__
39# XXX implementations raises an exception, the exception can get dropped
40# XXX on the floor when it occurs, and pop up again at some "random" time
41# XXX later (it depends on when the next opcode gets executed that
42# XXX bothers to check). There isn't a workaround for this, so instead
43# XXX we disable the parts of the tests that trigger it unless
44# XXX CMP_BUG_FIXED is true. The bug is still there, we simply avoid
45# XXX provoking it here.
46# XXX Guido checked into a fix that will go into 2.2.3. The bug was
47# XXX already fixed in 2.3 CVS via a different means.
48CMP_BUG_FIXED = sys.version_info >= (2, 2, 3)
49
Tim Peters2a799bf2002-12-16 20:18:38 +000050
51#############################################################################
52# module tests
53
54class TestModule(unittest.TestCase):
55
56 def test_constants(self):
57 import datetime
58 self.assertEqual(datetime.MINYEAR, 1)
59 self.assertEqual(datetime.MAXYEAR, 9999)
60
61#############################################################################
62# tzinfo tests
63
64class FixedOffset(tzinfo):
65 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000066 if isinstance(offset, int):
67 offset = timedelta(minutes=offset)
68 if isinstance(dstoffset, int):
69 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000070 self.__offset = offset
71 self.__name = name
72 self.__dstoffset = dstoffset
73 def __repr__(self):
74 return self.__name.lower()
75 def utcoffset(self, dt):
76 return self.__offset
77 def tzname(self, dt):
78 return self.__name
79 def dst(self, dt):
80 return self.__dstoffset
81
Tim Petersfb8472c2002-12-21 05:04:42 +000082class PicklableFixedOffset(FixedOffset):
83 def __init__(self, offset=None, name=None, dstoffset=None):
84 FixedOffset.__init__(self, offset, name, dstoffset)
85
Tim Peters2a799bf2002-12-16 20:18:38 +000086class TestTZInfo(unittest.TestCase):
87
88 def test_non_abstractness(self):
89 # In order to allow subclasses to get pickled, the C implementation
90 # wasn't able to get away with having __init__ raise
91 # NotImplementedError.
92 useless = tzinfo()
93 dt = datetime.max
94 self.assertRaises(NotImplementedError, useless.tzname, dt)
95 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
96 self.assertRaises(NotImplementedError, useless.dst, dt)
97
98 def test_subclass_must_override(self):
99 class NotEnough(tzinfo):
100 def __init__(self, offset, name):
101 self.__offset = offset
102 self.__name = name
103 self.failUnless(issubclass(NotEnough, tzinfo))
104 ne = NotEnough(3, "NotByALongShot")
105 self.failUnless(isinstance(ne, tzinfo))
106
107 dt = datetime.now()
108 self.assertRaises(NotImplementedError, ne.tzname, dt)
109 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
110 self.assertRaises(NotImplementedError, ne.dst, dt)
111
112 def test_normal(self):
113 fo = FixedOffset(3, "Three")
114 self.failUnless(isinstance(fo, tzinfo))
115 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000116 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000117 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000118 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000119
120 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000121 # There's no point to pickling tzinfo objects on their own (they
122 # carry no data), but they need to be picklable anyway else
123 # concrete subclasses can't be pickled.
124 orig = tzinfo.__new__(tzinfo)
125 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000126 for pickler, unpickler, proto in pickle_choices:
127 green = pickler.dumps(orig, proto)
128 derived = unpickler.loads(green)
Tim Peters2a799bf2002-12-16 20:18:38 +0000129 self.failUnless(type(derived) is tzinfo)
130
131 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000132 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000133 offset = timedelta(minutes=-300)
134 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000135 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000136 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000137 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000138 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000139 for pickler, unpickler, proto in pickle_choices:
140 green = pickler.dumps(orig, proto)
141 derived = unpickler.loads(green)
Tim Peters2a799bf2002-12-16 20:18:38 +0000142 self.failUnless(isinstance(derived, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000143 self.failUnless(type(derived) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000144 self.assertEqual(derived.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000145 self.assertEqual(derived.tzname(None), 'cookie')
146
147#############################################################################
148# timedelta tests
149
150class TestTimeDelta(unittest.TestCase):
151
152 def test_constructor(self):
153 eq = self.assertEqual
154 td = timedelta
155
156 # Check keyword args to constructor
157 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
158 milliseconds=0, microseconds=0))
159 eq(td(1), td(days=1))
160 eq(td(0, 1), td(seconds=1))
161 eq(td(0, 0, 1), td(microseconds=1))
162 eq(td(weeks=1), td(days=7))
163 eq(td(days=1), td(hours=24))
164 eq(td(hours=1), td(minutes=60))
165 eq(td(minutes=1), td(seconds=60))
166 eq(td(seconds=1), td(milliseconds=1000))
167 eq(td(milliseconds=1), td(microseconds=1000))
168
169 # Check float args to constructor
170 eq(td(weeks=1.0/7), td(days=1))
171 eq(td(days=1.0/24), td(hours=1))
172 eq(td(hours=1.0/60), td(minutes=1))
173 eq(td(minutes=1.0/60), td(seconds=1))
174 eq(td(seconds=0.001), td(milliseconds=1))
175 eq(td(milliseconds=0.001), td(microseconds=1))
176
177 def test_computations(self):
178 eq = self.assertEqual
179 td = timedelta
180
181 a = td(7) # One week
182 b = td(0, 60) # One minute
183 c = td(0, 0, 1000) # One millisecond
184 eq(a+b+c, td(7, 60, 1000))
185 eq(a-b, td(6, 24*3600 - 60))
186 eq(-a, td(-7))
187 eq(+a, td(7))
188 eq(-b, td(-1, 24*3600 - 60))
189 eq(-c, td(-1, 24*3600 - 1, 999000))
190 eq(abs(a), a)
191 eq(abs(-a), a)
192 eq(td(6, 24*3600), a)
193 eq(td(0, 0, 60*1000000), b)
194 eq(a*10, td(70))
195 eq(a*10, 10*a)
196 eq(a*10L, 10*a)
197 eq(b*10, td(0, 600))
198 eq(10*b, td(0, 600))
199 eq(b*10L, td(0, 600))
200 eq(c*10, td(0, 0, 10000))
201 eq(10*c, td(0, 0, 10000))
202 eq(c*10L, td(0, 0, 10000))
203 eq(a*-1, -a)
204 eq(b*-2, -b-b)
205 eq(c*-2, -c+-c)
206 eq(b*(60*24), (b*60)*24)
207 eq(b*(60*24), (60*b)*24)
208 eq(c*1000, td(0, 1))
209 eq(1000*c, td(0, 1))
210 eq(a//7, td(1))
211 eq(b//10, td(0, 6))
212 eq(c//1000, td(0, 0, 1))
213 eq(a//10, td(0, 7*24*360))
214 eq(a//3600000, td(0, 0, 7*24*1000))
215
216 def test_disallowed_computations(self):
217 a = timedelta(42)
218
219 # Add/sub ints, longs, floats should be illegal
220 for i in 1, 1L, 1.0:
221 self.assertRaises(TypeError, lambda: a+i)
222 self.assertRaises(TypeError, lambda: a-i)
223 self.assertRaises(TypeError, lambda: i+a)
224 self.assertRaises(TypeError, lambda: i-a)
225
226 # Mul/div by float isn't supported.
227 x = 2.3
228 self.assertRaises(TypeError, lambda: a*x)
229 self.assertRaises(TypeError, lambda: x*a)
230 self.assertRaises(TypeError, lambda: a/x)
231 self.assertRaises(TypeError, lambda: x/a)
232 self.assertRaises(TypeError, lambda: a // x)
233 self.assertRaises(TypeError, lambda: x // a)
234
235 # Divison of int by timedelta doesn't make sense.
236 # Division by zero doesn't make sense.
237 for zero in 0, 0L:
238 self.assertRaises(TypeError, lambda: zero // a)
239 self.assertRaises(ZeroDivisionError, lambda: a // zero)
240
241 def test_basic_attributes(self):
242 days, seconds, us = 1, 7, 31
243 td = timedelta(days, seconds, us)
244 self.assertEqual(td.days, days)
245 self.assertEqual(td.seconds, seconds)
246 self.assertEqual(td.microseconds, us)
247
248 def test_carries(self):
249 t1 = timedelta(days=100,
250 weeks=-7,
251 hours=-24*(100-49),
252 minutes=-3,
253 seconds=12,
254 microseconds=(3*60 - 12) * 1e6 + 1)
255 t2 = timedelta(microseconds=1)
256 self.assertEqual(t1, t2)
257
258 def test_hash_equality(self):
259 t1 = timedelta(days=100,
260 weeks=-7,
261 hours=-24*(100-49),
262 minutes=-3,
263 seconds=12,
264 microseconds=(3*60 - 12) * 1000000)
265 t2 = timedelta()
266 self.assertEqual(hash(t1), hash(t2))
267
268 t1 += timedelta(weeks=7)
269 t2 += timedelta(days=7*7)
270 self.assertEqual(t1, t2)
271 self.assertEqual(hash(t1), hash(t2))
272
273 d = {t1: 1}
274 d[t2] = 2
275 self.assertEqual(len(d), 1)
276 self.assertEqual(d[t1], 2)
277
278 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000279 args = 12, 34, 56
280 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000281 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000282 green = pickler.dumps(orig, proto)
283 derived = unpickler.loads(green)
284 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000285
286 def test_compare(self):
287 t1 = timedelta(2, 3, 4)
288 t2 = timedelta(2, 3, 4)
289 self.failUnless(t1 == t2)
290 self.failUnless(t1 <= t2)
291 self.failUnless(t1 >= t2)
292 self.failUnless(not t1 != t2)
293 self.failUnless(not t1 < t2)
294 self.failUnless(not t1 > t2)
295 self.assertEqual(cmp(t1, t2), 0)
296 self.assertEqual(cmp(t2, t1), 0)
297
298 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
299 t2 = timedelta(*args) # this is larger than t1
300 self.failUnless(t1 < t2)
301 self.failUnless(t2 > t1)
302 self.failUnless(t1 <= t2)
303 self.failUnless(t2 >= t1)
304 self.failUnless(t1 != t2)
305 self.failUnless(t2 != t1)
306 self.failUnless(not t1 == t2)
307 self.failUnless(not t2 == t1)
308 self.failUnless(not t1 > t2)
309 self.failUnless(not t2 < t1)
310 self.failUnless(not t1 >= t2)
311 self.failUnless(not t2 <= t1)
312 self.assertEqual(cmp(t1, t2), -1)
313 self.assertEqual(cmp(t2, t1), 1)
314
315 for badarg in 10, 10L, 34.5, "abc", {}, [], ():
316 self.assertRaises(TypeError, lambda: t1 == badarg)
317 self.assertRaises(TypeError, lambda: t1 != badarg)
318 self.assertRaises(TypeError, lambda: t1 <= badarg)
319 self.assertRaises(TypeError, lambda: t1 < badarg)
320 self.assertRaises(TypeError, lambda: t1 > badarg)
321 self.assertRaises(TypeError, lambda: t1 >= badarg)
322 self.assertRaises(TypeError, lambda: badarg == t1)
323 self.assertRaises(TypeError, lambda: badarg != t1)
324 self.assertRaises(TypeError, lambda: badarg <= t1)
325 self.assertRaises(TypeError, lambda: badarg < t1)
326 self.assertRaises(TypeError, lambda: badarg > t1)
327 self.assertRaises(TypeError, lambda: badarg >= t1)
328
329 def test_str(self):
330 td = timedelta
331 eq = self.assertEqual
332
333 eq(str(td(1)), "1 day, 0:00:00")
334 eq(str(td(-1)), "-1 day, 0:00:00")
335 eq(str(td(2)), "2 days, 0:00:00")
336 eq(str(td(-2)), "-2 days, 0:00:00")
337
338 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
339 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
340 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
341 "-210 days, 23:12:34")
342
343 eq(str(td(milliseconds=1)), "0:00:00.001000")
344 eq(str(td(microseconds=3)), "0:00:00.000003")
345
346 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
347 microseconds=999999)),
348 "999999999 days, 23:59:59.999999")
349
350 def test_roundtrip(self):
351 for td in (timedelta(days=999999999, hours=23, minutes=59,
352 seconds=59, microseconds=999999),
353 timedelta(days=-999999999),
354 timedelta(days=1, seconds=2, microseconds=3)):
355
356 # Verify td -> string -> td identity.
357 s = repr(td)
358 self.failUnless(s.startswith('datetime.'))
359 s = s[9:]
360 td2 = eval(s)
361 self.assertEqual(td, td2)
362
363 # Verify identity via reconstructing from pieces.
364 td2 = timedelta(td.days, td.seconds, td.microseconds)
365 self.assertEqual(td, td2)
366
367 def test_resolution_info(self):
368 self.assert_(isinstance(timedelta.min, timedelta))
369 self.assert_(isinstance(timedelta.max, timedelta))
370 self.assert_(isinstance(timedelta.resolution, timedelta))
371 self.assert_(timedelta.max > timedelta.min)
372 self.assertEqual(timedelta.min, timedelta(-999999999))
373 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
374 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
375
376 def test_overflow(self):
377 tiny = timedelta.resolution
378
379 td = timedelta.min + tiny
380 td -= tiny # no problem
381 self.assertRaises(OverflowError, td.__sub__, tiny)
382 self.assertRaises(OverflowError, td.__add__, -tiny)
383
384 td = timedelta.max - tiny
385 td += tiny # no problem
386 self.assertRaises(OverflowError, td.__add__, tiny)
387 self.assertRaises(OverflowError, td.__sub__, -tiny)
388
389 self.assertRaises(OverflowError, lambda: -timedelta.max)
390
391 def test_microsecond_rounding(self):
392 td = timedelta
393 eq = self.assertEqual
394
395 # Single-field rounding.
396 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
397 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
398 eq(td(milliseconds=0.6/1000), td(microseconds=1))
399 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
400
401 # Rounding due to contributions from more than one field.
402 us_per_hour = 3600e6
403 us_per_day = us_per_hour * 24
404 eq(td(days=.4/us_per_day), td(0))
405 eq(td(hours=.2/us_per_hour), td(0))
406 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
407
408 eq(td(days=-.4/us_per_day), td(0))
409 eq(td(hours=-.2/us_per_hour), td(0))
410 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
411
412 def test_massive_normalization(self):
413 td = timedelta(microseconds=-1)
414 self.assertEqual((td.days, td.seconds, td.microseconds),
415 (-1, 24*3600-1, 999999))
416
417 def test_bool(self):
418 self.failUnless(timedelta(1))
419 self.failUnless(timedelta(0, 1))
420 self.failUnless(timedelta(0, 0, 1))
421 self.failUnless(timedelta(microseconds=1))
422 self.failUnless(not timedelta(0))
423
424#############################################################################
425# date tests
426
427class TestDateOnly(unittest.TestCase):
428 # Tests here won't pass if also run on datetime objects, so don't
429 # subclass this to test datetimes too.
430
431 def test_delta_non_days_ignored(self):
432 dt = date(2000, 1, 2)
433 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
434 microseconds=5)
435 days = timedelta(delta.days)
436 self.assertEqual(days, timedelta(1))
437
438 dt2 = dt + delta
439 self.assertEqual(dt2, dt + days)
440
441 dt2 = delta + dt
442 self.assertEqual(dt2, dt + days)
443
444 dt2 = dt - delta
445 self.assertEqual(dt2, dt - days)
446
447 delta = -delta
448 days = timedelta(delta.days)
449 self.assertEqual(days, timedelta(-2))
450
451 dt2 = dt + delta
452 self.assertEqual(dt2, dt + days)
453
454 dt2 = delta + dt
455 self.assertEqual(dt2, dt + days)
456
457 dt2 = dt - delta
458 self.assertEqual(dt2, dt - days)
459
460class TestDate(unittest.TestCase):
461 # Tests here should pass for both dates and datetimes, except for a
462 # few tests that TestDateTime overrides.
463
464 theclass = date
465
466 def test_basic_attributes(self):
467 dt = self.theclass(2002, 3, 1)
468 self.assertEqual(dt.year, 2002)
469 self.assertEqual(dt.month, 3)
470 self.assertEqual(dt.day, 1)
471
472 def test_roundtrip(self):
473 for dt in (self.theclass(1, 2, 3),
474 self.theclass.today()):
475 # Verify dt -> string -> date identity.
476 s = repr(dt)
477 self.failUnless(s.startswith('datetime.'))
478 s = s[9:]
479 dt2 = eval(s)
480 self.assertEqual(dt, dt2)
481
482 # Verify identity via reconstructing from pieces.
483 dt2 = self.theclass(dt.year, dt.month, dt.day)
484 self.assertEqual(dt, dt2)
485
486 def test_ordinal_conversions(self):
487 # Check some fixed values.
488 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
489 (1, 12, 31, 365),
490 (2, 1, 1, 366),
491 # first example from "Calendrical Calculations"
492 (1945, 11, 12, 710347)]:
493 d = self.theclass(y, m, d)
494 self.assertEqual(n, d.toordinal())
495 fromord = self.theclass.fromordinal(n)
496 self.assertEqual(d, fromord)
497 if hasattr(fromord, "hour"):
498 # if we're checking something fancier than a date, verify
499 # the extra fields have been zeroed out
500 self.assertEqual(fromord.hour, 0)
501 self.assertEqual(fromord.minute, 0)
502 self.assertEqual(fromord.second, 0)
503 self.assertEqual(fromord.microsecond, 0)
504
Tim Peters0bf60bd2003-01-08 20:40:01 +0000505 # Check first and last days of year spottily across the whole
506 # range of years supported.
507 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000508 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
509 d = self.theclass(year, 1, 1)
510 n = d.toordinal()
511 d2 = self.theclass.fromordinal(n)
512 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000513 # Verify that moving back a day gets to the end of year-1.
514 if year > 1:
515 d = self.theclass.fromordinal(n-1)
516 d2 = self.theclass(year-1, 12, 31)
517 self.assertEqual(d, d2)
518 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000519
520 # Test every day in a leap-year and a non-leap year.
521 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
522 for year, isleap in (2000, True), (2002, False):
523 n = self.theclass(year, 1, 1).toordinal()
524 for month, maxday in zip(range(1, 13), dim):
525 if month == 2 and isleap:
526 maxday += 1
527 for day in range(1, maxday+1):
528 d = self.theclass(year, month, day)
529 self.assertEqual(d.toordinal(), n)
530 self.assertEqual(d, self.theclass.fromordinal(n))
531 n += 1
532
533 def test_extreme_ordinals(self):
534 a = self.theclass.min
535 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
536 aord = a.toordinal()
537 b = a.fromordinal(aord)
538 self.assertEqual(a, b)
539
540 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
541
542 b = a + timedelta(days=1)
543 self.assertEqual(b.toordinal(), aord + 1)
544 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
545
546 a = self.theclass.max
547 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
548 aord = a.toordinal()
549 b = a.fromordinal(aord)
550 self.assertEqual(a, b)
551
552 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
553
554 b = a - timedelta(days=1)
555 self.assertEqual(b.toordinal(), aord - 1)
556 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
557
558 def test_bad_constructor_arguments(self):
559 # bad years
560 self.theclass(MINYEAR, 1, 1) # no exception
561 self.theclass(MAXYEAR, 1, 1) # no exception
562 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
563 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
564 # bad months
565 self.theclass(2000, 1, 1) # no exception
566 self.theclass(2000, 12, 1) # no exception
567 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
568 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
569 # bad days
570 self.theclass(2000, 2, 29) # no exception
571 self.theclass(2004, 2, 29) # no exception
572 self.theclass(2400, 2, 29) # no exception
573 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
574 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
575 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
576 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
577 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
578 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
579
580 def test_hash_equality(self):
581 d = self.theclass(2000, 12, 31)
582 # same thing
583 e = self.theclass(2000, 12, 31)
584 self.assertEqual(d, e)
585 self.assertEqual(hash(d), hash(e))
586
587 dic = {d: 1}
588 dic[e] = 2
589 self.assertEqual(len(dic), 1)
590 self.assertEqual(dic[d], 2)
591 self.assertEqual(dic[e], 2)
592
593 d = self.theclass(2001, 1, 1)
594 # same thing
595 e = self.theclass(2001, 1, 1)
596 self.assertEqual(d, e)
597 self.assertEqual(hash(d), hash(e))
598
599 dic = {d: 1}
600 dic[e] = 2
601 self.assertEqual(len(dic), 1)
602 self.assertEqual(dic[d], 2)
603 self.assertEqual(dic[e], 2)
604
605 def test_computations(self):
606 a = self.theclass(2002, 1, 31)
607 b = self.theclass(1956, 1, 31)
608
609 diff = a-b
610 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
611 self.assertEqual(diff.seconds, 0)
612 self.assertEqual(diff.microseconds, 0)
613
614 day = timedelta(1)
615 week = timedelta(7)
616 a = self.theclass(2002, 3, 2)
617 self.assertEqual(a + day, self.theclass(2002, 3, 3))
618 self.assertEqual(day + a, self.theclass(2002, 3, 3))
619 self.assertEqual(a - day, self.theclass(2002, 3, 1))
620 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
621 self.assertEqual(a + week, self.theclass(2002, 3, 9))
622 self.assertEqual(a - week, self.theclass(2002, 2, 23))
623 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
624 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
625 self.assertEqual((a + week) - a, week)
626 self.assertEqual((a + day) - a, day)
627 self.assertEqual((a - week) - a, -week)
628 self.assertEqual((a - day) - a, -day)
629 self.assertEqual(a - (a + week), -week)
630 self.assertEqual(a - (a + day), -day)
631 self.assertEqual(a - (a - week), week)
632 self.assertEqual(a - (a - day), day)
633
634 # Add/sub ints, longs, floats should be illegal
635 for i in 1, 1L, 1.0:
636 self.assertRaises(TypeError, lambda: a+i)
637 self.assertRaises(TypeError, lambda: a-i)
638 self.assertRaises(TypeError, lambda: i+a)
639 self.assertRaises(TypeError, lambda: i-a)
640
641 # delta - date is senseless.
642 self.assertRaises(TypeError, lambda: day - a)
643 # mixing date and (delta or date) via * or // is senseless
644 self.assertRaises(TypeError, lambda: day * a)
645 self.assertRaises(TypeError, lambda: a * day)
646 self.assertRaises(TypeError, lambda: day // a)
647 self.assertRaises(TypeError, lambda: a // day)
648 self.assertRaises(TypeError, lambda: a * a)
649 self.assertRaises(TypeError, lambda: a // a)
650 # date + date is senseless
651 self.assertRaises(TypeError, lambda: a + a)
652
653 def test_overflow(self):
654 tiny = self.theclass.resolution
655
656 dt = self.theclass.min + tiny
657 dt -= tiny # no problem
658 self.assertRaises(OverflowError, dt.__sub__, tiny)
659 self.assertRaises(OverflowError, dt.__add__, -tiny)
660
661 dt = self.theclass.max - tiny
662 dt += tiny # no problem
663 self.assertRaises(OverflowError, dt.__add__, tiny)
664 self.assertRaises(OverflowError, dt.__sub__, -tiny)
665
666 def test_fromtimestamp(self):
667 import time
668
669 # Try an arbitrary fixed value.
670 year, month, day = 1999, 9, 19
671 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
672 d = self.theclass.fromtimestamp(ts)
673 self.assertEqual(d.year, year)
674 self.assertEqual(d.month, month)
675 self.assertEqual(d.day, day)
676
677 def test_today(self):
678 import time
679
680 # We claim that today() is like fromtimestamp(time.time()), so
681 # prove it.
682 for dummy in range(3):
683 today = self.theclass.today()
684 ts = time.time()
685 todayagain = self.theclass.fromtimestamp(ts)
686 if today == todayagain:
687 break
688 # There are several legit reasons that could fail:
689 # 1. It recently became midnight, between the today() and the
690 # time() calls.
691 # 2. The platform time() has such fine resolution that we'll
692 # never get the same value twice.
693 # 3. The platform time() has poor resolution, and we just
694 # happened to call today() right before a resolution quantum
695 # boundary.
696 # 4. The system clock got fiddled between calls.
697 # In any case, wait a little while and try again.
698 time.sleep(0.1)
699
700 # It worked or it didn't. If it didn't, assume it's reason #2, and
701 # let the test pass if they're within half a second of each other.
702 self.failUnless(today == todayagain or
703 abs(todayagain - today) < timedelta(seconds=0.5))
704
705 def test_weekday(self):
706 for i in range(7):
707 # March 4, 2002 is a Monday
708 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
709 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
710 # January 2, 1956 is a Monday
711 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
712 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
713
714 def test_isocalendar(self):
715 # Check examples from
716 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
717 for i in range(7):
718 d = self.theclass(2003, 12, 22+i)
719 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
720 d = self.theclass(2003, 12, 29) + timedelta(i)
721 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
722 d = self.theclass(2004, 1, 5+i)
723 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
724 d = self.theclass(2009, 12, 21+i)
725 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
726 d = self.theclass(2009, 12, 28) + timedelta(i)
727 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
728 d = self.theclass(2010, 1, 4+i)
729 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
730
731 def test_iso_long_years(self):
732 # Calculate long ISO years and compare to table from
733 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
734 ISO_LONG_YEARS_TABLE = """
735 4 32 60 88
736 9 37 65 93
737 15 43 71 99
738 20 48 76
739 26 54 82
740
741 105 133 161 189
742 111 139 167 195
743 116 144 172
744 122 150 178
745 128 156 184
746
747 201 229 257 285
748 207 235 263 291
749 212 240 268 296
750 218 246 274
751 224 252 280
752
753 303 331 359 387
754 308 336 364 392
755 314 342 370 398
756 320 348 376
757 325 353 381
758 """
759 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
760 iso_long_years.sort()
761 L = []
762 for i in range(400):
763 d = self.theclass(2000+i, 12, 31)
764 d1 = self.theclass(1600+i, 12, 31)
765 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
766 if d.isocalendar()[1] == 53:
767 L.append(i)
768 self.assertEqual(L, iso_long_years)
769
770 def test_isoformat(self):
771 t = self.theclass(2, 3, 2)
772 self.assertEqual(t.isoformat(), "0002-03-02")
773
774 def test_ctime(self):
775 t = self.theclass(2002, 3, 2)
776 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
777
778 def test_strftime(self):
779 t = self.theclass(2005, 3, 2)
780 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
781
782 self.assertRaises(TypeError, t.strftime) # needs an arg
783 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
784 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
785
786 # A naive object replaces %z and %Z w/ empty strings.
787 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
788
789 def test_resolution_info(self):
790 self.assert_(isinstance(self.theclass.min, self.theclass))
791 self.assert_(isinstance(self.theclass.max, self.theclass))
792 self.assert_(isinstance(self.theclass.resolution, timedelta))
793 self.assert_(self.theclass.max > self.theclass.min)
794
795 def test_extreme_timedelta(self):
796 big = self.theclass.max - self.theclass.min
797 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
798 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
799 # n == 315537897599999999 ~= 2**58.13
800 justasbig = timedelta(0, 0, n)
801 self.assertEqual(big, justasbig)
802 self.assertEqual(self.theclass.min + big, self.theclass.max)
803 self.assertEqual(self.theclass.max - big, self.theclass.min)
804
805 def test_timetuple(self):
806 for i in range(7):
807 # January 2, 1956 is a Monday (0)
808 d = self.theclass(1956, 1, 2+i)
809 t = d.timetuple()
810 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
811 # February 1, 1956 is a Wednesday (2)
812 d = self.theclass(1956, 2, 1+i)
813 t = d.timetuple()
814 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
815 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
816 # of the year.
817 d = self.theclass(1956, 3, 1+i)
818 t = d.timetuple()
819 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
820 self.assertEqual(t.tm_year, 1956)
821 self.assertEqual(t.tm_mon, 3)
822 self.assertEqual(t.tm_mday, 1+i)
823 self.assertEqual(t.tm_hour, 0)
824 self.assertEqual(t.tm_min, 0)
825 self.assertEqual(t.tm_sec, 0)
826 self.assertEqual(t.tm_wday, (3+i)%7)
827 self.assertEqual(t.tm_yday, 61+i)
828 self.assertEqual(t.tm_isdst, -1)
829
830 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000831 args = 6, 7, 23
832 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000833 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000834 green = pickler.dumps(orig, proto)
835 derived = unpickler.loads(green)
836 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000837
838 def test_compare(self):
839 t1 = self.theclass(2, 3, 4)
840 t2 = self.theclass(2, 3, 4)
841 self.failUnless(t1 == t2)
842 self.failUnless(t1 <= t2)
843 self.failUnless(t1 >= t2)
844 self.failUnless(not t1 != t2)
845 self.failUnless(not t1 < t2)
846 self.failUnless(not t1 > t2)
847 self.assertEqual(cmp(t1, t2), 0)
848 self.assertEqual(cmp(t2, t1), 0)
849
850 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
851 t2 = self.theclass(*args) # this is larger than t1
852 self.failUnless(t1 < t2)
853 self.failUnless(t2 > t1)
854 self.failUnless(t1 <= t2)
855 self.failUnless(t2 >= t1)
856 self.failUnless(t1 != t2)
857 self.failUnless(t2 != t1)
858 self.failUnless(not t1 == t2)
859 self.failUnless(not t2 == t1)
860 self.failUnless(not t1 > t2)
861 self.failUnless(not t2 < t1)
862 self.failUnless(not t1 >= t2)
863 self.failUnless(not t2 <= t1)
864 self.assertEqual(cmp(t1, t2), -1)
865 self.assertEqual(cmp(t2, t1), 1)
866
867 for badarg in 10, 10L, 34.5, "abc", {}, [], ():
868 self.assertRaises(TypeError, lambda: t1 == badarg)
869 self.assertRaises(TypeError, lambda: t1 != badarg)
870 self.assertRaises(TypeError, lambda: t1 <= badarg)
871 self.assertRaises(TypeError, lambda: t1 < badarg)
872 self.assertRaises(TypeError, lambda: t1 > badarg)
873 self.assertRaises(TypeError, lambda: t1 >= badarg)
874 self.assertRaises(TypeError, lambda: badarg == t1)
875 self.assertRaises(TypeError, lambda: badarg != t1)
876 self.assertRaises(TypeError, lambda: badarg <= t1)
877 self.assertRaises(TypeError, lambda: badarg < t1)
878 self.assertRaises(TypeError, lambda: badarg > t1)
879 self.assertRaises(TypeError, lambda: badarg >= t1)
880
Tim Peters8d81a012003-01-24 22:36:34 +0000881 def test_mixed_compare(self):
882 our = self.theclass(2000, 4, 5)
883 self.assertRaises(TypeError, cmp, our, 1)
884 self.assertRaises(TypeError, cmp, 1, our)
885
886 class AnotherDateTimeClass(object):
887 def __cmp__(self, other):
888 # Return "equal" so calling this can't be confused with
889 # compare-by-address (which never says "equal" for distinct
890 # objects).
891 return 0
892
893 # This still errors, because date and datetime comparison raise
894 # TypeError instead of NotImplemented when they don't know what to
895 # do, in order to stop comparison from falling back to the default
896 # compare-by-address.
897 their = AnotherDateTimeClass()
898 self.assertRaises(TypeError, cmp, our, their)
899 # Oops: The next stab raises TypeError in the C implementation,
900 # but not in the Python implementation of datetime. The difference
901 # is due to that the Python implementation defines __cmp__ but
902 # the C implementation defines tp_richcompare. This is more pain
903 # to fix than it's worth, so commenting out the test.
904 # self.assertEqual(cmp(their, our), 0)
905
906 # But date and datetime comparison return NotImplemented instead if the
907 # other object has a timetuple attr. This gives the other object a
908 # chance to do the comparison.
909 class Comparable(AnotherDateTimeClass):
910 def timetuple(self):
911 return ()
912
913 their = Comparable()
914 self.assertEqual(cmp(our, their), 0)
915 self.assertEqual(cmp(their, our), 0)
916 self.failUnless(our == their)
917 self.failUnless(their == our)
918
Tim Peters2a799bf2002-12-16 20:18:38 +0000919 def test_bool(self):
920 # All dates are considered true.
921 self.failUnless(self.theclass.min)
922 self.failUnless(self.theclass.max)
923
Tim Petersd6844152002-12-22 20:58:42 +0000924 def test_srftime_out_of_range(self):
925 # For nasty technical reasons, we can't handle years before 1900.
926 cls = self.theclass
927 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
928 for y in 1, 49, 51, 99, 100, 1000, 1899:
929 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +0000930
931 def test_replace(self):
932 cls = self.theclass
933 args = [1, 2, 3]
934 base = cls(*args)
935 self.assertEqual(base, base.replace())
936
937 i = 0
938 for name, newval in (("year", 2),
939 ("month", 3),
940 ("day", 4)):
941 newargs = args[:]
942 newargs[i] = newval
943 expected = cls(*newargs)
944 got = base.replace(**{name: newval})
945 self.assertEqual(expected, got)
946 i += 1
947
948 # Out of bounds.
949 base = cls(2000, 2, 29)
950 self.assertRaises(ValueError, base.replace, year=2001)
951
Tim Peters2a799bf2002-12-16 20:18:38 +0000952#############################################################################
953# datetime tests
954
955class TestDateTime(TestDate):
956
957 theclass = datetime
958
959 def test_basic_attributes(self):
960 dt = self.theclass(2002, 3, 1, 12, 0)
961 self.assertEqual(dt.year, 2002)
962 self.assertEqual(dt.month, 3)
963 self.assertEqual(dt.day, 1)
964 self.assertEqual(dt.hour, 12)
965 self.assertEqual(dt.minute, 0)
966 self.assertEqual(dt.second, 0)
967 self.assertEqual(dt.microsecond, 0)
968
969 def test_basic_attributes_nonzero(self):
970 # Make sure all attributes are non-zero so bugs in
971 # bit-shifting access show up.
972 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
973 self.assertEqual(dt.year, 2002)
974 self.assertEqual(dt.month, 3)
975 self.assertEqual(dt.day, 1)
976 self.assertEqual(dt.hour, 12)
977 self.assertEqual(dt.minute, 59)
978 self.assertEqual(dt.second, 59)
979 self.assertEqual(dt.microsecond, 8000)
980
981 def test_roundtrip(self):
982 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
983 self.theclass.now()):
984 # Verify dt -> string -> datetime identity.
985 s = repr(dt)
986 self.failUnless(s.startswith('datetime.'))
987 s = s[9:]
988 dt2 = eval(s)
989 self.assertEqual(dt, dt2)
990
991 # Verify identity via reconstructing from pieces.
992 dt2 = self.theclass(dt.year, dt.month, dt.day,
993 dt.hour, dt.minute, dt.second,
994 dt.microsecond)
995 self.assertEqual(dt, dt2)
996
997 def test_isoformat(self):
998 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
999 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1000 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1001 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1002 # str is ISO format with the separator forced to a blank.
1003 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1004
1005 t = self.theclass(2, 3, 2)
1006 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1007 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1008 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1009 # str is ISO format with the separator forced to a blank.
1010 self.assertEqual(str(t), "0002-03-02 00:00:00")
1011
1012 def test_more_ctime(self):
1013 # Test fields that TestDate doesn't touch.
1014 import time
1015
1016 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1017 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1018 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1019 # out. The difference is that t.ctime() produces " 2" for the day,
1020 # but platform ctime() produces "02" for the day. According to
1021 # C99, t.ctime() is correct here.
1022 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1023
1024 # So test a case where that difference doesn't matter.
1025 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1026 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1027
1028 def test_tz_independent_comparing(self):
1029 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1030 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1031 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1032 self.assertEqual(dt1, dt3)
1033 self.assert_(dt2 > dt3)
1034
1035 # Make sure comparison doesn't forget microseconds, and isn't done
1036 # via comparing a float timestamp (an IEEE double doesn't have enough
1037 # precision to span microsecond resolution across years 1 thru 9999,
1038 # so comparing via timestamp necessarily calls some distinct values
1039 # equal).
1040 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1041 us = timedelta(microseconds=1)
1042 dt2 = dt1 + us
1043 self.assertEqual(dt2 - dt1, us)
1044 self.assert_(dt1 < dt2)
1045
1046 def test_bad_constructor_arguments(self):
1047 # bad years
1048 self.theclass(MINYEAR, 1, 1) # no exception
1049 self.theclass(MAXYEAR, 1, 1) # no exception
1050 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1051 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1052 # bad months
1053 self.theclass(2000, 1, 1) # no exception
1054 self.theclass(2000, 12, 1) # no exception
1055 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1056 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1057 # bad days
1058 self.theclass(2000, 2, 29) # no exception
1059 self.theclass(2004, 2, 29) # no exception
1060 self.theclass(2400, 2, 29) # no exception
1061 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1062 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1063 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1064 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1065 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1066 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1067 # bad hours
1068 self.theclass(2000, 1, 31, 0) # no exception
1069 self.theclass(2000, 1, 31, 23) # no exception
1070 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1071 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1072 # bad minutes
1073 self.theclass(2000, 1, 31, 23, 0) # no exception
1074 self.theclass(2000, 1, 31, 23, 59) # no exception
1075 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1076 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1077 # bad seconds
1078 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1079 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1080 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1081 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1082 # bad microseconds
1083 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1084 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1085 self.assertRaises(ValueError, self.theclass,
1086 2000, 1, 31, 23, 59, 59, -1)
1087 self.assertRaises(ValueError, self.theclass,
1088 2000, 1, 31, 23, 59, 59,
1089 1000000)
1090
1091 def test_hash_equality(self):
1092 d = self.theclass(2000, 12, 31, 23, 30, 17)
1093 e = self.theclass(2000, 12, 31, 23, 30, 17)
1094 self.assertEqual(d, e)
1095 self.assertEqual(hash(d), hash(e))
1096
1097 dic = {d: 1}
1098 dic[e] = 2
1099 self.assertEqual(len(dic), 1)
1100 self.assertEqual(dic[d], 2)
1101 self.assertEqual(dic[e], 2)
1102
1103 d = self.theclass(2001, 1, 1, 0, 5, 17)
1104 e = self.theclass(2001, 1, 1, 0, 5, 17)
1105 self.assertEqual(d, e)
1106 self.assertEqual(hash(d), hash(e))
1107
1108 dic = {d: 1}
1109 dic[e] = 2
1110 self.assertEqual(len(dic), 1)
1111 self.assertEqual(dic[d], 2)
1112 self.assertEqual(dic[e], 2)
1113
1114 def test_computations(self):
1115 a = self.theclass(2002, 1, 31)
1116 b = self.theclass(1956, 1, 31)
1117 diff = a-b
1118 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1119 self.assertEqual(diff.seconds, 0)
1120 self.assertEqual(diff.microseconds, 0)
1121 a = self.theclass(2002, 3, 2, 17, 6)
1122 millisec = timedelta(0, 0, 1000)
1123 hour = timedelta(0, 3600)
1124 day = timedelta(1)
1125 week = timedelta(7)
1126 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1127 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1128 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1129 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1130 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1131 self.assertEqual(a - hour, a + -hour)
1132 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1133 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1134 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1135 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1136 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1137 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1138 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1139 self.assertEqual((a + week) - a, week)
1140 self.assertEqual((a + day) - a, day)
1141 self.assertEqual((a + hour) - a, hour)
1142 self.assertEqual((a + millisec) - a, millisec)
1143 self.assertEqual((a - week) - a, -week)
1144 self.assertEqual((a - day) - a, -day)
1145 self.assertEqual((a - hour) - a, -hour)
1146 self.assertEqual((a - millisec) - a, -millisec)
1147 self.assertEqual(a - (a + week), -week)
1148 self.assertEqual(a - (a + day), -day)
1149 self.assertEqual(a - (a + hour), -hour)
1150 self.assertEqual(a - (a + millisec), -millisec)
1151 self.assertEqual(a - (a - week), week)
1152 self.assertEqual(a - (a - day), day)
1153 self.assertEqual(a - (a - hour), hour)
1154 self.assertEqual(a - (a - millisec), millisec)
1155 self.assertEqual(a + (week + day + hour + millisec),
1156 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1157 self.assertEqual(a + (week + day + hour + millisec),
1158 (((a + week) + day) + hour) + millisec)
1159 self.assertEqual(a - (week + day + hour + millisec),
1160 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1161 self.assertEqual(a - (week + day + hour + millisec),
1162 (((a - week) - day) - hour) - millisec)
1163 # Add/sub ints, longs, floats should be illegal
1164 for i in 1, 1L, 1.0:
1165 self.assertRaises(TypeError, lambda: a+i)
1166 self.assertRaises(TypeError, lambda: a-i)
1167 self.assertRaises(TypeError, lambda: i+a)
1168 self.assertRaises(TypeError, lambda: i-a)
1169
1170 # delta - datetime is senseless.
1171 self.assertRaises(TypeError, lambda: day - a)
1172 # mixing datetime and (delta or datetime) via * or // is senseless
1173 self.assertRaises(TypeError, lambda: day * a)
1174 self.assertRaises(TypeError, lambda: a * day)
1175 self.assertRaises(TypeError, lambda: day // a)
1176 self.assertRaises(TypeError, lambda: a // day)
1177 self.assertRaises(TypeError, lambda: a * a)
1178 self.assertRaises(TypeError, lambda: a // a)
1179 # datetime + datetime is senseless
1180 self.assertRaises(TypeError, lambda: a + a)
1181
1182 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001183 args = 6, 7, 23, 20, 59, 1, 64**2
1184 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001185 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001186 green = pickler.dumps(orig, proto)
1187 derived = unpickler.loads(green)
1188 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001189
1190 def test_more_compare(self):
1191 # The test_compare() inherited from TestDate covers the error cases.
1192 # We just want to test lexicographic ordering on the members datetime
1193 # has that date lacks.
1194 args = [2000, 11, 29, 20, 58, 16, 999998]
1195 t1 = self.theclass(*args)
1196 t2 = self.theclass(*args)
1197 self.failUnless(t1 == t2)
1198 self.failUnless(t1 <= t2)
1199 self.failUnless(t1 >= t2)
1200 self.failUnless(not t1 != t2)
1201 self.failUnless(not t1 < t2)
1202 self.failUnless(not t1 > t2)
1203 self.assertEqual(cmp(t1, t2), 0)
1204 self.assertEqual(cmp(t2, t1), 0)
1205
1206 for i in range(len(args)):
1207 newargs = args[:]
1208 newargs[i] = args[i] + 1
1209 t2 = self.theclass(*newargs) # this is larger than t1
1210 self.failUnless(t1 < t2)
1211 self.failUnless(t2 > t1)
1212 self.failUnless(t1 <= t2)
1213 self.failUnless(t2 >= t1)
1214 self.failUnless(t1 != t2)
1215 self.failUnless(t2 != t1)
1216 self.failUnless(not t1 == t2)
1217 self.failUnless(not t2 == t1)
1218 self.failUnless(not t1 > t2)
1219 self.failUnless(not t2 < t1)
1220 self.failUnless(not t1 >= t2)
1221 self.failUnless(not t2 <= t1)
1222 self.assertEqual(cmp(t1, t2), -1)
1223 self.assertEqual(cmp(t2, t1), 1)
1224
1225
1226 # A helper for timestamp constructor tests.
1227 def verify_field_equality(self, expected, got):
1228 self.assertEqual(expected.tm_year, got.year)
1229 self.assertEqual(expected.tm_mon, got.month)
1230 self.assertEqual(expected.tm_mday, got.day)
1231 self.assertEqual(expected.tm_hour, got.hour)
1232 self.assertEqual(expected.tm_min, got.minute)
1233 self.assertEqual(expected.tm_sec, got.second)
1234
1235 def test_fromtimestamp(self):
1236 import time
1237
1238 ts = time.time()
1239 expected = time.localtime(ts)
1240 got = self.theclass.fromtimestamp(ts)
1241 self.verify_field_equality(expected, got)
1242
1243 def test_utcfromtimestamp(self):
1244 import time
1245
1246 ts = time.time()
1247 expected = time.gmtime(ts)
1248 got = self.theclass.utcfromtimestamp(ts)
1249 self.verify_field_equality(expected, got)
1250
1251 def test_utcnow(self):
1252 import time
1253
1254 # Call it a success if utcnow() and utcfromtimestamp() are within
1255 # a second of each other.
1256 tolerance = timedelta(seconds=1)
1257 for dummy in range(3):
1258 from_now = self.theclass.utcnow()
1259 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1260 if abs(from_timestamp - from_now) <= tolerance:
1261 break
1262 # Else try again a few times.
1263 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1264
1265 def test_more_timetuple(self):
1266 # This tests fields beyond those tested by the TestDate.test_timetuple.
1267 t = self.theclass(2004, 12, 31, 6, 22, 33)
1268 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1269 self.assertEqual(t.timetuple(),
1270 (t.year, t.month, t.day,
1271 t.hour, t.minute, t.second,
1272 t.weekday(),
1273 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1274 -1))
1275 tt = t.timetuple()
1276 self.assertEqual(tt.tm_year, t.year)
1277 self.assertEqual(tt.tm_mon, t.month)
1278 self.assertEqual(tt.tm_mday, t.day)
1279 self.assertEqual(tt.tm_hour, t.hour)
1280 self.assertEqual(tt.tm_min, t.minute)
1281 self.assertEqual(tt.tm_sec, t.second)
1282 self.assertEqual(tt.tm_wday, t.weekday())
1283 self.assertEqual(tt.tm_yday, t.toordinal() -
1284 date(t.year, 1, 1).toordinal() + 1)
1285 self.assertEqual(tt.tm_isdst, -1)
1286
1287 def test_more_strftime(self):
1288 # This tests fields beyond those tested by the TestDate.test_strftime.
1289 t = self.theclass(2004, 12, 31, 6, 22, 33)
1290 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1291 "12 31 04 33 22 06 366")
1292
1293 def test_extract(self):
1294 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1295 self.assertEqual(dt.date(), date(2002, 3, 4))
1296 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1297
1298 def test_combine(self):
1299 d = date(2002, 3, 4)
1300 t = time(18, 45, 3, 1234)
1301 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1302 combine = self.theclass.combine
1303 dt = combine(d, t)
1304 self.assertEqual(dt, expected)
1305
1306 dt = combine(time=t, date=d)
1307 self.assertEqual(dt, expected)
1308
1309 self.assertEqual(d, dt.date())
1310 self.assertEqual(t, dt.time())
1311 self.assertEqual(dt, combine(dt.date(), dt.time()))
1312
1313 self.assertRaises(TypeError, combine) # need an arg
1314 self.assertRaises(TypeError, combine, d) # need two args
1315 self.assertRaises(TypeError, combine, t, d) # args reversed
1316 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1317 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1318
Tim Peters12bf3392002-12-24 05:41:27 +00001319 def test_replace(self):
1320 cls = self.theclass
1321 args = [1, 2, 3, 4, 5, 6, 7]
1322 base = cls(*args)
1323 self.assertEqual(base, base.replace())
1324
1325 i = 0
1326 for name, newval in (("year", 2),
1327 ("month", 3),
1328 ("day", 4),
1329 ("hour", 5),
1330 ("minute", 6),
1331 ("second", 7),
1332 ("microsecond", 8)):
1333 newargs = args[:]
1334 newargs[i] = newval
1335 expected = cls(*newargs)
1336 got = base.replace(**{name: newval})
1337 self.assertEqual(expected, got)
1338 i += 1
1339
1340 # Out of bounds.
1341 base = cls(2000, 2, 29)
1342 self.assertRaises(ValueError, base.replace, year=2001)
1343
Tim Peters80475bb2002-12-25 07:40:55 +00001344 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001345 # Pretty boring! The TZ test is more interesting here. astimezone()
1346 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001347 dt = self.theclass.now()
1348 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001349 self.assertRaises(TypeError, dt.astimezone) # not enough args
1350 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1351 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001352 self.assertRaises(ValueError, dt.astimezone, f) # naive
1353 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001354
Tim Peters52dcce22003-01-23 16:36:11 +00001355 class Bogus(tzinfo):
1356 def utcoffset(self, dt): return None
1357 def dst(self, dt): return timedelta(0)
1358 bog = Bogus()
1359 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1360
1361 class AlsoBogus(tzinfo):
1362 def utcoffset(self, dt): return timedelta(0)
1363 def dst(self, dt): return None
1364 alsobog = AlsoBogus()
1365 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001366
Tim Peters2a799bf2002-12-16 20:18:38 +00001367class TestTime(unittest.TestCase):
1368
1369 theclass = time
1370
1371 def test_basic_attributes(self):
1372 t = self.theclass(12, 0)
1373 self.assertEqual(t.hour, 12)
1374 self.assertEqual(t.minute, 0)
1375 self.assertEqual(t.second, 0)
1376 self.assertEqual(t.microsecond, 0)
1377
1378 def test_basic_attributes_nonzero(self):
1379 # Make sure all attributes are non-zero so bugs in
1380 # bit-shifting access show up.
1381 t = self.theclass(12, 59, 59, 8000)
1382 self.assertEqual(t.hour, 12)
1383 self.assertEqual(t.minute, 59)
1384 self.assertEqual(t.second, 59)
1385 self.assertEqual(t.microsecond, 8000)
1386
1387 def test_roundtrip(self):
1388 t = self.theclass(1, 2, 3, 4)
1389
1390 # Verify t -> string -> time identity.
1391 s = repr(t)
1392 self.failUnless(s.startswith('datetime.'))
1393 s = s[9:]
1394 t2 = eval(s)
1395 self.assertEqual(t, t2)
1396
1397 # Verify identity via reconstructing from pieces.
1398 t2 = self.theclass(t.hour, t.minute, t.second,
1399 t.microsecond)
1400 self.assertEqual(t, t2)
1401
1402 def test_comparing(self):
1403 args = [1, 2, 3, 4]
1404 t1 = self.theclass(*args)
1405 t2 = self.theclass(*args)
1406 self.failUnless(t1 == t2)
1407 self.failUnless(t1 <= t2)
1408 self.failUnless(t1 >= t2)
1409 self.failUnless(not t1 != t2)
1410 self.failUnless(not t1 < t2)
1411 self.failUnless(not t1 > t2)
1412 self.assertEqual(cmp(t1, t2), 0)
1413 self.assertEqual(cmp(t2, t1), 0)
1414
1415 for i in range(len(args)):
1416 newargs = args[:]
1417 newargs[i] = args[i] + 1
1418 t2 = self.theclass(*newargs) # this is larger than t1
1419 self.failUnless(t1 < t2)
1420 self.failUnless(t2 > t1)
1421 self.failUnless(t1 <= t2)
1422 self.failUnless(t2 >= t1)
1423 self.failUnless(t1 != t2)
1424 self.failUnless(t2 != t1)
1425 self.failUnless(not t1 == t2)
1426 self.failUnless(not t2 == t1)
1427 self.failUnless(not t1 > t2)
1428 self.failUnless(not t2 < t1)
1429 self.failUnless(not t1 >= t2)
1430 self.failUnless(not t2 <= t1)
1431 self.assertEqual(cmp(t1, t2), -1)
1432 self.assertEqual(cmp(t2, t1), 1)
1433
Tim Peters0bf60bd2003-01-08 20:40:01 +00001434 badargs = (10, 10L, 34.5, "abc", {}, [], ())
1435 if CMP_BUG_FIXED:
1436 badargs += (date(1, 1, 1), datetime(1, 1, 1, 1, 1), timedelta(9))
1437 for badarg in badargs:
Tim Peters2a799bf2002-12-16 20:18:38 +00001438 self.assertRaises(TypeError, lambda: t1 == badarg)
1439 self.assertRaises(TypeError, lambda: t1 != badarg)
1440 self.assertRaises(TypeError, lambda: t1 <= badarg)
1441 self.assertRaises(TypeError, lambda: t1 < badarg)
1442 self.assertRaises(TypeError, lambda: t1 > badarg)
1443 self.assertRaises(TypeError, lambda: t1 >= badarg)
1444 self.assertRaises(TypeError, lambda: badarg == t1)
1445 self.assertRaises(TypeError, lambda: badarg != t1)
1446 self.assertRaises(TypeError, lambda: badarg <= t1)
1447 self.assertRaises(TypeError, lambda: badarg < t1)
1448 self.assertRaises(TypeError, lambda: badarg > t1)
1449 self.assertRaises(TypeError, lambda: badarg >= t1)
1450
1451 def test_bad_constructor_arguments(self):
1452 # bad hours
1453 self.theclass(0, 0) # no exception
1454 self.theclass(23, 0) # no exception
1455 self.assertRaises(ValueError, self.theclass, -1, 0)
1456 self.assertRaises(ValueError, self.theclass, 24, 0)
1457 # bad minutes
1458 self.theclass(23, 0) # no exception
1459 self.theclass(23, 59) # no exception
1460 self.assertRaises(ValueError, self.theclass, 23, -1)
1461 self.assertRaises(ValueError, self.theclass, 23, 60)
1462 # bad seconds
1463 self.theclass(23, 59, 0) # no exception
1464 self.theclass(23, 59, 59) # no exception
1465 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1466 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1467 # bad microseconds
1468 self.theclass(23, 59, 59, 0) # no exception
1469 self.theclass(23, 59, 59, 999999) # no exception
1470 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1471 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1472
1473 def test_hash_equality(self):
1474 d = self.theclass(23, 30, 17)
1475 e = self.theclass(23, 30, 17)
1476 self.assertEqual(d, e)
1477 self.assertEqual(hash(d), hash(e))
1478
1479 dic = {d: 1}
1480 dic[e] = 2
1481 self.assertEqual(len(dic), 1)
1482 self.assertEqual(dic[d], 2)
1483 self.assertEqual(dic[e], 2)
1484
1485 d = self.theclass(0, 5, 17)
1486 e = self.theclass(0, 5, 17)
1487 self.assertEqual(d, e)
1488 self.assertEqual(hash(d), hash(e))
1489
1490 dic = {d: 1}
1491 dic[e] = 2
1492 self.assertEqual(len(dic), 1)
1493 self.assertEqual(dic[d], 2)
1494 self.assertEqual(dic[e], 2)
1495
1496 def test_isoformat(self):
1497 t = self.theclass(4, 5, 1, 123)
1498 self.assertEqual(t.isoformat(), "04:05:01.000123")
1499 self.assertEqual(t.isoformat(), str(t))
1500
1501 t = self.theclass()
1502 self.assertEqual(t.isoformat(), "00:00:00")
1503 self.assertEqual(t.isoformat(), str(t))
1504
1505 t = self.theclass(microsecond=1)
1506 self.assertEqual(t.isoformat(), "00:00:00.000001")
1507 self.assertEqual(t.isoformat(), str(t))
1508
1509 t = self.theclass(microsecond=10)
1510 self.assertEqual(t.isoformat(), "00:00:00.000010")
1511 self.assertEqual(t.isoformat(), str(t))
1512
1513 t = self.theclass(microsecond=100)
1514 self.assertEqual(t.isoformat(), "00:00:00.000100")
1515 self.assertEqual(t.isoformat(), str(t))
1516
1517 t = self.theclass(microsecond=1000)
1518 self.assertEqual(t.isoformat(), "00:00:00.001000")
1519 self.assertEqual(t.isoformat(), str(t))
1520
1521 t = self.theclass(microsecond=10000)
1522 self.assertEqual(t.isoformat(), "00:00:00.010000")
1523 self.assertEqual(t.isoformat(), str(t))
1524
1525 t = self.theclass(microsecond=100000)
1526 self.assertEqual(t.isoformat(), "00:00:00.100000")
1527 self.assertEqual(t.isoformat(), str(t))
1528
1529 def test_strftime(self):
1530 t = self.theclass(1, 2, 3, 4)
1531 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1532 # A naive object replaces %z and %Z with empty strings.
1533 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1534
1535 def test_str(self):
1536 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1537 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1538 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1539 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1540 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1541
1542 def test_repr(self):
1543 name = 'datetime.' + self.theclass.__name__
1544 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1545 "%s(1, 2, 3, 4)" % name)
1546 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1547 "%s(10, 2, 3, 4000)" % name)
1548 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1549 "%s(0, 2, 3, 400000)" % name)
1550 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1551 "%s(12, 2, 3)" % name)
1552 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1553 "%s(23, 15)" % name)
1554
1555 def test_resolution_info(self):
1556 self.assert_(isinstance(self.theclass.min, self.theclass))
1557 self.assert_(isinstance(self.theclass.max, self.theclass))
1558 self.assert_(isinstance(self.theclass.resolution, timedelta))
1559 self.assert_(self.theclass.max > self.theclass.min)
1560
1561 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001562 args = 20, 59, 16, 64**2
1563 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001564 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001565 green = pickler.dumps(orig, proto)
1566 derived = unpickler.loads(green)
1567 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001568
1569 def test_bool(self):
1570 cls = self.theclass
1571 self.failUnless(cls(1))
1572 self.failUnless(cls(0, 1))
1573 self.failUnless(cls(0, 0, 1))
1574 self.failUnless(cls(0, 0, 0, 1))
1575 self.failUnless(not cls(0))
1576 self.failUnless(not cls())
1577
Tim Peters12bf3392002-12-24 05:41:27 +00001578 def test_replace(self):
1579 cls = self.theclass
1580 args = [1, 2, 3, 4]
1581 base = cls(*args)
1582 self.assertEqual(base, base.replace())
1583
1584 i = 0
1585 for name, newval in (("hour", 5),
1586 ("minute", 6),
1587 ("second", 7),
1588 ("microsecond", 8)):
1589 newargs = args[:]
1590 newargs[i] = newval
1591 expected = cls(*newargs)
1592 got = base.replace(**{name: newval})
1593 self.assertEqual(expected, got)
1594 i += 1
1595
1596 # Out of bounds.
1597 base = cls(1)
1598 self.assertRaises(ValueError, base.replace, hour=24)
1599 self.assertRaises(ValueError, base.replace, minute=-1)
1600 self.assertRaises(ValueError, base.replace, second=100)
1601 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1602
Tim Peters855fe882002-12-22 03:43:39 +00001603# A mixin for classes with a tzinfo= argument. Subclasses must define
1604# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001605# must be legit (which is true for time and datetime).
Tim Peters855fe882002-12-22 03:43:39 +00001606class TZInfoBase(unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001607
Tim Petersbad8ff02002-12-30 20:52:32 +00001608 def test_argument_passing(self):
1609 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001610 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001611 class introspective(tzinfo):
1612 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001613 def utcoffset(self, dt):
1614 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001615 dst = utcoffset
1616
1617 obj = cls(1, 2, 3, tzinfo=introspective())
1618
Tim Peters0bf60bd2003-01-08 20:40:01 +00001619 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001620 self.assertEqual(obj.tzname(), expected)
1621
Tim Peters0bf60bd2003-01-08 20:40:01 +00001622 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001623 self.assertEqual(obj.utcoffset(), expected)
1624 self.assertEqual(obj.dst(), expected)
1625
Tim Peters855fe882002-12-22 03:43:39 +00001626 def test_bad_tzinfo_classes(self):
1627 cls = self.theclass
1628 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001629
Tim Peters855fe882002-12-22 03:43:39 +00001630 class NiceTry(object):
1631 def __init__(self): pass
1632 def utcoffset(self, dt): pass
1633 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1634
1635 class BetterTry(tzinfo):
1636 def __init__(self): pass
1637 def utcoffset(self, dt): pass
1638 b = BetterTry()
1639 t = cls(1, 1, 1, tzinfo=b)
1640 self.failUnless(t.tzinfo is b)
1641
1642 def test_utc_offset_out_of_bounds(self):
1643 class Edgy(tzinfo):
1644 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00001645 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00001646 def utcoffset(self, dt):
1647 return self.offset
1648
1649 cls = self.theclass
1650 for offset, legit in ((-1440, False),
1651 (-1439, True),
1652 (1439, True),
1653 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001654 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00001655 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001656 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00001657 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001658 else:
1659 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00001660 if legit:
1661 aofs = abs(offset)
1662 h, m = divmod(aofs, 60)
1663 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001664 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00001665 t = t.timetz()
1666 self.assertEqual(str(t), "01:02:03" + tag)
1667 else:
1668 self.assertRaises(ValueError, str, t)
1669
1670 def test_tzinfo_classes(self):
1671 cls = self.theclass
1672 class C1(tzinfo):
1673 def utcoffset(self, dt): return None
1674 def dst(self, dt): return None
1675 def tzname(self, dt): return None
1676 for t in (cls(1, 1, 1),
1677 cls(1, 1, 1, tzinfo=None),
1678 cls(1, 1, 1, tzinfo=C1())):
1679 self.failUnless(t.utcoffset() is None)
1680 self.failUnless(t.dst() is None)
1681 self.failUnless(t.tzname() is None)
1682
Tim Peters855fe882002-12-22 03:43:39 +00001683 class C3(tzinfo):
1684 def utcoffset(self, dt): return timedelta(minutes=-1439)
1685 def dst(self, dt): return timedelta(minutes=1439)
1686 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001687 t = cls(1, 1, 1, tzinfo=C3())
1688 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
1689 self.assertEqual(t.dst(), timedelta(minutes=1439))
1690 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00001691
1692 # Wrong types.
1693 class C4(tzinfo):
1694 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001695 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00001696 def tzname(self, dt): return 0
1697 t = cls(1, 1, 1, tzinfo=C4())
1698 self.assertRaises(TypeError, t.utcoffset)
1699 self.assertRaises(TypeError, t.dst)
1700 self.assertRaises(TypeError, t.tzname)
1701
1702 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00001703 class C6(tzinfo):
1704 def utcoffset(self, dt): return timedelta(hours=-24)
1705 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00001706 t = cls(1, 1, 1, tzinfo=C6())
1707 self.assertRaises(ValueError, t.utcoffset)
1708 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00001709
1710 # Not a whole number of minutes.
1711 class C7(tzinfo):
1712 def utcoffset(self, dt): return timedelta(seconds=61)
1713 def dst(self, dt): return timedelta(microseconds=-81)
1714 t = cls(1, 1, 1, tzinfo=C7())
1715 self.assertRaises(ValueError, t.utcoffset)
1716 self.assertRaises(ValueError, t.dst)
1717
Tim Peters4c0db782002-12-26 05:01:19 +00001718 def test_aware_compare(self):
1719 cls = self.theclass
1720
Tim Peters60c76e42002-12-27 00:41:11 +00001721 # Ensure that utcoffset() gets ignored if the comparands have
1722 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00001723 class OperandDependentOffset(tzinfo):
1724 def utcoffset(self, t):
1725 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00001726 # d0 and d1 equal after adjustment
1727 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00001728 else:
Tim Peters397301e2003-01-02 21:28:08 +00001729 # d2 off in the weeds
1730 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00001731
1732 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
1733 d0 = base.replace(minute=3)
1734 d1 = base.replace(minute=9)
1735 d2 = base.replace(minute=11)
1736 for x in d0, d1, d2:
1737 for y in d0, d1, d2:
1738 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00001739 expected = cmp(x.minute, y.minute)
1740 self.assertEqual(got, expected)
1741
1742 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00001743 # Note that a time can't actually have an operand-depedent offset,
1744 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
1745 # so skip this test for time.
1746 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00001747 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
1748 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
1749 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
1750 for x in d0, d1, d2:
1751 for y in d0, d1, d2:
1752 got = cmp(x, y)
1753 if (x is d0 or x is d1) and (y is d0 or y is d1):
1754 expected = 0
1755 elif x is y is d2:
1756 expected = 0
1757 elif x is d2:
1758 expected = -1
1759 else:
1760 assert y is d2
1761 expected = 1
1762 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00001763
Tim Peters855fe882002-12-22 03:43:39 +00001764
Tim Peters0bf60bd2003-01-08 20:40:01 +00001765# Testing time objects with a non-None tzinfo.
Tim Peters855fe882002-12-22 03:43:39 +00001766class TestTimeTZ(TestTime, TZInfoBase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001767 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00001768
1769 def test_empty(self):
1770 t = self.theclass()
1771 self.assertEqual(t.hour, 0)
1772 self.assertEqual(t.minute, 0)
1773 self.assertEqual(t.second, 0)
1774 self.assertEqual(t.microsecond, 0)
1775 self.failUnless(t.tzinfo is None)
1776
Tim Peters2a799bf2002-12-16 20:18:38 +00001777 def test_zones(self):
1778 est = FixedOffset(-300, "EST", 1)
1779 utc = FixedOffset(0, "UTC", -2)
1780 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001781 t1 = time( 7, 47, tzinfo=est)
1782 t2 = time(12, 47, tzinfo=utc)
1783 t3 = time(13, 47, tzinfo=met)
1784 t4 = time(microsecond=40)
1785 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00001786
1787 self.assertEqual(t1.tzinfo, est)
1788 self.assertEqual(t2.tzinfo, utc)
1789 self.assertEqual(t3.tzinfo, met)
1790 self.failUnless(t4.tzinfo is None)
1791 self.assertEqual(t5.tzinfo, utc)
1792
Tim Peters855fe882002-12-22 03:43:39 +00001793 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
1794 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
1795 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00001796 self.failUnless(t4.utcoffset() is None)
1797 self.assertRaises(TypeError, t1.utcoffset, "no args")
1798
1799 self.assertEqual(t1.tzname(), "EST")
1800 self.assertEqual(t2.tzname(), "UTC")
1801 self.assertEqual(t3.tzname(), "MET")
1802 self.failUnless(t4.tzname() is None)
1803 self.assertRaises(TypeError, t1.tzname, "no args")
1804
Tim Peters855fe882002-12-22 03:43:39 +00001805 self.assertEqual(t1.dst(), timedelta(minutes=1))
1806 self.assertEqual(t2.dst(), timedelta(minutes=-2))
1807 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00001808 self.failUnless(t4.dst() is None)
1809 self.assertRaises(TypeError, t1.dst, "no args")
1810
1811 self.assertEqual(hash(t1), hash(t2))
1812 self.assertEqual(hash(t1), hash(t3))
1813 self.assertEqual(hash(t2), hash(t3))
1814
1815 self.assertEqual(t1, t2)
1816 self.assertEqual(t1, t3)
1817 self.assertEqual(t2, t3)
1818 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
1819 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
1820 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
1821
1822 self.assertEqual(str(t1), "07:47:00-05:00")
1823 self.assertEqual(str(t2), "12:47:00+00:00")
1824 self.assertEqual(str(t3), "13:47:00+01:00")
1825 self.assertEqual(str(t4), "00:00:00.000040")
1826 self.assertEqual(str(t5), "00:00:00.000040+00:00")
1827
1828 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
1829 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
1830 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
1831 self.assertEqual(t4.isoformat(), "00:00:00.000040")
1832 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
1833
Tim Peters0bf60bd2003-01-08 20:40:01 +00001834 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00001835 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
1836 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
1837 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
1838 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
1839 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
1840
1841 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
1842 "07:47:00 %Z=EST %z=-0500")
1843 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
1844 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
1845
1846 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00001847 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00001848 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
1849 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
1850
Tim Petersb92bb712002-12-21 17:44:07 +00001851 # Check that an invalid tzname result raises an exception.
1852 class Badtzname(tzinfo):
1853 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00001854 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00001855 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
1856 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00001857
1858 def test_hash_edge_cases(self):
1859 # Offsets that overflow a basic time.
1860 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
1861 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
1862 self.assertEqual(hash(t1), hash(t2))
1863
1864 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
1865 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
1866 self.assertEqual(hash(t1), hash(t2))
1867
Tim Peters2a799bf2002-12-16 20:18:38 +00001868 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001869 # Try one without a tzinfo.
1870 args = 20, 59, 16, 64**2
1871 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001872 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001873 green = pickler.dumps(orig, proto)
1874 derived = unpickler.loads(green)
1875 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001876
1877 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00001878 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00001879 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001880 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001881 green = pickler.dumps(orig, proto)
1882 derived = unpickler.loads(green)
1883 self.assertEqual(orig, derived)
1884 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
1885 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
1886 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00001887
1888 def test_more_bool(self):
1889 # Test cases with non-None tzinfo.
1890 cls = self.theclass
1891
1892 t = cls(0, tzinfo=FixedOffset(-300, ""))
1893 self.failUnless(t)
1894
1895 t = cls(5, tzinfo=FixedOffset(-300, ""))
1896 self.failUnless(t)
1897
1898 t = cls(5, tzinfo=FixedOffset(300, ""))
1899 self.failUnless(not t)
1900
1901 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
1902 self.failUnless(not t)
1903
1904 # Mostly ensuring this doesn't overflow internally.
1905 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
1906 self.failUnless(t)
1907
1908 # But this should yield a value error -- the utcoffset is bogus.
1909 t = cls(0, tzinfo=FixedOffset(24*60, ""))
1910 self.assertRaises(ValueError, lambda: bool(t))
1911
1912 # Likewise.
1913 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
1914 self.assertRaises(ValueError, lambda: bool(t))
1915
Tim Peters12bf3392002-12-24 05:41:27 +00001916 def test_replace(self):
1917 cls = self.theclass
1918 z100 = FixedOffset(100, "+100")
1919 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
1920 args = [1, 2, 3, 4, z100]
1921 base = cls(*args)
1922 self.assertEqual(base, base.replace())
1923
1924 i = 0
1925 for name, newval in (("hour", 5),
1926 ("minute", 6),
1927 ("second", 7),
1928 ("microsecond", 8),
1929 ("tzinfo", zm200)):
1930 newargs = args[:]
1931 newargs[i] = newval
1932 expected = cls(*newargs)
1933 got = base.replace(**{name: newval})
1934 self.assertEqual(expected, got)
1935 i += 1
1936
1937 # Ensure we can get rid of a tzinfo.
1938 self.assertEqual(base.tzname(), "+100")
1939 base2 = base.replace(tzinfo=None)
1940 self.failUnless(base2.tzinfo is None)
1941 self.failUnless(base2.tzname() is None)
1942
1943 # Ensure we can add one.
1944 base3 = base2.replace(tzinfo=z100)
1945 self.assertEqual(base, base3)
1946 self.failUnless(base.tzinfo is base3.tzinfo)
1947
1948 # Out of bounds.
1949 base = cls(1)
1950 self.assertRaises(ValueError, base.replace, hour=24)
1951 self.assertRaises(ValueError, base.replace, minute=-1)
1952 self.assertRaises(ValueError, base.replace, second=100)
1953 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1954
Tim Peters60c76e42002-12-27 00:41:11 +00001955 def test_mixed_compare(self):
1956 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001957 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00001958 self.assertEqual(t1, t2)
1959 t2 = t2.replace(tzinfo=None)
1960 self.assertEqual(t1, t2)
1961 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
1962 self.assertEqual(t1, t2)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001963 if CMP_BUG_FIXED:
1964 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
1965 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00001966
Tim Peters0bf60bd2003-01-08 20:40:01 +00001967 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00001968 class Varies(tzinfo):
1969 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00001970 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00001971 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00001972 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00001973 return self.offset
1974
1975 v = Varies()
1976 t1 = t2.replace(tzinfo=v)
1977 t2 = t2.replace(tzinfo=v)
1978 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
1979 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
1980 self.assertEqual(t1, t2)
1981
1982 # But if they're not identical, it isn't ignored.
1983 t2 = t2.replace(tzinfo=Varies())
1984 self.failUnless(t1 < t2) # t1's offset counter still going up
1985
Tim Peters4c0db782002-12-26 05:01:19 +00001986
Tim Peters0bf60bd2003-01-08 20:40:01 +00001987# Testing datetime objects with a non-None tzinfo.
1988
Tim Peters855fe882002-12-22 03:43:39 +00001989class TestDateTimeTZ(TestDateTime, TZInfoBase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001990 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00001991
1992 def test_trivial(self):
1993 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
1994 self.assertEqual(dt.year, 1)
1995 self.assertEqual(dt.month, 2)
1996 self.assertEqual(dt.day, 3)
1997 self.assertEqual(dt.hour, 4)
1998 self.assertEqual(dt.minute, 5)
1999 self.assertEqual(dt.second, 6)
2000 self.assertEqual(dt.microsecond, 7)
2001 self.assertEqual(dt.tzinfo, None)
2002
2003 def test_even_more_compare(self):
2004 # The test_compare() and test_more_compare() inherited from TestDate
2005 # and TestDateTime covered non-tzinfo cases.
2006
2007 # Smallest possible after UTC adjustment.
2008 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2009 # Largest possible after UTC adjustment.
2010 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2011 tzinfo=FixedOffset(-1439, ""))
2012
2013 # Make sure those compare correctly, and w/o overflow.
2014 self.failUnless(t1 < t2)
2015 self.failUnless(t1 != t2)
2016 self.failUnless(t2 > t1)
2017
2018 self.failUnless(t1 == t1)
2019 self.failUnless(t2 == t2)
2020
2021 # Equal afer adjustment.
2022 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2023 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2024 self.assertEqual(t1, t2)
2025
2026 # Change t1 not to subtract a minute, and t1 should be larger.
2027 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2028 self.failUnless(t1 > t2)
2029
2030 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2031 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2032 self.failUnless(t1 < t2)
2033
2034 # Back to the original t1, but make seconds resolve it.
2035 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2036 second=1)
2037 self.failUnless(t1 > t2)
2038
2039 # Likewise, but make microseconds resolve it.
2040 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2041 microsecond=1)
2042 self.failUnless(t1 > t2)
2043
2044 # Make t2 naive and it should fail.
2045 t2 = self.theclass.min
2046 self.assertRaises(TypeError, lambda: t1 == t2)
2047 self.assertEqual(t2, t2)
2048
2049 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2050 class Naive(tzinfo):
2051 def utcoffset(self, dt): return None
2052 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2053 self.assertRaises(TypeError, lambda: t1 == t2)
2054 self.assertEqual(t2, t2)
2055
2056 # OTOH, it's OK to compare two of these mixing the two ways of being
2057 # naive.
2058 t1 = self.theclass(5, 6, 7)
2059 self.assertEqual(t1, t2)
2060
2061 # Try a bogus uctoffset.
2062 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002063 def utcoffset(self, dt):
2064 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002065 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2066 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002067 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002068
Tim Peters2a799bf2002-12-16 20:18:38 +00002069 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002070 # Try one without a tzinfo.
2071 args = 6, 7, 23, 20, 59, 1, 64**2
2072 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002073 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002074 green = pickler.dumps(orig, proto)
2075 derived = unpickler.loads(green)
2076 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002077
2078 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002079 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002080 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002081 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002082 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002083 green = pickler.dumps(orig, proto)
2084 derived = unpickler.loads(green)
2085 self.assertEqual(orig, derived)
2086 self.failUnless(isinstance(derived.tzinfo,
2087 PicklableFixedOffset))
2088 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2089 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002090
2091 def test_extreme_hashes(self):
2092 # If an attempt is made to hash these via subtracting the offset
2093 # then hashing a datetime object, OverflowError results. The
2094 # Python implementation used to blow up here.
2095 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2096 hash(t)
2097 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2098 tzinfo=FixedOffset(-1439, ""))
2099 hash(t)
2100
2101 # OTOH, an OOB offset should blow up.
2102 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2103 self.assertRaises(ValueError, hash, t)
2104
2105 def test_zones(self):
2106 est = FixedOffset(-300, "EST")
2107 utc = FixedOffset(0, "UTC")
2108 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002109 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2110 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2111 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002112 self.assertEqual(t1.tzinfo, est)
2113 self.assertEqual(t2.tzinfo, utc)
2114 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002115 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2116 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2117 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002118 self.assertEqual(t1.tzname(), "EST")
2119 self.assertEqual(t2.tzname(), "UTC")
2120 self.assertEqual(t3.tzname(), "MET")
2121 self.assertEqual(hash(t1), hash(t2))
2122 self.assertEqual(hash(t1), hash(t3))
2123 self.assertEqual(hash(t2), hash(t3))
2124 self.assertEqual(t1, t2)
2125 self.assertEqual(t1, t3)
2126 self.assertEqual(t2, t3)
2127 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2128 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2129 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002130 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002131 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2132 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2133 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2134
2135 def test_combine(self):
2136 met = FixedOffset(60, "MET")
2137 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002138 tz = time(18, 45, 3, 1234, tzinfo=met)
2139 dt = datetime.combine(d, tz)
2140 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002141 tzinfo=met))
2142
2143 def test_extract(self):
2144 met = FixedOffset(60, "MET")
2145 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2146 self.assertEqual(dt.date(), date(2002, 3, 4))
2147 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002148 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002149
2150 def test_tz_aware_arithmetic(self):
2151 import random
2152
2153 now = self.theclass.now()
2154 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002155 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002156 nowaware = self.theclass.combine(now.date(), timeaware)
2157 self.failUnless(nowaware.tzinfo is tz55)
2158 self.assertEqual(nowaware.timetz(), timeaware)
2159
2160 # Can't mix aware and non-aware.
2161 self.assertRaises(TypeError, lambda: now - nowaware)
2162 self.assertRaises(TypeError, lambda: nowaware - now)
2163
Tim Peters0bf60bd2003-01-08 20:40:01 +00002164 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002165 self.assertRaises(TypeError, lambda: now + nowaware)
2166 self.assertRaises(TypeError, lambda: nowaware + now)
2167 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2168
2169 # Subtracting should yield 0.
2170 self.assertEqual(now - now, timedelta(0))
2171 self.assertEqual(nowaware - nowaware, timedelta(0))
2172
2173 # Adding a delta should preserve tzinfo.
2174 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2175 nowawareplus = nowaware + delta
2176 self.failUnless(nowaware.tzinfo is tz55)
2177 nowawareplus2 = delta + nowaware
2178 self.failUnless(nowawareplus2.tzinfo is tz55)
2179 self.assertEqual(nowawareplus, nowawareplus2)
2180
2181 # that - delta should be what we started with, and that - what we
2182 # started with should be delta.
2183 diff = nowawareplus - delta
2184 self.failUnless(diff.tzinfo is tz55)
2185 self.assertEqual(nowaware, diff)
2186 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2187 self.assertEqual(nowawareplus - nowaware, delta)
2188
2189 # Make up a random timezone.
2190 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002191 # Attach it to nowawareplus.
2192 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002193 self.failUnless(nowawareplus.tzinfo is tzr)
2194 # Make sure the difference takes the timezone adjustments into account.
2195 got = nowaware - nowawareplus
2196 # Expected: (nowaware base - nowaware offset) -
2197 # (nowawareplus base - nowawareplus offset) =
2198 # (nowaware base - nowawareplus base) +
2199 # (nowawareplus offset - nowaware offset) =
2200 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002201 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002202 self.assertEqual(got, expected)
2203
2204 # Try max possible difference.
2205 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2206 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2207 tzinfo=FixedOffset(-1439, "max"))
2208 maxdiff = max - min
2209 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2210 timedelta(minutes=2*1439))
2211
2212 def test_tzinfo_now(self):
2213 meth = self.theclass.now
2214 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2215 base = meth()
2216 # Try with and without naming the keyword.
2217 off42 = FixedOffset(42, "42")
2218 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002219 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002220 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002221 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002222 # Bad argument with and w/o naming the keyword.
2223 self.assertRaises(TypeError, meth, 16)
2224 self.assertRaises(TypeError, meth, tzinfo=16)
2225 # Bad keyword name.
2226 self.assertRaises(TypeError, meth, tinfo=off42)
2227 # Too many args.
2228 self.assertRaises(TypeError, meth, off42, off42)
2229
Tim Peters10cadce2003-01-23 19:58:02 +00002230 # We don't know which time zone we're in, and don't have a tzinfo
2231 # class to represent it, so seeing whether a tz argument actually
2232 # does a conversion is tricky.
2233 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2234 utc = FixedOffset(0, "utc", 0)
2235 for dummy in range(3):
2236 now = datetime.now(weirdtz)
2237 self.failUnless(now.tzinfo is weirdtz)
2238 utcnow = datetime.utcnow().replace(tzinfo=utc)
2239 now2 = utcnow.astimezone(weirdtz)
2240 if abs(now - now2) < timedelta(seconds=30):
2241 break
2242 # Else the code is broken, or more than 30 seconds passed between
2243 # calls; assuming the latter, just try again.
2244 else:
2245 # Three strikes and we're out.
2246 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2247
Tim Peters2a799bf2002-12-16 20:18:38 +00002248 def test_tzinfo_fromtimestamp(self):
2249 import time
2250 meth = self.theclass.fromtimestamp
2251 ts = time.time()
2252 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2253 base = meth(ts)
2254 # Try with and without naming the keyword.
2255 off42 = FixedOffset(42, "42")
2256 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002257 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002258 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002259 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002260 # Bad argument with and w/o naming the keyword.
2261 self.assertRaises(TypeError, meth, ts, 16)
2262 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2263 # Bad keyword name.
2264 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2265 # Too many args.
2266 self.assertRaises(TypeError, meth, ts, off42, off42)
2267 # Too few args.
2268 self.assertRaises(TypeError, meth)
2269
Tim Peters2a44a8d2003-01-23 20:53:10 +00002270 # Try to make sure tz= actually does some conversion.
2271 timestamp = 1000000000 # 2001-09-09 01:46:40 UTC, give or take
2272 utc = FixedOffset(0, "utc", 0)
2273 expected = datetime(2001, 9, 9, 1, 46, 40)
2274 got = datetime.utcfromtimestamp(timestamp)
2275 # We don't support leap seconds, but maybe the platfrom insists
2276 # on using them, so don't demand exact equality).
2277 self.failUnless(abs(got - expected) < timedelta(minutes=1))
2278
2279 est = FixedOffset(-5*60, "est", 0)
2280 expected -= timedelta(hours=5)
2281 got = datetime.fromtimestamp(timestamp, est).replace(tzinfo=None)
2282 self.failUnless(abs(got - expected) < timedelta(minutes=1))
2283
Tim Peters2a799bf2002-12-16 20:18:38 +00002284 def test_tzinfo_utcnow(self):
2285 meth = self.theclass.utcnow
2286 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2287 base = meth()
2288 # Try with and without naming the keyword; for whatever reason,
2289 # utcnow() doesn't accept a tzinfo argument.
2290 off42 = FixedOffset(42, "42")
2291 self.assertRaises(TypeError, meth, off42)
2292 self.assertRaises(TypeError, meth, tzinfo=off42)
2293
2294 def test_tzinfo_utcfromtimestamp(self):
2295 import time
2296 meth = self.theclass.utcfromtimestamp
2297 ts = time.time()
2298 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2299 base = meth(ts)
2300 # Try with and without naming the keyword; for whatever reason,
2301 # utcfromtimestamp() doesn't accept a tzinfo argument.
2302 off42 = FixedOffset(42, "42")
2303 self.assertRaises(TypeError, meth, ts, off42)
2304 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2305
2306 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002307 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002308 # DST flag.
2309 class DST(tzinfo):
2310 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002311 if isinstance(dstvalue, int):
2312 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002313 self.dstvalue = dstvalue
2314 def dst(self, dt):
2315 return self.dstvalue
2316
2317 cls = self.theclass
2318 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2319 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2320 t = d.timetuple()
2321 self.assertEqual(1, t.tm_year)
2322 self.assertEqual(1, t.tm_mon)
2323 self.assertEqual(1, t.tm_mday)
2324 self.assertEqual(10, t.tm_hour)
2325 self.assertEqual(20, t.tm_min)
2326 self.assertEqual(30, t.tm_sec)
2327 self.assertEqual(0, t.tm_wday)
2328 self.assertEqual(1, t.tm_yday)
2329 self.assertEqual(flag, t.tm_isdst)
2330
2331 # dst() returns wrong type.
2332 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2333
2334 # dst() at the edge.
2335 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2336 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2337
2338 # dst() out of range.
2339 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2340 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2341
2342 def test_utctimetuple(self):
2343 class DST(tzinfo):
2344 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002345 if isinstance(dstvalue, int):
2346 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002347 self.dstvalue = dstvalue
2348 def dst(self, dt):
2349 return self.dstvalue
2350
2351 cls = self.theclass
2352 # This can't work: DST didn't implement utcoffset.
2353 self.assertRaises(NotImplementedError,
2354 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2355
2356 class UOFS(DST):
2357 def __init__(self, uofs, dofs=None):
2358 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002359 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002360 def utcoffset(self, dt):
2361 return self.uofs
2362
2363 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2364 # in effect for a UTC time.
2365 for dstvalue in -33, 33, 0, None:
2366 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2367 t = d.utctimetuple()
2368 self.assertEqual(d.year, t.tm_year)
2369 self.assertEqual(d.month, t.tm_mon)
2370 self.assertEqual(d.day, t.tm_mday)
2371 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2372 self.assertEqual(13, t.tm_min)
2373 self.assertEqual(d.second, t.tm_sec)
2374 self.assertEqual(d.weekday(), t.tm_wday)
2375 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2376 t.tm_yday)
2377 self.assertEqual(0, t.tm_isdst)
2378
2379 # At the edges, UTC adjustment can normalize into years out-of-range
2380 # for a datetime object. Ensure that a correct timetuple is
2381 # created anyway.
2382 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2383 # That goes back 1 minute less than a full day.
2384 t = tiny.utctimetuple()
2385 self.assertEqual(t.tm_year, MINYEAR-1)
2386 self.assertEqual(t.tm_mon, 12)
2387 self.assertEqual(t.tm_mday, 31)
2388 self.assertEqual(t.tm_hour, 0)
2389 self.assertEqual(t.tm_min, 1)
2390 self.assertEqual(t.tm_sec, 37)
2391 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2392 self.assertEqual(t.tm_isdst, 0)
2393
2394 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2395 # That goes forward 1 minute less than a full day.
2396 t = huge.utctimetuple()
2397 self.assertEqual(t.tm_year, MAXYEAR+1)
2398 self.assertEqual(t.tm_mon, 1)
2399 self.assertEqual(t.tm_mday, 1)
2400 self.assertEqual(t.tm_hour, 23)
2401 self.assertEqual(t.tm_min, 58)
2402 self.assertEqual(t.tm_sec, 37)
2403 self.assertEqual(t.tm_yday, 1)
2404 self.assertEqual(t.tm_isdst, 0)
2405
2406 def test_tzinfo_isoformat(self):
2407 zero = FixedOffset(0, "+00:00")
2408 plus = FixedOffset(220, "+03:40")
2409 minus = FixedOffset(-231, "-03:51")
2410 unknown = FixedOffset(None, "")
2411
2412 cls = self.theclass
2413 datestr = '0001-02-03'
2414 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002415 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002416 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2417 timestr = '04:05:59' + (us and '.987001' or '')
2418 ofsstr = ofs is not None and d.tzname() or ''
2419 tailstr = timestr + ofsstr
2420 iso = d.isoformat()
2421 self.assertEqual(iso, datestr + 'T' + tailstr)
2422 self.assertEqual(iso, d.isoformat('T'))
2423 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2424 self.assertEqual(str(d), datestr + ' ' + tailstr)
2425
Tim Peters12bf3392002-12-24 05:41:27 +00002426 def test_replace(self):
2427 cls = self.theclass
2428 z100 = FixedOffset(100, "+100")
2429 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2430 args = [1, 2, 3, 4, 5, 6, 7, z100]
2431 base = cls(*args)
2432 self.assertEqual(base, base.replace())
2433
2434 i = 0
2435 for name, newval in (("year", 2),
2436 ("month", 3),
2437 ("day", 4),
2438 ("hour", 5),
2439 ("minute", 6),
2440 ("second", 7),
2441 ("microsecond", 8),
2442 ("tzinfo", zm200)):
2443 newargs = args[:]
2444 newargs[i] = newval
2445 expected = cls(*newargs)
2446 got = base.replace(**{name: newval})
2447 self.assertEqual(expected, got)
2448 i += 1
2449
2450 # Ensure we can get rid of a tzinfo.
2451 self.assertEqual(base.tzname(), "+100")
2452 base2 = base.replace(tzinfo=None)
2453 self.failUnless(base2.tzinfo is None)
2454 self.failUnless(base2.tzname() is None)
2455
2456 # Ensure we can add one.
2457 base3 = base2.replace(tzinfo=z100)
2458 self.assertEqual(base, base3)
2459 self.failUnless(base.tzinfo is base3.tzinfo)
2460
2461 # Out of bounds.
2462 base = cls(2000, 2, 29)
2463 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002464
Tim Peters80475bb2002-12-25 07:40:55 +00002465 def test_more_astimezone(self):
2466 # The inherited test_astimezone covered some trivial and error cases.
2467 fnone = FixedOffset(None, "None")
2468 f44m = FixedOffset(44, "44")
2469 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2470
Tim Peters10cadce2003-01-23 19:58:02 +00002471 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002472 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002473 # Replacing with degenerate tzinfo raises an exception.
2474 self.assertRaises(ValueError, dt.astimezone, fnone)
2475 # Ditto with None tz.
2476 self.assertRaises(TypeError, dt.astimezone, None)
2477 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002478 x = dt.astimezone(dt.tzinfo)
2479 self.failUnless(x.tzinfo is f44m)
2480 self.assertEqual(x.date(), dt.date())
2481 self.assertEqual(x.time(), dt.time())
2482
2483 # Replacing with different tzinfo does adjust.
2484 got = dt.astimezone(fm5h)
2485 self.failUnless(got.tzinfo is fm5h)
2486 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2487 expected = dt - dt.utcoffset() # in effect, convert to UTC
2488 expected += fm5h.utcoffset(dt) # and from there to local time
2489 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2490 self.assertEqual(got.date(), expected.date())
2491 self.assertEqual(got.time(), expected.time())
2492 self.assertEqual(got.timetz(), expected.timetz())
2493 self.failUnless(got.tzinfo is expected.tzinfo)
2494 self.assertEqual(got, expected)
2495
Tim Peters4c0db782002-12-26 05:01:19 +00002496 def test_aware_subtract(self):
2497 cls = self.theclass
2498
Tim Peters60c76e42002-12-27 00:41:11 +00002499 # Ensure that utcoffset() is ignored when the operands have the
2500 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002501 class OperandDependentOffset(tzinfo):
2502 def utcoffset(self, t):
2503 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002504 # d0 and d1 equal after adjustment
2505 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002506 else:
Tim Peters397301e2003-01-02 21:28:08 +00002507 # d2 off in the weeds
2508 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002509
2510 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2511 d0 = base.replace(minute=3)
2512 d1 = base.replace(minute=9)
2513 d2 = base.replace(minute=11)
2514 for x in d0, d1, d2:
2515 for y in d0, d1, d2:
2516 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002517 expected = timedelta(minutes=x.minute - y.minute)
2518 self.assertEqual(got, expected)
2519
2520 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2521 # ignored.
2522 base = cls(8, 9, 10, 11, 12, 13, 14)
2523 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2524 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2525 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2526 for x in d0, d1, d2:
2527 for y in d0, d1, d2:
2528 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002529 if (x is d0 or x is d1) and (y is d0 or y is d1):
2530 expected = timedelta(0)
2531 elif x is y is d2:
2532 expected = timedelta(0)
2533 elif x is d2:
2534 expected = timedelta(minutes=(11-59)-0)
2535 else:
2536 assert y is d2
2537 expected = timedelta(minutes=0-(11-59))
2538 self.assertEqual(got, expected)
2539
Tim Peters60c76e42002-12-27 00:41:11 +00002540 def test_mixed_compare(self):
2541 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002542 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002543 self.assertEqual(t1, t2)
2544 t2 = t2.replace(tzinfo=None)
2545 self.assertEqual(t1, t2)
2546 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2547 self.assertEqual(t1, t2)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002548 if CMP_BUG_FIXED:
2549 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2550 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002551
Tim Peters0bf60bd2003-01-08 20:40:01 +00002552 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002553 class Varies(tzinfo):
2554 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002555 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002556 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002557 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002558 return self.offset
2559
2560 v = Varies()
2561 t1 = t2.replace(tzinfo=v)
2562 t2 = t2.replace(tzinfo=v)
2563 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2564 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2565 self.assertEqual(t1, t2)
2566
2567 # But if they're not identical, it isn't ignored.
2568 t2 = t2.replace(tzinfo=Varies())
2569 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002570
Tim Peters621818b2002-12-29 23:44:49 +00002571# Pain to set up DST-aware tzinfo classes.
2572
2573def first_sunday_on_or_after(dt):
2574 days_to_go = 6 - dt.weekday()
2575 if days_to_go:
2576 dt += timedelta(days_to_go)
2577 return dt
2578
2579ZERO = timedelta(0)
2580HOUR = timedelta(hours=1)
2581DAY = timedelta(days=1)
2582# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2583DSTSTART = datetime(1, 4, 1, 2)
2584# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002585# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2586# being standard time on that day, there is no spelling in local time of
2587# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2588DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002589
2590class USTimeZone(tzinfo):
2591
2592 def __init__(self, hours, reprname, stdname, dstname):
2593 self.stdoffset = timedelta(hours=hours)
2594 self.reprname = reprname
2595 self.stdname = stdname
2596 self.dstname = dstname
2597
2598 def __repr__(self):
2599 return self.reprname
2600
2601 def tzname(self, dt):
2602 if self.dst(dt):
2603 return self.dstname
2604 else:
2605 return self.stdname
2606
2607 def utcoffset(self, dt):
2608 return self.stdoffset + self.dst(dt)
2609
2610 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00002611 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00002612 # An exception instead may be sensible here, in one or more of
2613 # the cases.
2614 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00002615 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00002616
2617 # Find first Sunday in April.
2618 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
2619 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
2620
2621 # Find last Sunday in October.
2622 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
2623 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
2624
Tim Peters621818b2002-12-29 23:44:49 +00002625 # Can't compare naive to aware objects, so strip the timezone from
2626 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00002627 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00002628 return HOUR
2629 else:
2630 return ZERO
2631
Tim Peters521fc152002-12-31 17:36:56 +00002632Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
2633Central = USTimeZone(-6, "Central", "CST", "CDT")
2634Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
2635Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00002636utc_real = FixedOffset(0, "UTC", 0)
2637# For better test coverage, we want another flavor of UTC that's west of
2638# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00002639utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00002640
2641class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00002642 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002643 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00002644 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002645
Tim Peters0bf60bd2003-01-08 20:40:01 +00002646 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00002647
Tim Peters521fc152002-12-31 17:36:56 +00002648 # Check a time that's inside DST.
2649 def checkinside(self, dt, tz, utc, dston, dstoff):
2650 self.assertEqual(dt.dst(), HOUR)
2651
2652 # Conversion to our own timezone is always an identity.
2653 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00002654
2655 asutc = dt.astimezone(utc)
2656 there_and_back = asutc.astimezone(tz)
2657
2658 # Conversion to UTC and back isn't always an identity here,
2659 # because there are redundant spellings (in local time) of
2660 # UTC time when DST begins: the clock jumps from 1:59:59
2661 # to 3:00:00, and a local time of 2:MM:SS doesn't really
2662 # make sense then. The classes above treat 2:MM:SS as
2663 # daylight time then (it's "after 2am"), really an alias
2664 # for 1:MM:SS standard time. The latter form is what
2665 # conversion back from UTC produces.
2666 if dt.date() == dston.date() and dt.hour == 2:
2667 # We're in the redundant hour, and coming back from
2668 # UTC gives the 1:MM:SS standard-time spelling.
2669 self.assertEqual(there_and_back + HOUR, dt)
2670 # Although during was considered to be in daylight
2671 # time, there_and_back is not.
2672 self.assertEqual(there_and_back.dst(), ZERO)
2673 # They're the same times in UTC.
2674 self.assertEqual(there_and_back.astimezone(utc),
2675 dt.astimezone(utc))
2676 else:
2677 # We're not in the redundant hour.
2678 self.assertEqual(dt, there_and_back)
2679
Tim Peters327098a2003-01-20 22:54:38 +00002680 # Because we have a redundant spelling when DST begins, there is
2681 # (unforunately) an hour when DST ends that can't be spelled at all in
2682 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
2683 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
2684 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
2685 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
2686 # expressed in local time. Nevertheless, we want conversion back
2687 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00002688 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00002689 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00002690 if dt.date() == dstoff.date() and dt.hour == 0:
2691 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00002692 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00002693 self.assertEqual(nexthour_tz, dt.replace(hour=1))
2694 nexthour_utc += HOUR
2695 nexthour_tz = nexthour_utc.astimezone(tz)
2696 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00002697 else:
Tim Peters327098a2003-01-20 22:54:38 +00002698 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00002699
2700 # Check a time that's outside DST.
2701 def checkoutside(self, dt, tz, utc):
2702 self.assertEqual(dt.dst(), ZERO)
2703
2704 # Conversion to our own timezone is always an identity.
2705 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00002706
2707 # Converting to UTC and back is an identity too.
2708 asutc = dt.astimezone(utc)
2709 there_and_back = asutc.astimezone(tz)
2710 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00002711
Tim Peters1024bf82002-12-30 17:09:40 +00002712 def convert_between_tz_and_utc(self, tz, utc):
2713 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00002714 # Because 1:MM on the day DST ends is taken as being standard time,
2715 # there is no spelling in tz for the last hour of daylight time.
2716 # For purposes of the test, the last hour of DST is 0:MM, which is
2717 # taken as being daylight time (and 1:MM is taken as being standard
2718 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00002719 dstoff = self.dstoff.replace(tzinfo=tz)
2720 for delta in (timedelta(weeks=13),
2721 DAY,
2722 HOUR,
2723 timedelta(minutes=1),
2724 timedelta(microseconds=1)):
2725
Tim Peters521fc152002-12-31 17:36:56 +00002726 self.checkinside(dston, tz, utc, dston, dstoff)
2727 for during in dston + delta, dstoff - delta:
2728 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00002729
Tim Peters521fc152002-12-31 17:36:56 +00002730 self.checkoutside(dstoff, tz, utc)
2731 for outside in dston - delta, dstoff + delta:
2732 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00002733
Tim Peters621818b2002-12-29 23:44:49 +00002734 def test_easy(self):
2735 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00002736 self.convert_between_tz_and_utc(Eastern, utc_real)
2737 self.convert_between_tz_and_utc(Pacific, utc_real)
2738 self.convert_between_tz_and_utc(Eastern, utc_fake)
2739 self.convert_between_tz_and_utc(Pacific, utc_fake)
2740 # The next is really dancing near the edge. It works because
2741 # Pacific and Eastern are far enough apart that their "problem
2742 # hours" don't overlap.
2743 self.convert_between_tz_and_utc(Eastern, Pacific)
2744 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00002745 # OTOH, these fail! Don't enable them. The difficulty is that
2746 # the edge case tests assume that every hour is representable in
2747 # the "utc" class. This is always true for a fixed-offset tzinfo
2748 # class (lke utc_real and utc_fake), but not for Eastern or Central.
2749 # For these adjacent DST-aware time zones, the range of time offsets
2750 # tested ends up creating hours in the one that aren't representable
2751 # in the other. For the same reason, we would see failures in the
2752 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
2753 # offset deltas in convert_between_tz_and_utc().
2754 #
2755 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
2756 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00002757
Tim Petersf3615152003-01-01 21:51:37 +00002758 def test_tricky(self):
2759 # 22:00 on day before daylight starts.
2760 fourback = self.dston - timedelta(hours=4)
2761 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00002762 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00002763 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
2764 # 2", we should get the 3 spelling.
2765 # If we plug 22:00 the day before into Eastern, it "looks like std
2766 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
2767 # to 22:00 lands on 2:00, which makes no sense in local time (the
2768 # local clock jumps from 1 to 3). The point here is to make sure we
2769 # get the 3 spelling.
2770 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00002771 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00002772 self.assertEqual(expected, got)
2773
2774 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
2775 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00002776 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00002777 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
2778 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
2779 # spelling.
2780 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00002781 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00002782 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00002783
Tim Petersadf64202003-01-04 06:03:15 +00002784 # Now on the day DST ends, we want "repeat an hour" behavior.
2785 # UTC 4:MM 5:MM 6:MM 7:MM checking these
2786 # EST 23:MM 0:MM 1:MM 2:MM
2787 # EDT 0:MM 1:MM 2:MM 3:MM
2788 # wall 0:MM 1:MM 1:MM 2:MM against these
2789 for utc in utc_real, utc_fake:
2790 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00002791 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00002792 # Convert that to UTC.
2793 first_std_hour -= tz.utcoffset(None)
2794 # Adjust for possibly fake UTC.
2795 asutc = first_std_hour + utc.utcoffset(None)
2796 # First UTC hour to convert; this is 4:00 when utc=utc_real &
2797 # tz=Eastern.
2798 asutcbase = asutc.replace(tzinfo=utc)
2799 for tzhour in (0, 1, 1, 2):
2800 expectedbase = self.dstoff.replace(hour=tzhour)
2801 for minute in 0, 30, 59:
2802 expected = expectedbase.replace(minute=minute)
2803 asutc = asutcbase.replace(minute=minute)
2804 astz = asutc.astimezone(tz)
2805 self.assertEqual(astz.replace(tzinfo=None), expected)
2806 asutcbase += HOUR
2807
2808
Tim Peters710fb152003-01-02 19:35:54 +00002809 def test_bogus_dst(self):
2810 class ok(tzinfo):
2811 def utcoffset(self, dt): return HOUR
2812 def dst(self, dt): return HOUR
2813
2814 now = self.theclass.now().replace(tzinfo=utc_real)
2815 # Doesn't blow up.
2816 now.astimezone(ok())
2817
2818 # Does blow up.
2819 class notok(ok):
2820 def dst(self, dt): return None
2821 self.assertRaises(ValueError, now.astimezone, notok())
2822
Tim Peters52dcce22003-01-23 16:36:11 +00002823 def test_fromutc(self):
2824 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
2825 now = datetime.utcnow().replace(tzinfo=utc_real)
2826 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
2827 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
2828 enow = Eastern.fromutc(now) # doesn't blow up
2829 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
2830 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
2831 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
2832
2833 # Always converts UTC to standard time.
2834 class FauxUSTimeZone(USTimeZone):
2835 def fromutc(self, dt):
2836 return dt + self.stdoffset
2837 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
2838
2839 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
2840 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
2841 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
2842
2843 # Check around DST start.
2844 start = self.dston.replace(hour=4, tzinfo=Eastern)
2845 fstart = start.replace(tzinfo=FEastern)
2846 for wall in 23, 0, 1, 3, 4, 5:
2847 expected = start.replace(hour=wall)
2848 if wall == 23:
2849 expected -= timedelta(days=1)
2850 got = Eastern.fromutc(start)
2851 self.assertEqual(expected, got)
2852
2853 expected = fstart + FEastern.stdoffset
2854 got = FEastern.fromutc(fstart)
2855 self.assertEqual(expected, got)
2856
2857 # Ensure astimezone() calls fromutc() too.
2858 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
2859 self.assertEqual(expected, got)
2860
2861 start += HOUR
2862 fstart += HOUR
2863
2864 # Check around DST end.
2865 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
2866 fstart = start.replace(tzinfo=FEastern)
2867 for wall in 0, 1, 1, 2, 3, 4:
2868 expected = start.replace(hour=wall)
2869 got = Eastern.fromutc(start)
2870 self.assertEqual(expected, got)
2871
2872 expected = fstart + FEastern.stdoffset
2873 got = FEastern.fromutc(fstart)
2874 self.assertEqual(expected, got)
2875
2876 # Ensure astimezone() calls fromutc() too.
2877 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
2878 self.assertEqual(expected, got)
2879
2880 start += HOUR
2881 fstart += HOUR
2882
Tim Peters710fb152003-01-02 19:35:54 +00002883
Tim Peterscfd4a8b2002-12-16 21:12:37 +00002884def test_suite():
Tim Peters2a799bf2002-12-16 20:18:38 +00002885 allsuites = [unittest.makeSuite(klass, 'test')
2886 for klass in (TestModule,
2887 TestTZInfo,
2888 TestTimeDelta,
2889 TestDateOnly,
2890 TestDate,
2891 TestDateTime,
2892 TestTime,
2893 TestTimeTZ,
2894 TestDateTimeTZ,
Tim Peters621818b2002-12-29 23:44:49 +00002895 TestTimezoneConversions,
Tim Peters2a799bf2002-12-16 20:18:38 +00002896 )
2897 ]
2898 return unittest.TestSuite(allsuites)
2899
2900def test_main():
2901 import gc
2902 import sys
2903
Tim Peterscfd4a8b2002-12-16 21:12:37 +00002904 thesuite = test_suite()
Tim Peters2a799bf2002-12-16 20:18:38 +00002905 lastrc = None
2906 while True:
2907 test_support.run_suite(thesuite)
2908 if 1: # change to 0, under a debug build, for some leak detection
2909 break
2910 gc.collect()
2911 if gc.garbage:
2912 raise SystemError("gc.garbage not empty after test run: %r" %
2913 gc.garbage)
2914 if hasattr(sys, 'gettotalrefcount'):
2915 thisrc = sys.gettotalrefcount()
2916 print >> sys.stderr, '*' * 10, 'total refs:', thisrc,
2917 if lastrc:
2918 print >> sys.stderr, 'delta:', thisrc - lastrc
2919 else:
2920 print >> sys.stderr
2921 lastrc = thisrc
2922
2923if __name__ == "__main__":
2924 test_main()