blob: 712a3497dd7b0b7d1d5cd94b44bde0eff78b0247 [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
Tim Peters35ad6412003-02-05 04:08:07 +000019pickle_choices = [(pickler, unpickler, proto)
20 for pickler in pickle, cPickle
21 for unpickler in pickle, cPickle
22 for proto in range(3)]
23assert len(pickle_choices) == 2*2*3
Guido van Rossum177e41a2003-01-30 22:06:23 +000024
Tim Peters0bf60bd2003-01-08 20:40:01 +000025# XXX The test suite uncovered a bug in Python 2.2.2: if x and y are
26# XXX instances of new-style classes (like date and time) that both
27# XXX define __cmp__, and x is compared to y, and one of the __cmp__
28# XXX implementations raises an exception, the exception can get dropped
29# XXX on the floor when it occurs, and pop up again at some "random" time
30# XXX later (it depends on when the next opcode gets executed that
31# XXX bothers to check). There isn't a workaround for this, so instead
32# XXX we disable the parts of the tests that trigger it unless
33# XXX CMP_BUG_FIXED is true. The bug is still there, we simply avoid
34# XXX provoking it here.
35# XXX Guido checked into a fix that will go into 2.2.3. The bug was
36# XXX already fixed in 2.3 CVS via a different means.
37CMP_BUG_FIXED = sys.version_info >= (2, 2, 3)
38
Tim Peters2a799bf2002-12-16 20:18:38 +000039
40#############################################################################
41# module tests
42
43class TestModule(unittest.TestCase):
44
45 def test_constants(self):
46 import datetime
47 self.assertEqual(datetime.MINYEAR, 1)
48 self.assertEqual(datetime.MAXYEAR, 9999)
49
50#############################################################################
51# tzinfo tests
52
53class FixedOffset(tzinfo):
54 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000055 if isinstance(offset, int):
56 offset = timedelta(minutes=offset)
57 if isinstance(dstoffset, int):
58 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000059 self.__offset = offset
60 self.__name = name
61 self.__dstoffset = dstoffset
62 def __repr__(self):
63 return self.__name.lower()
64 def utcoffset(self, dt):
65 return self.__offset
66 def tzname(self, dt):
67 return self.__name
68 def dst(self, dt):
69 return self.__dstoffset
70
Tim Petersfb8472c2002-12-21 05:04:42 +000071class PicklableFixedOffset(FixedOffset):
72 def __init__(self, offset=None, name=None, dstoffset=None):
73 FixedOffset.__init__(self, offset, name, dstoffset)
74
Tim Peters2a799bf2002-12-16 20:18:38 +000075class TestTZInfo(unittest.TestCase):
76
77 def test_non_abstractness(self):
78 # In order to allow subclasses to get pickled, the C implementation
79 # wasn't able to get away with having __init__ raise
80 # NotImplementedError.
81 useless = tzinfo()
82 dt = datetime.max
83 self.assertRaises(NotImplementedError, useless.tzname, dt)
84 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
85 self.assertRaises(NotImplementedError, useless.dst, dt)
86
87 def test_subclass_must_override(self):
88 class NotEnough(tzinfo):
89 def __init__(self, offset, name):
90 self.__offset = offset
91 self.__name = name
92 self.failUnless(issubclass(NotEnough, tzinfo))
93 ne = NotEnough(3, "NotByALongShot")
94 self.failUnless(isinstance(ne, tzinfo))
95
96 dt = datetime.now()
97 self.assertRaises(NotImplementedError, ne.tzname, dt)
98 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
99 self.assertRaises(NotImplementedError, ne.dst, dt)
100
101 def test_normal(self):
102 fo = FixedOffset(3, "Three")
103 self.failUnless(isinstance(fo, tzinfo))
104 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000105 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000106 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000107 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000108
109 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000110 # There's no point to pickling tzinfo objects on their own (they
111 # carry no data), but they need to be picklable anyway else
112 # concrete subclasses can't be pickled.
113 orig = tzinfo.__new__(tzinfo)
114 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000115 for pickler, unpickler, proto in pickle_choices:
116 green = pickler.dumps(orig, proto)
117 derived = unpickler.loads(green)
Tim Peters2a799bf2002-12-16 20:18:38 +0000118 self.failUnless(type(derived) is tzinfo)
119
120 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000121 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000122 offset = timedelta(minutes=-300)
123 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000124 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000125 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000126 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000127 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000128 for pickler, unpickler, proto in pickle_choices:
129 green = pickler.dumps(orig, proto)
130 derived = unpickler.loads(green)
Tim Peters2a799bf2002-12-16 20:18:38 +0000131 self.failUnless(isinstance(derived, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000132 self.failUnless(type(derived) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000133 self.assertEqual(derived.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000134 self.assertEqual(derived.tzname(None), 'cookie')
135
136#############################################################################
137# timedelta tests
138
139class TestTimeDelta(unittest.TestCase):
140
141 def test_constructor(self):
142 eq = self.assertEqual
143 td = timedelta
144
145 # Check keyword args to constructor
146 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
147 milliseconds=0, microseconds=0))
148 eq(td(1), td(days=1))
149 eq(td(0, 1), td(seconds=1))
150 eq(td(0, 0, 1), td(microseconds=1))
151 eq(td(weeks=1), td(days=7))
152 eq(td(days=1), td(hours=24))
153 eq(td(hours=1), td(minutes=60))
154 eq(td(minutes=1), td(seconds=60))
155 eq(td(seconds=1), td(milliseconds=1000))
156 eq(td(milliseconds=1), td(microseconds=1000))
157
158 # Check float args to constructor
159 eq(td(weeks=1.0/7), td(days=1))
160 eq(td(days=1.0/24), td(hours=1))
161 eq(td(hours=1.0/60), td(minutes=1))
162 eq(td(minutes=1.0/60), td(seconds=1))
163 eq(td(seconds=0.001), td(milliseconds=1))
164 eq(td(milliseconds=0.001), td(microseconds=1))
165
166 def test_computations(self):
167 eq = self.assertEqual
168 td = timedelta
169
170 a = td(7) # One week
171 b = td(0, 60) # One minute
172 c = td(0, 0, 1000) # One millisecond
173 eq(a+b+c, td(7, 60, 1000))
174 eq(a-b, td(6, 24*3600 - 60))
175 eq(-a, td(-7))
176 eq(+a, td(7))
177 eq(-b, td(-1, 24*3600 - 60))
178 eq(-c, td(-1, 24*3600 - 1, 999000))
179 eq(abs(a), a)
180 eq(abs(-a), a)
181 eq(td(6, 24*3600), a)
182 eq(td(0, 0, 60*1000000), b)
183 eq(a*10, td(70))
184 eq(a*10, 10*a)
185 eq(a*10L, 10*a)
186 eq(b*10, td(0, 600))
187 eq(10*b, td(0, 600))
188 eq(b*10L, td(0, 600))
189 eq(c*10, td(0, 0, 10000))
190 eq(10*c, td(0, 0, 10000))
191 eq(c*10L, td(0, 0, 10000))
192 eq(a*-1, -a)
193 eq(b*-2, -b-b)
194 eq(c*-2, -c+-c)
195 eq(b*(60*24), (b*60)*24)
196 eq(b*(60*24), (60*b)*24)
197 eq(c*1000, td(0, 1))
198 eq(1000*c, td(0, 1))
199 eq(a//7, td(1))
200 eq(b//10, td(0, 6))
201 eq(c//1000, td(0, 0, 1))
202 eq(a//10, td(0, 7*24*360))
203 eq(a//3600000, td(0, 0, 7*24*1000))
204
205 def test_disallowed_computations(self):
206 a = timedelta(42)
207
208 # Add/sub ints, longs, floats should be illegal
209 for i in 1, 1L, 1.0:
210 self.assertRaises(TypeError, lambda: a+i)
211 self.assertRaises(TypeError, lambda: a-i)
212 self.assertRaises(TypeError, lambda: i+a)
213 self.assertRaises(TypeError, lambda: i-a)
214
215 # Mul/div by float isn't supported.
216 x = 2.3
217 self.assertRaises(TypeError, lambda: a*x)
218 self.assertRaises(TypeError, lambda: x*a)
219 self.assertRaises(TypeError, lambda: a/x)
220 self.assertRaises(TypeError, lambda: x/a)
221 self.assertRaises(TypeError, lambda: a // x)
222 self.assertRaises(TypeError, lambda: x // a)
223
224 # Divison of int by timedelta doesn't make sense.
225 # Division by zero doesn't make sense.
226 for zero in 0, 0L:
227 self.assertRaises(TypeError, lambda: zero // a)
228 self.assertRaises(ZeroDivisionError, lambda: a // zero)
229
230 def test_basic_attributes(self):
231 days, seconds, us = 1, 7, 31
232 td = timedelta(days, seconds, us)
233 self.assertEqual(td.days, days)
234 self.assertEqual(td.seconds, seconds)
235 self.assertEqual(td.microseconds, us)
236
237 def test_carries(self):
238 t1 = timedelta(days=100,
239 weeks=-7,
240 hours=-24*(100-49),
241 minutes=-3,
242 seconds=12,
243 microseconds=(3*60 - 12) * 1e6 + 1)
244 t2 = timedelta(microseconds=1)
245 self.assertEqual(t1, t2)
246
247 def test_hash_equality(self):
248 t1 = timedelta(days=100,
249 weeks=-7,
250 hours=-24*(100-49),
251 minutes=-3,
252 seconds=12,
253 microseconds=(3*60 - 12) * 1000000)
254 t2 = timedelta()
255 self.assertEqual(hash(t1), hash(t2))
256
257 t1 += timedelta(weeks=7)
258 t2 += timedelta(days=7*7)
259 self.assertEqual(t1, t2)
260 self.assertEqual(hash(t1), hash(t2))
261
262 d = {t1: 1}
263 d[t2] = 2
264 self.assertEqual(len(d), 1)
265 self.assertEqual(d[t1], 2)
266
267 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000268 args = 12, 34, 56
269 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000270 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000271 green = pickler.dumps(orig, proto)
272 derived = unpickler.loads(green)
273 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000274
275 def test_compare(self):
276 t1 = timedelta(2, 3, 4)
277 t2 = timedelta(2, 3, 4)
278 self.failUnless(t1 == t2)
279 self.failUnless(t1 <= t2)
280 self.failUnless(t1 >= t2)
281 self.failUnless(not t1 != t2)
282 self.failUnless(not t1 < t2)
283 self.failUnless(not t1 > t2)
284 self.assertEqual(cmp(t1, t2), 0)
285 self.assertEqual(cmp(t2, t1), 0)
286
287 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
288 t2 = timedelta(*args) # this is larger than t1
289 self.failUnless(t1 < t2)
290 self.failUnless(t2 > t1)
291 self.failUnless(t1 <= t2)
292 self.failUnless(t2 >= t1)
293 self.failUnless(t1 != t2)
294 self.failUnless(t2 != t1)
295 self.failUnless(not t1 == t2)
296 self.failUnless(not t2 == t1)
297 self.failUnless(not t1 > t2)
298 self.failUnless(not t2 < t1)
299 self.failUnless(not t1 >= t2)
300 self.failUnless(not t2 <= t1)
301 self.assertEqual(cmp(t1, t2), -1)
302 self.assertEqual(cmp(t2, t1), 1)
303
304 for badarg in 10, 10L, 34.5, "abc", {}, [], ():
305 self.assertRaises(TypeError, lambda: t1 == badarg)
306 self.assertRaises(TypeError, lambda: t1 != badarg)
307 self.assertRaises(TypeError, lambda: t1 <= badarg)
308 self.assertRaises(TypeError, lambda: t1 < badarg)
309 self.assertRaises(TypeError, lambda: t1 > badarg)
310 self.assertRaises(TypeError, lambda: t1 >= badarg)
311 self.assertRaises(TypeError, lambda: badarg == t1)
312 self.assertRaises(TypeError, lambda: badarg != t1)
313 self.assertRaises(TypeError, lambda: badarg <= t1)
314 self.assertRaises(TypeError, lambda: badarg < t1)
315 self.assertRaises(TypeError, lambda: badarg > t1)
316 self.assertRaises(TypeError, lambda: badarg >= t1)
317
318 def test_str(self):
319 td = timedelta
320 eq = self.assertEqual
321
322 eq(str(td(1)), "1 day, 0:00:00")
323 eq(str(td(-1)), "-1 day, 0:00:00")
324 eq(str(td(2)), "2 days, 0:00:00")
325 eq(str(td(-2)), "-2 days, 0:00:00")
326
327 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
328 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
329 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
330 "-210 days, 23:12:34")
331
332 eq(str(td(milliseconds=1)), "0:00:00.001000")
333 eq(str(td(microseconds=3)), "0:00:00.000003")
334
335 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
336 microseconds=999999)),
337 "999999999 days, 23:59:59.999999")
338
339 def test_roundtrip(self):
340 for td in (timedelta(days=999999999, hours=23, minutes=59,
341 seconds=59, microseconds=999999),
342 timedelta(days=-999999999),
343 timedelta(days=1, seconds=2, microseconds=3)):
344
345 # Verify td -> string -> td identity.
346 s = repr(td)
347 self.failUnless(s.startswith('datetime.'))
348 s = s[9:]
349 td2 = eval(s)
350 self.assertEqual(td, td2)
351
352 # Verify identity via reconstructing from pieces.
353 td2 = timedelta(td.days, td.seconds, td.microseconds)
354 self.assertEqual(td, td2)
355
356 def test_resolution_info(self):
357 self.assert_(isinstance(timedelta.min, timedelta))
358 self.assert_(isinstance(timedelta.max, timedelta))
359 self.assert_(isinstance(timedelta.resolution, timedelta))
360 self.assert_(timedelta.max > timedelta.min)
361 self.assertEqual(timedelta.min, timedelta(-999999999))
362 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
363 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
364
365 def test_overflow(self):
366 tiny = timedelta.resolution
367
368 td = timedelta.min + tiny
369 td -= tiny # no problem
370 self.assertRaises(OverflowError, td.__sub__, tiny)
371 self.assertRaises(OverflowError, td.__add__, -tiny)
372
373 td = timedelta.max - tiny
374 td += tiny # no problem
375 self.assertRaises(OverflowError, td.__add__, tiny)
376 self.assertRaises(OverflowError, td.__sub__, -tiny)
377
378 self.assertRaises(OverflowError, lambda: -timedelta.max)
379
380 def test_microsecond_rounding(self):
381 td = timedelta
382 eq = self.assertEqual
383
384 # Single-field rounding.
385 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
386 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
387 eq(td(milliseconds=0.6/1000), td(microseconds=1))
388 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
389
390 # Rounding due to contributions from more than one field.
391 us_per_hour = 3600e6
392 us_per_day = us_per_hour * 24
393 eq(td(days=.4/us_per_day), td(0))
394 eq(td(hours=.2/us_per_hour), td(0))
395 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
396
397 eq(td(days=-.4/us_per_day), td(0))
398 eq(td(hours=-.2/us_per_hour), td(0))
399 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
400
401 def test_massive_normalization(self):
402 td = timedelta(microseconds=-1)
403 self.assertEqual((td.days, td.seconds, td.microseconds),
404 (-1, 24*3600-1, 999999))
405
406 def test_bool(self):
407 self.failUnless(timedelta(1))
408 self.failUnless(timedelta(0, 1))
409 self.failUnless(timedelta(0, 0, 1))
410 self.failUnless(timedelta(microseconds=1))
411 self.failUnless(not timedelta(0))
412
413#############################################################################
414# date tests
415
416class TestDateOnly(unittest.TestCase):
417 # Tests here won't pass if also run on datetime objects, so don't
418 # subclass this to test datetimes too.
419
420 def test_delta_non_days_ignored(self):
421 dt = date(2000, 1, 2)
422 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
423 microseconds=5)
424 days = timedelta(delta.days)
425 self.assertEqual(days, timedelta(1))
426
427 dt2 = dt + delta
428 self.assertEqual(dt2, dt + days)
429
430 dt2 = delta + dt
431 self.assertEqual(dt2, dt + days)
432
433 dt2 = dt - delta
434 self.assertEqual(dt2, dt - days)
435
436 delta = -delta
437 days = timedelta(delta.days)
438 self.assertEqual(days, timedelta(-2))
439
440 dt2 = dt + delta
441 self.assertEqual(dt2, dt + days)
442
443 dt2 = delta + dt
444 self.assertEqual(dt2, dt + days)
445
446 dt2 = dt - delta
447 self.assertEqual(dt2, dt - days)
448
449class TestDate(unittest.TestCase):
450 # Tests here should pass for both dates and datetimes, except for a
451 # few tests that TestDateTime overrides.
452
453 theclass = date
454
455 def test_basic_attributes(self):
456 dt = self.theclass(2002, 3, 1)
457 self.assertEqual(dt.year, 2002)
458 self.assertEqual(dt.month, 3)
459 self.assertEqual(dt.day, 1)
460
461 def test_roundtrip(self):
462 for dt in (self.theclass(1, 2, 3),
463 self.theclass.today()):
464 # Verify dt -> string -> date identity.
465 s = repr(dt)
466 self.failUnless(s.startswith('datetime.'))
467 s = s[9:]
468 dt2 = eval(s)
469 self.assertEqual(dt, dt2)
470
471 # Verify identity via reconstructing from pieces.
472 dt2 = self.theclass(dt.year, dt.month, dt.day)
473 self.assertEqual(dt, dt2)
474
475 def test_ordinal_conversions(self):
476 # Check some fixed values.
477 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
478 (1, 12, 31, 365),
479 (2, 1, 1, 366),
480 # first example from "Calendrical Calculations"
481 (1945, 11, 12, 710347)]:
482 d = self.theclass(y, m, d)
483 self.assertEqual(n, d.toordinal())
484 fromord = self.theclass.fromordinal(n)
485 self.assertEqual(d, fromord)
486 if hasattr(fromord, "hour"):
487 # if we're checking something fancier than a date, verify
488 # the extra fields have been zeroed out
489 self.assertEqual(fromord.hour, 0)
490 self.assertEqual(fromord.minute, 0)
491 self.assertEqual(fromord.second, 0)
492 self.assertEqual(fromord.microsecond, 0)
493
Tim Peters0bf60bd2003-01-08 20:40:01 +0000494 # Check first and last days of year spottily across the whole
495 # range of years supported.
496 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000497 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
498 d = self.theclass(year, 1, 1)
499 n = d.toordinal()
500 d2 = self.theclass.fromordinal(n)
501 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000502 # Verify that moving back a day gets to the end of year-1.
503 if year > 1:
504 d = self.theclass.fromordinal(n-1)
505 d2 = self.theclass(year-1, 12, 31)
506 self.assertEqual(d, d2)
507 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000508
509 # Test every day in a leap-year and a non-leap year.
510 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
511 for year, isleap in (2000, True), (2002, False):
512 n = self.theclass(year, 1, 1).toordinal()
513 for month, maxday in zip(range(1, 13), dim):
514 if month == 2 and isleap:
515 maxday += 1
516 for day in range(1, maxday+1):
517 d = self.theclass(year, month, day)
518 self.assertEqual(d.toordinal(), n)
519 self.assertEqual(d, self.theclass.fromordinal(n))
520 n += 1
521
522 def test_extreme_ordinals(self):
523 a = self.theclass.min
524 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
525 aord = a.toordinal()
526 b = a.fromordinal(aord)
527 self.assertEqual(a, b)
528
529 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
530
531 b = a + timedelta(days=1)
532 self.assertEqual(b.toordinal(), aord + 1)
533 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
534
535 a = self.theclass.max
536 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
537 aord = a.toordinal()
538 b = a.fromordinal(aord)
539 self.assertEqual(a, b)
540
541 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
542
543 b = a - timedelta(days=1)
544 self.assertEqual(b.toordinal(), aord - 1)
545 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
546
547 def test_bad_constructor_arguments(self):
548 # bad years
549 self.theclass(MINYEAR, 1, 1) # no exception
550 self.theclass(MAXYEAR, 1, 1) # no exception
551 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
552 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
553 # bad months
554 self.theclass(2000, 1, 1) # no exception
555 self.theclass(2000, 12, 1) # no exception
556 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
557 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
558 # bad days
559 self.theclass(2000, 2, 29) # no exception
560 self.theclass(2004, 2, 29) # no exception
561 self.theclass(2400, 2, 29) # no exception
562 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
563 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
564 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
565 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
566 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
567 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
568
569 def test_hash_equality(self):
570 d = self.theclass(2000, 12, 31)
571 # same thing
572 e = self.theclass(2000, 12, 31)
573 self.assertEqual(d, e)
574 self.assertEqual(hash(d), hash(e))
575
576 dic = {d: 1}
577 dic[e] = 2
578 self.assertEqual(len(dic), 1)
579 self.assertEqual(dic[d], 2)
580 self.assertEqual(dic[e], 2)
581
582 d = self.theclass(2001, 1, 1)
583 # same thing
584 e = self.theclass(2001, 1, 1)
585 self.assertEqual(d, e)
586 self.assertEqual(hash(d), hash(e))
587
588 dic = {d: 1}
589 dic[e] = 2
590 self.assertEqual(len(dic), 1)
591 self.assertEqual(dic[d], 2)
592 self.assertEqual(dic[e], 2)
593
594 def test_computations(self):
595 a = self.theclass(2002, 1, 31)
596 b = self.theclass(1956, 1, 31)
597
598 diff = a-b
599 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
600 self.assertEqual(diff.seconds, 0)
601 self.assertEqual(diff.microseconds, 0)
602
603 day = timedelta(1)
604 week = timedelta(7)
605 a = self.theclass(2002, 3, 2)
606 self.assertEqual(a + day, self.theclass(2002, 3, 3))
607 self.assertEqual(day + a, self.theclass(2002, 3, 3))
608 self.assertEqual(a - day, self.theclass(2002, 3, 1))
609 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
610 self.assertEqual(a + week, self.theclass(2002, 3, 9))
611 self.assertEqual(a - week, self.theclass(2002, 2, 23))
612 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
613 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
614 self.assertEqual((a + week) - a, week)
615 self.assertEqual((a + day) - a, day)
616 self.assertEqual((a - week) - a, -week)
617 self.assertEqual((a - day) - a, -day)
618 self.assertEqual(a - (a + week), -week)
619 self.assertEqual(a - (a + day), -day)
620 self.assertEqual(a - (a - week), week)
621 self.assertEqual(a - (a - day), day)
622
623 # Add/sub ints, longs, floats should be illegal
624 for i in 1, 1L, 1.0:
625 self.assertRaises(TypeError, lambda: a+i)
626 self.assertRaises(TypeError, lambda: a-i)
627 self.assertRaises(TypeError, lambda: i+a)
628 self.assertRaises(TypeError, lambda: i-a)
629
630 # delta - date is senseless.
631 self.assertRaises(TypeError, lambda: day - a)
632 # mixing date and (delta or date) via * or // is senseless
633 self.assertRaises(TypeError, lambda: day * a)
634 self.assertRaises(TypeError, lambda: a * day)
635 self.assertRaises(TypeError, lambda: day // a)
636 self.assertRaises(TypeError, lambda: a // day)
637 self.assertRaises(TypeError, lambda: a * a)
638 self.assertRaises(TypeError, lambda: a // a)
639 # date + date is senseless
640 self.assertRaises(TypeError, lambda: a + a)
641
642 def test_overflow(self):
643 tiny = self.theclass.resolution
644
645 dt = self.theclass.min + tiny
646 dt -= tiny # no problem
647 self.assertRaises(OverflowError, dt.__sub__, tiny)
648 self.assertRaises(OverflowError, dt.__add__, -tiny)
649
650 dt = self.theclass.max - tiny
651 dt += tiny # no problem
652 self.assertRaises(OverflowError, dt.__add__, tiny)
653 self.assertRaises(OverflowError, dt.__sub__, -tiny)
654
655 def test_fromtimestamp(self):
656 import time
657
658 # Try an arbitrary fixed value.
659 year, month, day = 1999, 9, 19
660 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
661 d = self.theclass.fromtimestamp(ts)
662 self.assertEqual(d.year, year)
663 self.assertEqual(d.month, month)
664 self.assertEqual(d.day, day)
665
666 def test_today(self):
667 import time
668
669 # We claim that today() is like fromtimestamp(time.time()), so
670 # prove it.
671 for dummy in range(3):
672 today = self.theclass.today()
673 ts = time.time()
674 todayagain = self.theclass.fromtimestamp(ts)
675 if today == todayagain:
676 break
677 # There are several legit reasons that could fail:
678 # 1. It recently became midnight, between the today() and the
679 # time() calls.
680 # 2. The platform time() has such fine resolution that we'll
681 # never get the same value twice.
682 # 3. The platform time() has poor resolution, and we just
683 # happened to call today() right before a resolution quantum
684 # boundary.
685 # 4. The system clock got fiddled between calls.
686 # In any case, wait a little while and try again.
687 time.sleep(0.1)
688
689 # It worked or it didn't. If it didn't, assume it's reason #2, and
690 # let the test pass if they're within half a second of each other.
691 self.failUnless(today == todayagain or
692 abs(todayagain - today) < timedelta(seconds=0.5))
693
694 def test_weekday(self):
695 for i in range(7):
696 # March 4, 2002 is a Monday
697 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
698 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
699 # January 2, 1956 is a Monday
700 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
701 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
702
703 def test_isocalendar(self):
704 # Check examples from
705 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
706 for i in range(7):
707 d = self.theclass(2003, 12, 22+i)
708 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
709 d = self.theclass(2003, 12, 29) + timedelta(i)
710 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
711 d = self.theclass(2004, 1, 5+i)
712 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
713 d = self.theclass(2009, 12, 21+i)
714 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
715 d = self.theclass(2009, 12, 28) + timedelta(i)
716 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
717 d = self.theclass(2010, 1, 4+i)
718 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
719
720 def test_iso_long_years(self):
721 # Calculate long ISO years and compare to table from
722 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
723 ISO_LONG_YEARS_TABLE = """
724 4 32 60 88
725 9 37 65 93
726 15 43 71 99
727 20 48 76
728 26 54 82
729
730 105 133 161 189
731 111 139 167 195
732 116 144 172
733 122 150 178
734 128 156 184
735
736 201 229 257 285
737 207 235 263 291
738 212 240 268 296
739 218 246 274
740 224 252 280
741
742 303 331 359 387
743 308 336 364 392
744 314 342 370 398
745 320 348 376
746 325 353 381
747 """
748 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
749 iso_long_years.sort()
750 L = []
751 for i in range(400):
752 d = self.theclass(2000+i, 12, 31)
753 d1 = self.theclass(1600+i, 12, 31)
754 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
755 if d.isocalendar()[1] == 53:
756 L.append(i)
757 self.assertEqual(L, iso_long_years)
758
759 def test_isoformat(self):
760 t = self.theclass(2, 3, 2)
761 self.assertEqual(t.isoformat(), "0002-03-02")
762
763 def test_ctime(self):
764 t = self.theclass(2002, 3, 2)
765 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
766
767 def test_strftime(self):
768 t = self.theclass(2005, 3, 2)
769 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
770
771 self.assertRaises(TypeError, t.strftime) # needs an arg
772 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
773 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
774
775 # A naive object replaces %z and %Z w/ empty strings.
776 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
777
778 def test_resolution_info(self):
779 self.assert_(isinstance(self.theclass.min, self.theclass))
780 self.assert_(isinstance(self.theclass.max, self.theclass))
781 self.assert_(isinstance(self.theclass.resolution, timedelta))
782 self.assert_(self.theclass.max > self.theclass.min)
783
784 def test_extreme_timedelta(self):
785 big = self.theclass.max - self.theclass.min
786 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
787 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
788 # n == 315537897599999999 ~= 2**58.13
789 justasbig = timedelta(0, 0, n)
790 self.assertEqual(big, justasbig)
791 self.assertEqual(self.theclass.min + big, self.theclass.max)
792 self.assertEqual(self.theclass.max - big, self.theclass.min)
793
794 def test_timetuple(self):
795 for i in range(7):
796 # January 2, 1956 is a Monday (0)
797 d = self.theclass(1956, 1, 2+i)
798 t = d.timetuple()
799 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
800 # February 1, 1956 is a Wednesday (2)
801 d = self.theclass(1956, 2, 1+i)
802 t = d.timetuple()
803 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
804 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
805 # of the year.
806 d = self.theclass(1956, 3, 1+i)
807 t = d.timetuple()
808 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
809 self.assertEqual(t.tm_year, 1956)
810 self.assertEqual(t.tm_mon, 3)
811 self.assertEqual(t.tm_mday, 1+i)
812 self.assertEqual(t.tm_hour, 0)
813 self.assertEqual(t.tm_min, 0)
814 self.assertEqual(t.tm_sec, 0)
815 self.assertEqual(t.tm_wday, (3+i)%7)
816 self.assertEqual(t.tm_yday, 61+i)
817 self.assertEqual(t.tm_isdst, -1)
818
819 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000820 args = 6, 7, 23
821 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000822 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000823 green = pickler.dumps(orig, proto)
824 derived = unpickler.loads(green)
825 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000826
827 def test_compare(self):
828 t1 = self.theclass(2, 3, 4)
829 t2 = self.theclass(2, 3, 4)
830 self.failUnless(t1 == t2)
831 self.failUnless(t1 <= t2)
832 self.failUnless(t1 >= t2)
833 self.failUnless(not t1 != t2)
834 self.failUnless(not t1 < t2)
835 self.failUnless(not t1 > t2)
836 self.assertEqual(cmp(t1, t2), 0)
837 self.assertEqual(cmp(t2, t1), 0)
838
839 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
840 t2 = self.theclass(*args) # this is larger than t1
841 self.failUnless(t1 < t2)
842 self.failUnless(t2 > t1)
843 self.failUnless(t1 <= t2)
844 self.failUnless(t2 >= t1)
845 self.failUnless(t1 != t2)
846 self.failUnless(t2 != t1)
847 self.failUnless(not t1 == t2)
848 self.failUnless(not t2 == t1)
849 self.failUnless(not t1 > t2)
850 self.failUnless(not t2 < t1)
851 self.failUnless(not t1 >= t2)
852 self.failUnless(not t2 <= t1)
853 self.assertEqual(cmp(t1, t2), -1)
854 self.assertEqual(cmp(t2, t1), 1)
855
856 for badarg in 10, 10L, 34.5, "abc", {}, [], ():
857 self.assertRaises(TypeError, lambda: t1 == badarg)
858 self.assertRaises(TypeError, lambda: t1 != badarg)
859 self.assertRaises(TypeError, lambda: t1 <= badarg)
860 self.assertRaises(TypeError, lambda: t1 < badarg)
861 self.assertRaises(TypeError, lambda: t1 > badarg)
862 self.assertRaises(TypeError, lambda: t1 >= badarg)
863 self.assertRaises(TypeError, lambda: badarg == t1)
864 self.assertRaises(TypeError, lambda: badarg != t1)
865 self.assertRaises(TypeError, lambda: badarg <= t1)
866 self.assertRaises(TypeError, lambda: badarg < t1)
867 self.assertRaises(TypeError, lambda: badarg > t1)
868 self.assertRaises(TypeError, lambda: badarg >= t1)
869
Tim Peters8d81a012003-01-24 22:36:34 +0000870 def test_mixed_compare(self):
871 our = self.theclass(2000, 4, 5)
872 self.assertRaises(TypeError, cmp, our, 1)
873 self.assertRaises(TypeError, cmp, 1, our)
874
875 class AnotherDateTimeClass(object):
876 def __cmp__(self, other):
877 # Return "equal" so calling this can't be confused with
878 # compare-by-address (which never says "equal" for distinct
879 # objects).
880 return 0
881
882 # This still errors, because date and datetime comparison raise
883 # TypeError instead of NotImplemented when they don't know what to
884 # do, in order to stop comparison from falling back to the default
885 # compare-by-address.
886 their = AnotherDateTimeClass()
887 self.assertRaises(TypeError, cmp, our, their)
888 # Oops: The next stab raises TypeError in the C implementation,
889 # but not in the Python implementation of datetime. The difference
890 # is due to that the Python implementation defines __cmp__ but
891 # the C implementation defines tp_richcompare. This is more pain
892 # to fix than it's worth, so commenting out the test.
893 # self.assertEqual(cmp(their, our), 0)
894
895 # But date and datetime comparison return NotImplemented instead if the
896 # other object has a timetuple attr. This gives the other object a
897 # chance to do the comparison.
898 class Comparable(AnotherDateTimeClass):
899 def timetuple(self):
900 return ()
901
902 their = Comparable()
903 self.assertEqual(cmp(our, their), 0)
904 self.assertEqual(cmp(their, our), 0)
905 self.failUnless(our == their)
906 self.failUnless(their == our)
907
Tim Peters2a799bf2002-12-16 20:18:38 +0000908 def test_bool(self):
909 # All dates are considered true.
910 self.failUnless(self.theclass.min)
911 self.failUnless(self.theclass.max)
912
Tim Petersd6844152002-12-22 20:58:42 +0000913 def test_srftime_out_of_range(self):
914 # For nasty technical reasons, we can't handle years before 1900.
915 cls = self.theclass
916 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
917 for y in 1, 49, 51, 99, 100, 1000, 1899:
918 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +0000919
920 def test_replace(self):
921 cls = self.theclass
922 args = [1, 2, 3]
923 base = cls(*args)
924 self.assertEqual(base, base.replace())
925
926 i = 0
927 for name, newval in (("year", 2),
928 ("month", 3),
929 ("day", 4)):
930 newargs = args[:]
931 newargs[i] = newval
932 expected = cls(*newargs)
933 got = base.replace(**{name: newval})
934 self.assertEqual(expected, got)
935 i += 1
936
937 # Out of bounds.
938 base = cls(2000, 2, 29)
939 self.assertRaises(ValueError, base.replace, year=2001)
940
Tim Peters2a799bf2002-12-16 20:18:38 +0000941#############################################################################
942# datetime tests
943
944class TestDateTime(TestDate):
945
946 theclass = datetime
947
948 def test_basic_attributes(self):
949 dt = self.theclass(2002, 3, 1, 12, 0)
950 self.assertEqual(dt.year, 2002)
951 self.assertEqual(dt.month, 3)
952 self.assertEqual(dt.day, 1)
953 self.assertEqual(dt.hour, 12)
954 self.assertEqual(dt.minute, 0)
955 self.assertEqual(dt.second, 0)
956 self.assertEqual(dt.microsecond, 0)
957
958 def test_basic_attributes_nonzero(self):
959 # Make sure all attributes are non-zero so bugs in
960 # bit-shifting access show up.
961 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
962 self.assertEqual(dt.year, 2002)
963 self.assertEqual(dt.month, 3)
964 self.assertEqual(dt.day, 1)
965 self.assertEqual(dt.hour, 12)
966 self.assertEqual(dt.minute, 59)
967 self.assertEqual(dt.second, 59)
968 self.assertEqual(dt.microsecond, 8000)
969
970 def test_roundtrip(self):
971 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
972 self.theclass.now()):
973 # Verify dt -> string -> datetime identity.
974 s = repr(dt)
975 self.failUnless(s.startswith('datetime.'))
976 s = s[9:]
977 dt2 = eval(s)
978 self.assertEqual(dt, dt2)
979
980 # Verify identity via reconstructing from pieces.
981 dt2 = self.theclass(dt.year, dt.month, dt.day,
982 dt.hour, dt.minute, dt.second,
983 dt.microsecond)
984 self.assertEqual(dt, dt2)
985
986 def test_isoformat(self):
987 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
988 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
989 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
990 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
991 # str is ISO format with the separator forced to a blank.
992 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
993
994 t = self.theclass(2, 3, 2)
995 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
996 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
997 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
998 # str is ISO format with the separator forced to a blank.
999 self.assertEqual(str(t), "0002-03-02 00:00:00")
1000
1001 def test_more_ctime(self):
1002 # Test fields that TestDate doesn't touch.
1003 import time
1004
1005 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1006 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1007 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1008 # out. The difference is that t.ctime() produces " 2" for the day,
1009 # but platform ctime() produces "02" for the day. According to
1010 # C99, t.ctime() is correct here.
1011 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1012
1013 # So test a case where that difference doesn't matter.
1014 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1015 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1016
1017 def test_tz_independent_comparing(self):
1018 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1019 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1020 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1021 self.assertEqual(dt1, dt3)
1022 self.assert_(dt2 > dt3)
1023
1024 # Make sure comparison doesn't forget microseconds, and isn't done
1025 # via comparing a float timestamp (an IEEE double doesn't have enough
1026 # precision to span microsecond resolution across years 1 thru 9999,
1027 # so comparing via timestamp necessarily calls some distinct values
1028 # equal).
1029 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1030 us = timedelta(microseconds=1)
1031 dt2 = dt1 + us
1032 self.assertEqual(dt2 - dt1, us)
1033 self.assert_(dt1 < dt2)
1034
1035 def test_bad_constructor_arguments(self):
1036 # bad years
1037 self.theclass(MINYEAR, 1, 1) # no exception
1038 self.theclass(MAXYEAR, 1, 1) # no exception
1039 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1040 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1041 # bad months
1042 self.theclass(2000, 1, 1) # no exception
1043 self.theclass(2000, 12, 1) # no exception
1044 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1045 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1046 # bad days
1047 self.theclass(2000, 2, 29) # no exception
1048 self.theclass(2004, 2, 29) # no exception
1049 self.theclass(2400, 2, 29) # no exception
1050 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1051 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1052 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1053 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1054 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1055 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1056 # bad hours
1057 self.theclass(2000, 1, 31, 0) # no exception
1058 self.theclass(2000, 1, 31, 23) # no exception
1059 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1060 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1061 # bad minutes
1062 self.theclass(2000, 1, 31, 23, 0) # no exception
1063 self.theclass(2000, 1, 31, 23, 59) # no exception
1064 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1065 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1066 # bad seconds
1067 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1068 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1069 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1070 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1071 # bad microseconds
1072 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1073 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1074 self.assertRaises(ValueError, self.theclass,
1075 2000, 1, 31, 23, 59, 59, -1)
1076 self.assertRaises(ValueError, self.theclass,
1077 2000, 1, 31, 23, 59, 59,
1078 1000000)
1079
1080 def test_hash_equality(self):
1081 d = self.theclass(2000, 12, 31, 23, 30, 17)
1082 e = self.theclass(2000, 12, 31, 23, 30, 17)
1083 self.assertEqual(d, e)
1084 self.assertEqual(hash(d), hash(e))
1085
1086 dic = {d: 1}
1087 dic[e] = 2
1088 self.assertEqual(len(dic), 1)
1089 self.assertEqual(dic[d], 2)
1090 self.assertEqual(dic[e], 2)
1091
1092 d = self.theclass(2001, 1, 1, 0, 5, 17)
1093 e = self.theclass(2001, 1, 1, 0, 5, 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 def test_computations(self):
1104 a = self.theclass(2002, 1, 31)
1105 b = self.theclass(1956, 1, 31)
1106 diff = a-b
1107 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1108 self.assertEqual(diff.seconds, 0)
1109 self.assertEqual(diff.microseconds, 0)
1110 a = self.theclass(2002, 3, 2, 17, 6)
1111 millisec = timedelta(0, 0, 1000)
1112 hour = timedelta(0, 3600)
1113 day = timedelta(1)
1114 week = timedelta(7)
1115 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1116 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1117 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1118 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1119 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1120 self.assertEqual(a - hour, a + -hour)
1121 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1122 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1123 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1124 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1125 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1126 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1127 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1128 self.assertEqual((a + week) - a, week)
1129 self.assertEqual((a + day) - a, day)
1130 self.assertEqual((a + hour) - a, hour)
1131 self.assertEqual((a + millisec) - a, millisec)
1132 self.assertEqual((a - week) - a, -week)
1133 self.assertEqual((a - day) - a, -day)
1134 self.assertEqual((a - hour) - a, -hour)
1135 self.assertEqual((a - millisec) - a, -millisec)
1136 self.assertEqual(a - (a + week), -week)
1137 self.assertEqual(a - (a + day), -day)
1138 self.assertEqual(a - (a + hour), -hour)
1139 self.assertEqual(a - (a + millisec), -millisec)
1140 self.assertEqual(a - (a - week), week)
1141 self.assertEqual(a - (a - day), day)
1142 self.assertEqual(a - (a - hour), hour)
1143 self.assertEqual(a - (a - millisec), millisec)
1144 self.assertEqual(a + (week + day + hour + millisec),
1145 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1146 self.assertEqual(a + (week + day + hour + millisec),
1147 (((a + week) + day) + hour) + millisec)
1148 self.assertEqual(a - (week + day + hour + millisec),
1149 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1150 self.assertEqual(a - (week + day + hour + millisec),
1151 (((a - week) - day) - hour) - millisec)
1152 # Add/sub ints, longs, floats should be illegal
1153 for i in 1, 1L, 1.0:
1154 self.assertRaises(TypeError, lambda: a+i)
1155 self.assertRaises(TypeError, lambda: a-i)
1156 self.assertRaises(TypeError, lambda: i+a)
1157 self.assertRaises(TypeError, lambda: i-a)
1158
1159 # delta - datetime is senseless.
1160 self.assertRaises(TypeError, lambda: day - a)
1161 # mixing datetime and (delta or datetime) via * or // is senseless
1162 self.assertRaises(TypeError, lambda: day * a)
1163 self.assertRaises(TypeError, lambda: a * day)
1164 self.assertRaises(TypeError, lambda: day // a)
1165 self.assertRaises(TypeError, lambda: a // day)
1166 self.assertRaises(TypeError, lambda: a * a)
1167 self.assertRaises(TypeError, lambda: a // a)
1168 # datetime + datetime is senseless
1169 self.assertRaises(TypeError, lambda: a + a)
1170
1171 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001172 args = 6, 7, 23, 20, 59, 1, 64**2
1173 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001174 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001175 green = pickler.dumps(orig, proto)
1176 derived = unpickler.loads(green)
1177 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001178
1179 def test_more_compare(self):
1180 # The test_compare() inherited from TestDate covers the error cases.
1181 # We just want to test lexicographic ordering on the members datetime
1182 # has that date lacks.
1183 args = [2000, 11, 29, 20, 58, 16, 999998]
1184 t1 = self.theclass(*args)
1185 t2 = self.theclass(*args)
1186 self.failUnless(t1 == t2)
1187 self.failUnless(t1 <= t2)
1188 self.failUnless(t1 >= t2)
1189 self.failUnless(not t1 != t2)
1190 self.failUnless(not t1 < t2)
1191 self.failUnless(not t1 > t2)
1192 self.assertEqual(cmp(t1, t2), 0)
1193 self.assertEqual(cmp(t2, t1), 0)
1194
1195 for i in range(len(args)):
1196 newargs = args[:]
1197 newargs[i] = args[i] + 1
1198 t2 = self.theclass(*newargs) # this is larger than t1
1199 self.failUnless(t1 < t2)
1200 self.failUnless(t2 > t1)
1201 self.failUnless(t1 <= t2)
1202 self.failUnless(t2 >= t1)
1203 self.failUnless(t1 != t2)
1204 self.failUnless(t2 != t1)
1205 self.failUnless(not t1 == t2)
1206 self.failUnless(not t2 == t1)
1207 self.failUnless(not t1 > t2)
1208 self.failUnless(not t2 < t1)
1209 self.failUnless(not t1 >= t2)
1210 self.failUnless(not t2 <= t1)
1211 self.assertEqual(cmp(t1, t2), -1)
1212 self.assertEqual(cmp(t2, t1), 1)
1213
1214
1215 # A helper for timestamp constructor tests.
1216 def verify_field_equality(self, expected, got):
1217 self.assertEqual(expected.tm_year, got.year)
1218 self.assertEqual(expected.tm_mon, got.month)
1219 self.assertEqual(expected.tm_mday, got.day)
1220 self.assertEqual(expected.tm_hour, got.hour)
1221 self.assertEqual(expected.tm_min, got.minute)
1222 self.assertEqual(expected.tm_sec, got.second)
1223
1224 def test_fromtimestamp(self):
1225 import time
1226
1227 ts = time.time()
1228 expected = time.localtime(ts)
1229 got = self.theclass.fromtimestamp(ts)
1230 self.verify_field_equality(expected, got)
1231
1232 def test_utcfromtimestamp(self):
1233 import time
1234
1235 ts = time.time()
1236 expected = time.gmtime(ts)
1237 got = self.theclass.utcfromtimestamp(ts)
1238 self.verify_field_equality(expected, got)
1239
1240 def test_utcnow(self):
1241 import time
1242
1243 # Call it a success if utcnow() and utcfromtimestamp() are within
1244 # a second of each other.
1245 tolerance = timedelta(seconds=1)
1246 for dummy in range(3):
1247 from_now = self.theclass.utcnow()
1248 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1249 if abs(from_timestamp - from_now) <= tolerance:
1250 break
1251 # Else try again a few times.
1252 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1253
1254 def test_more_timetuple(self):
1255 # This tests fields beyond those tested by the TestDate.test_timetuple.
1256 t = self.theclass(2004, 12, 31, 6, 22, 33)
1257 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1258 self.assertEqual(t.timetuple(),
1259 (t.year, t.month, t.day,
1260 t.hour, t.minute, t.second,
1261 t.weekday(),
1262 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1263 -1))
1264 tt = t.timetuple()
1265 self.assertEqual(tt.tm_year, t.year)
1266 self.assertEqual(tt.tm_mon, t.month)
1267 self.assertEqual(tt.tm_mday, t.day)
1268 self.assertEqual(tt.tm_hour, t.hour)
1269 self.assertEqual(tt.tm_min, t.minute)
1270 self.assertEqual(tt.tm_sec, t.second)
1271 self.assertEqual(tt.tm_wday, t.weekday())
1272 self.assertEqual(tt.tm_yday, t.toordinal() -
1273 date(t.year, 1, 1).toordinal() + 1)
1274 self.assertEqual(tt.tm_isdst, -1)
1275
1276 def test_more_strftime(self):
1277 # This tests fields beyond those tested by the TestDate.test_strftime.
1278 t = self.theclass(2004, 12, 31, 6, 22, 33)
1279 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1280 "12 31 04 33 22 06 366")
1281
1282 def test_extract(self):
1283 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1284 self.assertEqual(dt.date(), date(2002, 3, 4))
1285 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1286
1287 def test_combine(self):
1288 d = date(2002, 3, 4)
1289 t = time(18, 45, 3, 1234)
1290 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1291 combine = self.theclass.combine
1292 dt = combine(d, t)
1293 self.assertEqual(dt, expected)
1294
1295 dt = combine(time=t, date=d)
1296 self.assertEqual(dt, expected)
1297
1298 self.assertEqual(d, dt.date())
1299 self.assertEqual(t, dt.time())
1300 self.assertEqual(dt, combine(dt.date(), dt.time()))
1301
1302 self.assertRaises(TypeError, combine) # need an arg
1303 self.assertRaises(TypeError, combine, d) # need two args
1304 self.assertRaises(TypeError, combine, t, d) # args reversed
1305 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1306 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1307
Tim Peters12bf3392002-12-24 05:41:27 +00001308 def test_replace(self):
1309 cls = self.theclass
1310 args = [1, 2, 3, 4, 5, 6, 7]
1311 base = cls(*args)
1312 self.assertEqual(base, base.replace())
1313
1314 i = 0
1315 for name, newval in (("year", 2),
1316 ("month", 3),
1317 ("day", 4),
1318 ("hour", 5),
1319 ("minute", 6),
1320 ("second", 7),
1321 ("microsecond", 8)):
1322 newargs = args[:]
1323 newargs[i] = newval
1324 expected = cls(*newargs)
1325 got = base.replace(**{name: newval})
1326 self.assertEqual(expected, got)
1327 i += 1
1328
1329 # Out of bounds.
1330 base = cls(2000, 2, 29)
1331 self.assertRaises(ValueError, base.replace, year=2001)
1332
Tim Peters80475bb2002-12-25 07:40:55 +00001333 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001334 # Pretty boring! The TZ test is more interesting here. astimezone()
1335 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001336 dt = self.theclass.now()
1337 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001338 self.assertRaises(TypeError, dt.astimezone) # not enough args
1339 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1340 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001341 self.assertRaises(ValueError, dt.astimezone, f) # naive
1342 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001343
Tim Peters52dcce22003-01-23 16:36:11 +00001344 class Bogus(tzinfo):
1345 def utcoffset(self, dt): return None
1346 def dst(self, dt): return timedelta(0)
1347 bog = Bogus()
1348 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1349
1350 class AlsoBogus(tzinfo):
1351 def utcoffset(self, dt): return timedelta(0)
1352 def dst(self, dt): return None
1353 alsobog = AlsoBogus()
1354 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001355
Tim Peters2a799bf2002-12-16 20:18:38 +00001356class TestTime(unittest.TestCase):
1357
1358 theclass = time
1359
1360 def test_basic_attributes(self):
1361 t = self.theclass(12, 0)
1362 self.assertEqual(t.hour, 12)
1363 self.assertEqual(t.minute, 0)
1364 self.assertEqual(t.second, 0)
1365 self.assertEqual(t.microsecond, 0)
1366
1367 def test_basic_attributes_nonzero(self):
1368 # Make sure all attributes are non-zero so bugs in
1369 # bit-shifting access show up.
1370 t = self.theclass(12, 59, 59, 8000)
1371 self.assertEqual(t.hour, 12)
1372 self.assertEqual(t.minute, 59)
1373 self.assertEqual(t.second, 59)
1374 self.assertEqual(t.microsecond, 8000)
1375
1376 def test_roundtrip(self):
1377 t = self.theclass(1, 2, 3, 4)
1378
1379 # Verify t -> string -> time identity.
1380 s = repr(t)
1381 self.failUnless(s.startswith('datetime.'))
1382 s = s[9:]
1383 t2 = eval(s)
1384 self.assertEqual(t, t2)
1385
1386 # Verify identity via reconstructing from pieces.
1387 t2 = self.theclass(t.hour, t.minute, t.second,
1388 t.microsecond)
1389 self.assertEqual(t, t2)
1390
1391 def test_comparing(self):
1392 args = [1, 2, 3, 4]
1393 t1 = self.theclass(*args)
1394 t2 = self.theclass(*args)
1395 self.failUnless(t1 == t2)
1396 self.failUnless(t1 <= t2)
1397 self.failUnless(t1 >= t2)
1398 self.failUnless(not t1 != t2)
1399 self.failUnless(not t1 < t2)
1400 self.failUnless(not t1 > t2)
1401 self.assertEqual(cmp(t1, t2), 0)
1402 self.assertEqual(cmp(t2, t1), 0)
1403
1404 for i in range(len(args)):
1405 newargs = args[:]
1406 newargs[i] = args[i] + 1
1407 t2 = self.theclass(*newargs) # this is larger than t1
1408 self.failUnless(t1 < t2)
1409 self.failUnless(t2 > t1)
1410 self.failUnless(t1 <= t2)
1411 self.failUnless(t2 >= t1)
1412 self.failUnless(t1 != t2)
1413 self.failUnless(t2 != t1)
1414 self.failUnless(not t1 == t2)
1415 self.failUnless(not t2 == t1)
1416 self.failUnless(not t1 > t2)
1417 self.failUnless(not t2 < t1)
1418 self.failUnless(not t1 >= t2)
1419 self.failUnless(not t2 <= t1)
1420 self.assertEqual(cmp(t1, t2), -1)
1421 self.assertEqual(cmp(t2, t1), 1)
1422
Tim Peters0bf60bd2003-01-08 20:40:01 +00001423 badargs = (10, 10L, 34.5, "abc", {}, [], ())
1424 if CMP_BUG_FIXED:
1425 badargs += (date(1, 1, 1), datetime(1, 1, 1, 1, 1), timedelta(9))
1426 for badarg in badargs:
Tim Peters2a799bf2002-12-16 20:18:38 +00001427 self.assertRaises(TypeError, lambda: t1 == badarg)
1428 self.assertRaises(TypeError, lambda: t1 != badarg)
1429 self.assertRaises(TypeError, lambda: t1 <= badarg)
1430 self.assertRaises(TypeError, lambda: t1 < badarg)
1431 self.assertRaises(TypeError, lambda: t1 > badarg)
1432 self.assertRaises(TypeError, lambda: t1 >= badarg)
1433 self.assertRaises(TypeError, lambda: badarg == t1)
1434 self.assertRaises(TypeError, lambda: badarg != t1)
1435 self.assertRaises(TypeError, lambda: badarg <= t1)
1436 self.assertRaises(TypeError, lambda: badarg < t1)
1437 self.assertRaises(TypeError, lambda: badarg > t1)
1438 self.assertRaises(TypeError, lambda: badarg >= t1)
1439
1440 def test_bad_constructor_arguments(self):
1441 # bad hours
1442 self.theclass(0, 0) # no exception
1443 self.theclass(23, 0) # no exception
1444 self.assertRaises(ValueError, self.theclass, -1, 0)
1445 self.assertRaises(ValueError, self.theclass, 24, 0)
1446 # bad minutes
1447 self.theclass(23, 0) # no exception
1448 self.theclass(23, 59) # no exception
1449 self.assertRaises(ValueError, self.theclass, 23, -1)
1450 self.assertRaises(ValueError, self.theclass, 23, 60)
1451 # bad seconds
1452 self.theclass(23, 59, 0) # no exception
1453 self.theclass(23, 59, 59) # no exception
1454 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1455 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1456 # bad microseconds
1457 self.theclass(23, 59, 59, 0) # no exception
1458 self.theclass(23, 59, 59, 999999) # no exception
1459 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1460 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1461
1462 def test_hash_equality(self):
1463 d = self.theclass(23, 30, 17)
1464 e = self.theclass(23, 30, 17)
1465 self.assertEqual(d, e)
1466 self.assertEqual(hash(d), hash(e))
1467
1468 dic = {d: 1}
1469 dic[e] = 2
1470 self.assertEqual(len(dic), 1)
1471 self.assertEqual(dic[d], 2)
1472 self.assertEqual(dic[e], 2)
1473
1474 d = self.theclass(0, 5, 17)
1475 e = self.theclass(0, 5, 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 def test_isoformat(self):
1486 t = self.theclass(4, 5, 1, 123)
1487 self.assertEqual(t.isoformat(), "04:05:01.000123")
1488 self.assertEqual(t.isoformat(), str(t))
1489
1490 t = self.theclass()
1491 self.assertEqual(t.isoformat(), "00:00:00")
1492 self.assertEqual(t.isoformat(), str(t))
1493
1494 t = self.theclass(microsecond=1)
1495 self.assertEqual(t.isoformat(), "00:00:00.000001")
1496 self.assertEqual(t.isoformat(), str(t))
1497
1498 t = self.theclass(microsecond=10)
1499 self.assertEqual(t.isoformat(), "00:00:00.000010")
1500 self.assertEqual(t.isoformat(), str(t))
1501
1502 t = self.theclass(microsecond=100)
1503 self.assertEqual(t.isoformat(), "00:00:00.000100")
1504 self.assertEqual(t.isoformat(), str(t))
1505
1506 t = self.theclass(microsecond=1000)
1507 self.assertEqual(t.isoformat(), "00:00:00.001000")
1508 self.assertEqual(t.isoformat(), str(t))
1509
1510 t = self.theclass(microsecond=10000)
1511 self.assertEqual(t.isoformat(), "00:00:00.010000")
1512 self.assertEqual(t.isoformat(), str(t))
1513
1514 t = self.theclass(microsecond=100000)
1515 self.assertEqual(t.isoformat(), "00:00:00.100000")
1516 self.assertEqual(t.isoformat(), str(t))
1517
1518 def test_strftime(self):
1519 t = self.theclass(1, 2, 3, 4)
1520 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1521 # A naive object replaces %z and %Z with empty strings.
1522 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1523
1524 def test_str(self):
1525 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1526 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1527 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1528 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1529 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1530
1531 def test_repr(self):
1532 name = 'datetime.' + self.theclass.__name__
1533 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1534 "%s(1, 2, 3, 4)" % name)
1535 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1536 "%s(10, 2, 3, 4000)" % name)
1537 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1538 "%s(0, 2, 3, 400000)" % name)
1539 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1540 "%s(12, 2, 3)" % name)
1541 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1542 "%s(23, 15)" % name)
1543
1544 def test_resolution_info(self):
1545 self.assert_(isinstance(self.theclass.min, self.theclass))
1546 self.assert_(isinstance(self.theclass.max, self.theclass))
1547 self.assert_(isinstance(self.theclass.resolution, timedelta))
1548 self.assert_(self.theclass.max > self.theclass.min)
1549
1550 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001551 args = 20, 59, 16, 64**2
1552 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001553 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001554 green = pickler.dumps(orig, proto)
1555 derived = unpickler.loads(green)
1556 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001557
1558 def test_bool(self):
1559 cls = self.theclass
1560 self.failUnless(cls(1))
1561 self.failUnless(cls(0, 1))
1562 self.failUnless(cls(0, 0, 1))
1563 self.failUnless(cls(0, 0, 0, 1))
1564 self.failUnless(not cls(0))
1565 self.failUnless(not cls())
1566
Tim Peters12bf3392002-12-24 05:41:27 +00001567 def test_replace(self):
1568 cls = self.theclass
1569 args = [1, 2, 3, 4]
1570 base = cls(*args)
1571 self.assertEqual(base, base.replace())
1572
1573 i = 0
1574 for name, newval in (("hour", 5),
1575 ("minute", 6),
1576 ("second", 7),
1577 ("microsecond", 8)):
1578 newargs = args[:]
1579 newargs[i] = newval
1580 expected = cls(*newargs)
1581 got = base.replace(**{name: newval})
1582 self.assertEqual(expected, got)
1583 i += 1
1584
1585 # Out of bounds.
1586 base = cls(1)
1587 self.assertRaises(ValueError, base.replace, hour=24)
1588 self.assertRaises(ValueError, base.replace, minute=-1)
1589 self.assertRaises(ValueError, base.replace, second=100)
1590 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1591
Tim Peters855fe882002-12-22 03:43:39 +00001592# A mixin for classes with a tzinfo= argument. Subclasses must define
1593# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001594# must be legit (which is true for time and datetime).
Tim Peters855fe882002-12-22 03:43:39 +00001595class TZInfoBase(unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001596
Tim Petersbad8ff02002-12-30 20:52:32 +00001597 def test_argument_passing(self):
1598 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001599 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001600 class introspective(tzinfo):
1601 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001602 def utcoffset(self, dt):
1603 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001604 dst = utcoffset
1605
1606 obj = cls(1, 2, 3, tzinfo=introspective())
1607
Tim Peters0bf60bd2003-01-08 20:40:01 +00001608 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001609 self.assertEqual(obj.tzname(), expected)
1610
Tim Peters0bf60bd2003-01-08 20:40:01 +00001611 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001612 self.assertEqual(obj.utcoffset(), expected)
1613 self.assertEqual(obj.dst(), expected)
1614
Tim Peters855fe882002-12-22 03:43:39 +00001615 def test_bad_tzinfo_classes(self):
1616 cls = self.theclass
1617 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001618
Tim Peters855fe882002-12-22 03:43:39 +00001619 class NiceTry(object):
1620 def __init__(self): pass
1621 def utcoffset(self, dt): pass
1622 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1623
1624 class BetterTry(tzinfo):
1625 def __init__(self): pass
1626 def utcoffset(self, dt): pass
1627 b = BetterTry()
1628 t = cls(1, 1, 1, tzinfo=b)
1629 self.failUnless(t.tzinfo is b)
1630
1631 def test_utc_offset_out_of_bounds(self):
1632 class Edgy(tzinfo):
1633 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00001634 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00001635 def utcoffset(self, dt):
1636 return self.offset
1637
1638 cls = self.theclass
1639 for offset, legit in ((-1440, False),
1640 (-1439, True),
1641 (1439, True),
1642 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001643 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00001644 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001645 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00001646 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001647 else:
1648 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00001649 if legit:
1650 aofs = abs(offset)
1651 h, m = divmod(aofs, 60)
1652 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001653 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00001654 t = t.timetz()
1655 self.assertEqual(str(t), "01:02:03" + tag)
1656 else:
1657 self.assertRaises(ValueError, str, t)
1658
1659 def test_tzinfo_classes(self):
1660 cls = self.theclass
1661 class C1(tzinfo):
1662 def utcoffset(self, dt): return None
1663 def dst(self, dt): return None
1664 def tzname(self, dt): return None
1665 for t in (cls(1, 1, 1),
1666 cls(1, 1, 1, tzinfo=None),
1667 cls(1, 1, 1, tzinfo=C1())):
1668 self.failUnless(t.utcoffset() is None)
1669 self.failUnless(t.dst() is None)
1670 self.failUnless(t.tzname() is None)
1671
Tim Peters855fe882002-12-22 03:43:39 +00001672 class C3(tzinfo):
1673 def utcoffset(self, dt): return timedelta(minutes=-1439)
1674 def dst(self, dt): return timedelta(minutes=1439)
1675 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001676 t = cls(1, 1, 1, tzinfo=C3())
1677 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
1678 self.assertEqual(t.dst(), timedelta(minutes=1439))
1679 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00001680
1681 # Wrong types.
1682 class C4(tzinfo):
1683 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001684 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00001685 def tzname(self, dt): return 0
1686 t = cls(1, 1, 1, tzinfo=C4())
1687 self.assertRaises(TypeError, t.utcoffset)
1688 self.assertRaises(TypeError, t.dst)
1689 self.assertRaises(TypeError, t.tzname)
1690
1691 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00001692 class C6(tzinfo):
1693 def utcoffset(self, dt): return timedelta(hours=-24)
1694 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00001695 t = cls(1, 1, 1, tzinfo=C6())
1696 self.assertRaises(ValueError, t.utcoffset)
1697 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00001698
1699 # Not a whole number of minutes.
1700 class C7(tzinfo):
1701 def utcoffset(self, dt): return timedelta(seconds=61)
1702 def dst(self, dt): return timedelta(microseconds=-81)
1703 t = cls(1, 1, 1, tzinfo=C7())
1704 self.assertRaises(ValueError, t.utcoffset)
1705 self.assertRaises(ValueError, t.dst)
1706
Tim Peters4c0db782002-12-26 05:01:19 +00001707 def test_aware_compare(self):
1708 cls = self.theclass
1709
Tim Peters60c76e42002-12-27 00:41:11 +00001710 # Ensure that utcoffset() gets ignored if the comparands have
1711 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00001712 class OperandDependentOffset(tzinfo):
1713 def utcoffset(self, t):
1714 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00001715 # d0 and d1 equal after adjustment
1716 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00001717 else:
Tim Peters397301e2003-01-02 21:28:08 +00001718 # d2 off in the weeds
1719 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00001720
1721 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
1722 d0 = base.replace(minute=3)
1723 d1 = base.replace(minute=9)
1724 d2 = base.replace(minute=11)
1725 for x in d0, d1, d2:
1726 for y in d0, d1, d2:
1727 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00001728 expected = cmp(x.minute, y.minute)
1729 self.assertEqual(got, expected)
1730
1731 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00001732 # Note that a time can't actually have an operand-depedent offset,
1733 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
1734 # so skip this test for time.
1735 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00001736 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
1737 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
1738 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
1739 for x in d0, d1, d2:
1740 for y in d0, d1, d2:
1741 got = cmp(x, y)
1742 if (x is d0 or x is d1) and (y is d0 or y is d1):
1743 expected = 0
1744 elif x is y is d2:
1745 expected = 0
1746 elif x is d2:
1747 expected = -1
1748 else:
1749 assert y is d2
1750 expected = 1
1751 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00001752
Tim Peters855fe882002-12-22 03:43:39 +00001753
Tim Peters0bf60bd2003-01-08 20:40:01 +00001754# Testing time objects with a non-None tzinfo.
Tim Peters855fe882002-12-22 03:43:39 +00001755class TestTimeTZ(TestTime, TZInfoBase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001756 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00001757
1758 def test_empty(self):
1759 t = self.theclass()
1760 self.assertEqual(t.hour, 0)
1761 self.assertEqual(t.minute, 0)
1762 self.assertEqual(t.second, 0)
1763 self.assertEqual(t.microsecond, 0)
1764 self.failUnless(t.tzinfo is None)
1765
Tim Peters2a799bf2002-12-16 20:18:38 +00001766 def test_zones(self):
1767 est = FixedOffset(-300, "EST", 1)
1768 utc = FixedOffset(0, "UTC", -2)
1769 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001770 t1 = time( 7, 47, tzinfo=est)
1771 t2 = time(12, 47, tzinfo=utc)
1772 t3 = time(13, 47, tzinfo=met)
1773 t4 = time(microsecond=40)
1774 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00001775
1776 self.assertEqual(t1.tzinfo, est)
1777 self.assertEqual(t2.tzinfo, utc)
1778 self.assertEqual(t3.tzinfo, met)
1779 self.failUnless(t4.tzinfo is None)
1780 self.assertEqual(t5.tzinfo, utc)
1781
Tim Peters855fe882002-12-22 03:43:39 +00001782 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
1783 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
1784 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00001785 self.failUnless(t4.utcoffset() is None)
1786 self.assertRaises(TypeError, t1.utcoffset, "no args")
1787
1788 self.assertEqual(t1.tzname(), "EST")
1789 self.assertEqual(t2.tzname(), "UTC")
1790 self.assertEqual(t3.tzname(), "MET")
1791 self.failUnless(t4.tzname() is None)
1792 self.assertRaises(TypeError, t1.tzname, "no args")
1793
Tim Peters855fe882002-12-22 03:43:39 +00001794 self.assertEqual(t1.dst(), timedelta(minutes=1))
1795 self.assertEqual(t2.dst(), timedelta(minutes=-2))
1796 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00001797 self.failUnless(t4.dst() is None)
1798 self.assertRaises(TypeError, t1.dst, "no args")
1799
1800 self.assertEqual(hash(t1), hash(t2))
1801 self.assertEqual(hash(t1), hash(t3))
1802 self.assertEqual(hash(t2), hash(t3))
1803
1804 self.assertEqual(t1, t2)
1805 self.assertEqual(t1, t3)
1806 self.assertEqual(t2, t3)
1807 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
1808 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
1809 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
1810
1811 self.assertEqual(str(t1), "07:47:00-05:00")
1812 self.assertEqual(str(t2), "12:47:00+00:00")
1813 self.assertEqual(str(t3), "13:47:00+01:00")
1814 self.assertEqual(str(t4), "00:00:00.000040")
1815 self.assertEqual(str(t5), "00:00:00.000040+00:00")
1816
1817 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
1818 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
1819 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
1820 self.assertEqual(t4.isoformat(), "00:00:00.000040")
1821 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
1822
Tim Peters0bf60bd2003-01-08 20:40:01 +00001823 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00001824 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
1825 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
1826 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
1827 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
1828 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
1829
1830 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
1831 "07:47:00 %Z=EST %z=-0500")
1832 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
1833 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
1834
1835 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00001836 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00001837 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
1838 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
1839
Tim Petersb92bb712002-12-21 17:44:07 +00001840 # Check that an invalid tzname result raises an exception.
1841 class Badtzname(tzinfo):
1842 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00001843 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00001844 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
1845 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00001846
1847 def test_hash_edge_cases(self):
1848 # Offsets that overflow a basic time.
1849 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
1850 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
1851 self.assertEqual(hash(t1), hash(t2))
1852
1853 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
1854 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
1855 self.assertEqual(hash(t1), hash(t2))
1856
Tim Peters2a799bf2002-12-16 20:18:38 +00001857 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001858 # Try one without a tzinfo.
1859 args = 20, 59, 16, 64**2
1860 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001861 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001862 green = pickler.dumps(orig, proto)
1863 derived = unpickler.loads(green)
1864 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001865
1866 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00001867 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00001868 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001869 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001870 green = pickler.dumps(orig, proto)
1871 derived = unpickler.loads(green)
1872 self.assertEqual(orig, derived)
1873 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
1874 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
1875 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00001876
1877 def test_more_bool(self):
1878 # Test cases with non-None tzinfo.
1879 cls = self.theclass
1880
1881 t = cls(0, tzinfo=FixedOffset(-300, ""))
1882 self.failUnless(t)
1883
1884 t = cls(5, tzinfo=FixedOffset(-300, ""))
1885 self.failUnless(t)
1886
1887 t = cls(5, tzinfo=FixedOffset(300, ""))
1888 self.failUnless(not t)
1889
1890 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
1891 self.failUnless(not t)
1892
1893 # Mostly ensuring this doesn't overflow internally.
1894 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
1895 self.failUnless(t)
1896
1897 # But this should yield a value error -- the utcoffset is bogus.
1898 t = cls(0, tzinfo=FixedOffset(24*60, ""))
1899 self.assertRaises(ValueError, lambda: bool(t))
1900
1901 # Likewise.
1902 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
1903 self.assertRaises(ValueError, lambda: bool(t))
1904
Tim Peters12bf3392002-12-24 05:41:27 +00001905 def test_replace(self):
1906 cls = self.theclass
1907 z100 = FixedOffset(100, "+100")
1908 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
1909 args = [1, 2, 3, 4, z100]
1910 base = cls(*args)
1911 self.assertEqual(base, base.replace())
1912
1913 i = 0
1914 for name, newval in (("hour", 5),
1915 ("minute", 6),
1916 ("second", 7),
1917 ("microsecond", 8),
1918 ("tzinfo", zm200)):
1919 newargs = args[:]
1920 newargs[i] = newval
1921 expected = cls(*newargs)
1922 got = base.replace(**{name: newval})
1923 self.assertEqual(expected, got)
1924 i += 1
1925
1926 # Ensure we can get rid of a tzinfo.
1927 self.assertEqual(base.tzname(), "+100")
1928 base2 = base.replace(tzinfo=None)
1929 self.failUnless(base2.tzinfo is None)
1930 self.failUnless(base2.tzname() is None)
1931
1932 # Ensure we can add one.
1933 base3 = base2.replace(tzinfo=z100)
1934 self.assertEqual(base, base3)
1935 self.failUnless(base.tzinfo is base3.tzinfo)
1936
1937 # Out of bounds.
1938 base = cls(1)
1939 self.assertRaises(ValueError, base.replace, hour=24)
1940 self.assertRaises(ValueError, base.replace, minute=-1)
1941 self.assertRaises(ValueError, base.replace, second=100)
1942 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1943
Tim Peters60c76e42002-12-27 00:41:11 +00001944 def test_mixed_compare(self):
1945 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001946 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00001947 self.assertEqual(t1, t2)
1948 t2 = t2.replace(tzinfo=None)
1949 self.assertEqual(t1, t2)
1950 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
1951 self.assertEqual(t1, t2)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001952 if CMP_BUG_FIXED:
1953 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
1954 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00001955
Tim Peters0bf60bd2003-01-08 20:40:01 +00001956 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00001957 class Varies(tzinfo):
1958 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00001959 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00001960 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00001961 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00001962 return self.offset
1963
1964 v = Varies()
1965 t1 = t2.replace(tzinfo=v)
1966 t2 = t2.replace(tzinfo=v)
1967 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
1968 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
1969 self.assertEqual(t1, t2)
1970
1971 # But if they're not identical, it isn't ignored.
1972 t2 = t2.replace(tzinfo=Varies())
1973 self.failUnless(t1 < t2) # t1's offset counter still going up
1974
Tim Peters4c0db782002-12-26 05:01:19 +00001975
Tim Peters0bf60bd2003-01-08 20:40:01 +00001976# Testing datetime objects with a non-None tzinfo.
1977
Tim Peters855fe882002-12-22 03:43:39 +00001978class TestDateTimeTZ(TestDateTime, TZInfoBase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001979 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00001980
1981 def test_trivial(self):
1982 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
1983 self.assertEqual(dt.year, 1)
1984 self.assertEqual(dt.month, 2)
1985 self.assertEqual(dt.day, 3)
1986 self.assertEqual(dt.hour, 4)
1987 self.assertEqual(dt.minute, 5)
1988 self.assertEqual(dt.second, 6)
1989 self.assertEqual(dt.microsecond, 7)
1990 self.assertEqual(dt.tzinfo, None)
1991
1992 def test_even_more_compare(self):
1993 # The test_compare() and test_more_compare() inherited from TestDate
1994 # and TestDateTime covered non-tzinfo cases.
1995
1996 # Smallest possible after UTC adjustment.
1997 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
1998 # Largest possible after UTC adjustment.
1999 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2000 tzinfo=FixedOffset(-1439, ""))
2001
2002 # Make sure those compare correctly, and w/o overflow.
2003 self.failUnless(t1 < t2)
2004 self.failUnless(t1 != t2)
2005 self.failUnless(t2 > t1)
2006
2007 self.failUnless(t1 == t1)
2008 self.failUnless(t2 == t2)
2009
2010 # Equal afer adjustment.
2011 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2012 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2013 self.assertEqual(t1, t2)
2014
2015 # Change t1 not to subtract a minute, and t1 should be larger.
2016 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2017 self.failUnless(t1 > t2)
2018
2019 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2020 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2021 self.failUnless(t1 < t2)
2022
2023 # Back to the original t1, but make seconds resolve it.
2024 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2025 second=1)
2026 self.failUnless(t1 > t2)
2027
2028 # Likewise, but make microseconds resolve it.
2029 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2030 microsecond=1)
2031 self.failUnless(t1 > t2)
2032
2033 # Make t2 naive and it should fail.
2034 t2 = self.theclass.min
2035 self.assertRaises(TypeError, lambda: t1 == t2)
2036 self.assertEqual(t2, t2)
2037
2038 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2039 class Naive(tzinfo):
2040 def utcoffset(self, dt): return None
2041 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2042 self.assertRaises(TypeError, lambda: t1 == t2)
2043 self.assertEqual(t2, t2)
2044
2045 # OTOH, it's OK to compare two of these mixing the two ways of being
2046 # naive.
2047 t1 = self.theclass(5, 6, 7)
2048 self.assertEqual(t1, t2)
2049
2050 # Try a bogus uctoffset.
2051 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002052 def utcoffset(self, dt):
2053 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002054 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2055 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002056 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002057
Tim Peters2a799bf2002-12-16 20:18:38 +00002058 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002059 # Try one without a tzinfo.
2060 args = 6, 7, 23, 20, 59, 1, 64**2
2061 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002062 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002063 green = pickler.dumps(orig, proto)
2064 derived = unpickler.loads(green)
2065 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002066
2067 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002068 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002069 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002070 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002071 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002072 green = pickler.dumps(orig, proto)
2073 derived = unpickler.loads(green)
2074 self.assertEqual(orig, derived)
2075 self.failUnless(isinstance(derived.tzinfo,
2076 PicklableFixedOffset))
2077 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2078 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002079
2080 def test_extreme_hashes(self):
2081 # If an attempt is made to hash these via subtracting the offset
2082 # then hashing a datetime object, OverflowError results. The
2083 # Python implementation used to blow up here.
2084 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2085 hash(t)
2086 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2087 tzinfo=FixedOffset(-1439, ""))
2088 hash(t)
2089
2090 # OTOH, an OOB offset should blow up.
2091 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2092 self.assertRaises(ValueError, hash, t)
2093
2094 def test_zones(self):
2095 est = FixedOffset(-300, "EST")
2096 utc = FixedOffset(0, "UTC")
2097 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002098 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2099 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2100 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002101 self.assertEqual(t1.tzinfo, est)
2102 self.assertEqual(t2.tzinfo, utc)
2103 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002104 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2105 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2106 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002107 self.assertEqual(t1.tzname(), "EST")
2108 self.assertEqual(t2.tzname(), "UTC")
2109 self.assertEqual(t3.tzname(), "MET")
2110 self.assertEqual(hash(t1), hash(t2))
2111 self.assertEqual(hash(t1), hash(t3))
2112 self.assertEqual(hash(t2), hash(t3))
2113 self.assertEqual(t1, t2)
2114 self.assertEqual(t1, t3)
2115 self.assertEqual(t2, t3)
2116 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2117 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2118 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002119 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002120 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2121 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2122 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2123
2124 def test_combine(self):
2125 met = FixedOffset(60, "MET")
2126 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002127 tz = time(18, 45, 3, 1234, tzinfo=met)
2128 dt = datetime.combine(d, tz)
2129 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002130 tzinfo=met))
2131
2132 def test_extract(self):
2133 met = FixedOffset(60, "MET")
2134 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2135 self.assertEqual(dt.date(), date(2002, 3, 4))
2136 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002137 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002138
2139 def test_tz_aware_arithmetic(self):
2140 import random
2141
2142 now = self.theclass.now()
2143 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002144 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002145 nowaware = self.theclass.combine(now.date(), timeaware)
2146 self.failUnless(nowaware.tzinfo is tz55)
2147 self.assertEqual(nowaware.timetz(), timeaware)
2148
2149 # Can't mix aware and non-aware.
2150 self.assertRaises(TypeError, lambda: now - nowaware)
2151 self.assertRaises(TypeError, lambda: nowaware - now)
2152
Tim Peters0bf60bd2003-01-08 20:40:01 +00002153 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002154 self.assertRaises(TypeError, lambda: now + nowaware)
2155 self.assertRaises(TypeError, lambda: nowaware + now)
2156 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2157
2158 # Subtracting should yield 0.
2159 self.assertEqual(now - now, timedelta(0))
2160 self.assertEqual(nowaware - nowaware, timedelta(0))
2161
2162 # Adding a delta should preserve tzinfo.
2163 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2164 nowawareplus = nowaware + delta
2165 self.failUnless(nowaware.tzinfo is tz55)
2166 nowawareplus2 = delta + nowaware
2167 self.failUnless(nowawareplus2.tzinfo is tz55)
2168 self.assertEqual(nowawareplus, nowawareplus2)
2169
2170 # that - delta should be what we started with, and that - what we
2171 # started with should be delta.
2172 diff = nowawareplus - delta
2173 self.failUnless(diff.tzinfo is tz55)
2174 self.assertEqual(nowaware, diff)
2175 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2176 self.assertEqual(nowawareplus - nowaware, delta)
2177
2178 # Make up a random timezone.
2179 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002180 # Attach it to nowawareplus.
2181 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002182 self.failUnless(nowawareplus.tzinfo is tzr)
2183 # Make sure the difference takes the timezone adjustments into account.
2184 got = nowaware - nowawareplus
2185 # Expected: (nowaware base - nowaware offset) -
2186 # (nowawareplus base - nowawareplus offset) =
2187 # (nowaware base - nowawareplus base) +
2188 # (nowawareplus offset - nowaware offset) =
2189 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002190 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002191 self.assertEqual(got, expected)
2192
2193 # Try max possible difference.
2194 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2195 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2196 tzinfo=FixedOffset(-1439, "max"))
2197 maxdiff = max - min
2198 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2199 timedelta(minutes=2*1439))
2200
2201 def test_tzinfo_now(self):
2202 meth = self.theclass.now
2203 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2204 base = meth()
2205 # Try with and without naming the keyword.
2206 off42 = FixedOffset(42, "42")
2207 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002208 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002209 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002210 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002211 # Bad argument with and w/o naming the keyword.
2212 self.assertRaises(TypeError, meth, 16)
2213 self.assertRaises(TypeError, meth, tzinfo=16)
2214 # Bad keyword name.
2215 self.assertRaises(TypeError, meth, tinfo=off42)
2216 # Too many args.
2217 self.assertRaises(TypeError, meth, off42, off42)
2218
Tim Peters10cadce2003-01-23 19:58:02 +00002219 # We don't know which time zone we're in, and don't have a tzinfo
2220 # class to represent it, so seeing whether a tz argument actually
2221 # does a conversion is tricky.
2222 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2223 utc = FixedOffset(0, "utc", 0)
2224 for dummy in range(3):
2225 now = datetime.now(weirdtz)
2226 self.failUnless(now.tzinfo is weirdtz)
2227 utcnow = datetime.utcnow().replace(tzinfo=utc)
2228 now2 = utcnow.astimezone(weirdtz)
2229 if abs(now - now2) < timedelta(seconds=30):
2230 break
2231 # Else the code is broken, or more than 30 seconds passed between
2232 # calls; assuming the latter, just try again.
2233 else:
2234 # Three strikes and we're out.
2235 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2236
Tim Peters2a799bf2002-12-16 20:18:38 +00002237 def test_tzinfo_fromtimestamp(self):
2238 import time
2239 meth = self.theclass.fromtimestamp
2240 ts = time.time()
2241 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2242 base = meth(ts)
2243 # Try with and without naming the keyword.
2244 off42 = FixedOffset(42, "42")
2245 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002246 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002247 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002248 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002249 # Bad argument with and w/o naming the keyword.
2250 self.assertRaises(TypeError, meth, ts, 16)
2251 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2252 # Bad keyword name.
2253 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2254 # Too many args.
2255 self.assertRaises(TypeError, meth, ts, off42, off42)
2256 # Too few args.
2257 self.assertRaises(TypeError, meth)
2258
Tim Peters2a44a8d2003-01-23 20:53:10 +00002259 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002260 timestamp = 1000000000
2261 utcdatetime = datetime.utcfromtimestamp(timestamp)
2262 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2263 # But on some flavor of Mac, it's nowhere near that. So we can't have
2264 # any idea here what time that actually is, we can only test that
2265 # relative changes match.
2266 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2267 tz = FixedOffset(utcoffset, "tz", 0)
2268 expected = utcdatetime + utcoffset
2269 got = datetime.fromtimestamp(timestamp, tz)
2270 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002271
Tim Peters2a799bf2002-12-16 20:18:38 +00002272 def test_tzinfo_utcnow(self):
2273 meth = self.theclass.utcnow
2274 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2275 base = meth()
2276 # Try with and without naming the keyword; for whatever reason,
2277 # utcnow() doesn't accept a tzinfo argument.
2278 off42 = FixedOffset(42, "42")
2279 self.assertRaises(TypeError, meth, off42)
2280 self.assertRaises(TypeError, meth, tzinfo=off42)
2281
2282 def test_tzinfo_utcfromtimestamp(self):
2283 import time
2284 meth = self.theclass.utcfromtimestamp
2285 ts = time.time()
2286 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2287 base = meth(ts)
2288 # Try with and without naming the keyword; for whatever reason,
2289 # utcfromtimestamp() doesn't accept a tzinfo argument.
2290 off42 = FixedOffset(42, "42")
2291 self.assertRaises(TypeError, meth, ts, off42)
2292 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2293
2294 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002295 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002296 # DST flag.
2297 class DST(tzinfo):
2298 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002299 if isinstance(dstvalue, int):
2300 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002301 self.dstvalue = dstvalue
2302 def dst(self, dt):
2303 return self.dstvalue
2304
2305 cls = self.theclass
2306 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2307 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2308 t = d.timetuple()
2309 self.assertEqual(1, t.tm_year)
2310 self.assertEqual(1, t.tm_mon)
2311 self.assertEqual(1, t.tm_mday)
2312 self.assertEqual(10, t.tm_hour)
2313 self.assertEqual(20, t.tm_min)
2314 self.assertEqual(30, t.tm_sec)
2315 self.assertEqual(0, t.tm_wday)
2316 self.assertEqual(1, t.tm_yday)
2317 self.assertEqual(flag, t.tm_isdst)
2318
2319 # dst() returns wrong type.
2320 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2321
2322 # dst() at the edge.
2323 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2324 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2325
2326 # dst() out of range.
2327 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2328 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2329
2330 def test_utctimetuple(self):
2331 class DST(tzinfo):
2332 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002333 if isinstance(dstvalue, int):
2334 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002335 self.dstvalue = dstvalue
2336 def dst(self, dt):
2337 return self.dstvalue
2338
2339 cls = self.theclass
2340 # This can't work: DST didn't implement utcoffset.
2341 self.assertRaises(NotImplementedError,
2342 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2343
2344 class UOFS(DST):
2345 def __init__(self, uofs, dofs=None):
2346 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002347 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002348 def utcoffset(self, dt):
2349 return self.uofs
2350
2351 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2352 # in effect for a UTC time.
2353 for dstvalue in -33, 33, 0, None:
2354 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2355 t = d.utctimetuple()
2356 self.assertEqual(d.year, t.tm_year)
2357 self.assertEqual(d.month, t.tm_mon)
2358 self.assertEqual(d.day, t.tm_mday)
2359 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2360 self.assertEqual(13, t.tm_min)
2361 self.assertEqual(d.second, t.tm_sec)
2362 self.assertEqual(d.weekday(), t.tm_wday)
2363 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2364 t.tm_yday)
2365 self.assertEqual(0, t.tm_isdst)
2366
2367 # At the edges, UTC adjustment can normalize into years out-of-range
2368 # for a datetime object. Ensure that a correct timetuple is
2369 # created anyway.
2370 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2371 # That goes back 1 minute less than a full day.
2372 t = tiny.utctimetuple()
2373 self.assertEqual(t.tm_year, MINYEAR-1)
2374 self.assertEqual(t.tm_mon, 12)
2375 self.assertEqual(t.tm_mday, 31)
2376 self.assertEqual(t.tm_hour, 0)
2377 self.assertEqual(t.tm_min, 1)
2378 self.assertEqual(t.tm_sec, 37)
2379 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2380 self.assertEqual(t.tm_isdst, 0)
2381
2382 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2383 # That goes forward 1 minute less than a full day.
2384 t = huge.utctimetuple()
2385 self.assertEqual(t.tm_year, MAXYEAR+1)
2386 self.assertEqual(t.tm_mon, 1)
2387 self.assertEqual(t.tm_mday, 1)
2388 self.assertEqual(t.tm_hour, 23)
2389 self.assertEqual(t.tm_min, 58)
2390 self.assertEqual(t.tm_sec, 37)
2391 self.assertEqual(t.tm_yday, 1)
2392 self.assertEqual(t.tm_isdst, 0)
2393
2394 def test_tzinfo_isoformat(self):
2395 zero = FixedOffset(0, "+00:00")
2396 plus = FixedOffset(220, "+03:40")
2397 minus = FixedOffset(-231, "-03:51")
2398 unknown = FixedOffset(None, "")
2399
2400 cls = self.theclass
2401 datestr = '0001-02-03'
2402 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002403 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002404 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2405 timestr = '04:05:59' + (us and '.987001' or '')
2406 ofsstr = ofs is not None and d.tzname() or ''
2407 tailstr = timestr + ofsstr
2408 iso = d.isoformat()
2409 self.assertEqual(iso, datestr + 'T' + tailstr)
2410 self.assertEqual(iso, d.isoformat('T'))
2411 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2412 self.assertEqual(str(d), datestr + ' ' + tailstr)
2413
Tim Peters12bf3392002-12-24 05:41:27 +00002414 def test_replace(self):
2415 cls = self.theclass
2416 z100 = FixedOffset(100, "+100")
2417 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2418 args = [1, 2, 3, 4, 5, 6, 7, z100]
2419 base = cls(*args)
2420 self.assertEqual(base, base.replace())
2421
2422 i = 0
2423 for name, newval in (("year", 2),
2424 ("month", 3),
2425 ("day", 4),
2426 ("hour", 5),
2427 ("minute", 6),
2428 ("second", 7),
2429 ("microsecond", 8),
2430 ("tzinfo", zm200)):
2431 newargs = args[:]
2432 newargs[i] = newval
2433 expected = cls(*newargs)
2434 got = base.replace(**{name: newval})
2435 self.assertEqual(expected, got)
2436 i += 1
2437
2438 # Ensure we can get rid of a tzinfo.
2439 self.assertEqual(base.tzname(), "+100")
2440 base2 = base.replace(tzinfo=None)
2441 self.failUnless(base2.tzinfo is None)
2442 self.failUnless(base2.tzname() is None)
2443
2444 # Ensure we can add one.
2445 base3 = base2.replace(tzinfo=z100)
2446 self.assertEqual(base, base3)
2447 self.failUnless(base.tzinfo is base3.tzinfo)
2448
2449 # Out of bounds.
2450 base = cls(2000, 2, 29)
2451 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002452
Tim Peters80475bb2002-12-25 07:40:55 +00002453 def test_more_astimezone(self):
2454 # The inherited test_astimezone covered some trivial and error cases.
2455 fnone = FixedOffset(None, "None")
2456 f44m = FixedOffset(44, "44")
2457 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2458
Tim Peters10cadce2003-01-23 19:58:02 +00002459 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002460 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002461 # Replacing with degenerate tzinfo raises an exception.
2462 self.assertRaises(ValueError, dt.astimezone, fnone)
2463 # Ditto with None tz.
2464 self.assertRaises(TypeError, dt.astimezone, None)
2465 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002466 x = dt.astimezone(dt.tzinfo)
2467 self.failUnless(x.tzinfo is f44m)
2468 self.assertEqual(x.date(), dt.date())
2469 self.assertEqual(x.time(), dt.time())
2470
2471 # Replacing with different tzinfo does adjust.
2472 got = dt.astimezone(fm5h)
2473 self.failUnless(got.tzinfo is fm5h)
2474 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2475 expected = dt - dt.utcoffset() # in effect, convert to UTC
2476 expected += fm5h.utcoffset(dt) # and from there to local time
2477 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2478 self.assertEqual(got.date(), expected.date())
2479 self.assertEqual(got.time(), expected.time())
2480 self.assertEqual(got.timetz(), expected.timetz())
2481 self.failUnless(got.tzinfo is expected.tzinfo)
2482 self.assertEqual(got, expected)
2483
Tim Peters4c0db782002-12-26 05:01:19 +00002484 def test_aware_subtract(self):
2485 cls = self.theclass
2486
Tim Peters60c76e42002-12-27 00:41:11 +00002487 # Ensure that utcoffset() is ignored when the operands have the
2488 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002489 class OperandDependentOffset(tzinfo):
2490 def utcoffset(self, t):
2491 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002492 # d0 and d1 equal after adjustment
2493 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002494 else:
Tim Peters397301e2003-01-02 21:28:08 +00002495 # d2 off in the weeds
2496 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002497
2498 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2499 d0 = base.replace(minute=3)
2500 d1 = base.replace(minute=9)
2501 d2 = base.replace(minute=11)
2502 for x in d0, d1, d2:
2503 for y in d0, d1, d2:
2504 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002505 expected = timedelta(minutes=x.minute - y.minute)
2506 self.assertEqual(got, expected)
2507
2508 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2509 # ignored.
2510 base = cls(8, 9, 10, 11, 12, 13, 14)
2511 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2512 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2513 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2514 for x in d0, d1, d2:
2515 for y in d0, d1, d2:
2516 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002517 if (x is d0 or x is d1) and (y is d0 or y is d1):
2518 expected = timedelta(0)
2519 elif x is y is d2:
2520 expected = timedelta(0)
2521 elif x is d2:
2522 expected = timedelta(minutes=(11-59)-0)
2523 else:
2524 assert y is d2
2525 expected = timedelta(minutes=0-(11-59))
2526 self.assertEqual(got, expected)
2527
Tim Peters60c76e42002-12-27 00:41:11 +00002528 def test_mixed_compare(self):
2529 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002530 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002531 self.assertEqual(t1, t2)
2532 t2 = t2.replace(tzinfo=None)
2533 self.assertEqual(t1, t2)
2534 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2535 self.assertEqual(t1, t2)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002536 if CMP_BUG_FIXED:
2537 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2538 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002539
Tim Peters0bf60bd2003-01-08 20:40:01 +00002540 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002541 class Varies(tzinfo):
2542 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002543 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002544 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002545 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002546 return self.offset
2547
2548 v = Varies()
2549 t1 = t2.replace(tzinfo=v)
2550 t2 = t2.replace(tzinfo=v)
2551 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2552 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2553 self.assertEqual(t1, t2)
2554
2555 # But if they're not identical, it isn't ignored.
2556 t2 = t2.replace(tzinfo=Varies())
2557 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002558
Tim Peters621818b2002-12-29 23:44:49 +00002559# Pain to set up DST-aware tzinfo classes.
2560
2561def first_sunday_on_or_after(dt):
2562 days_to_go = 6 - dt.weekday()
2563 if days_to_go:
2564 dt += timedelta(days_to_go)
2565 return dt
2566
2567ZERO = timedelta(0)
2568HOUR = timedelta(hours=1)
2569DAY = timedelta(days=1)
2570# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2571DSTSTART = datetime(1, 4, 1, 2)
2572# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002573# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2574# being standard time on that day, there is no spelling in local time of
2575# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2576DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002577
2578class USTimeZone(tzinfo):
2579
2580 def __init__(self, hours, reprname, stdname, dstname):
2581 self.stdoffset = timedelta(hours=hours)
2582 self.reprname = reprname
2583 self.stdname = stdname
2584 self.dstname = dstname
2585
2586 def __repr__(self):
2587 return self.reprname
2588
2589 def tzname(self, dt):
2590 if self.dst(dt):
2591 return self.dstname
2592 else:
2593 return self.stdname
2594
2595 def utcoffset(self, dt):
2596 return self.stdoffset + self.dst(dt)
2597
2598 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00002599 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00002600 # An exception instead may be sensible here, in one or more of
2601 # the cases.
2602 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00002603 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00002604
2605 # Find first Sunday in April.
2606 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
2607 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
2608
2609 # Find last Sunday in October.
2610 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
2611 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
2612
Tim Peters621818b2002-12-29 23:44:49 +00002613 # Can't compare naive to aware objects, so strip the timezone from
2614 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00002615 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00002616 return HOUR
2617 else:
2618 return ZERO
2619
Tim Peters521fc152002-12-31 17:36:56 +00002620Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
2621Central = USTimeZone(-6, "Central", "CST", "CDT")
2622Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
2623Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00002624utc_real = FixedOffset(0, "UTC", 0)
2625# For better test coverage, we want another flavor of UTC that's west of
2626# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00002627utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00002628
2629class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00002630 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002631 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00002632 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002633
Tim Peters0bf60bd2003-01-08 20:40:01 +00002634 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00002635
Tim Peters521fc152002-12-31 17:36:56 +00002636 # Check a time that's inside DST.
2637 def checkinside(self, dt, tz, utc, dston, dstoff):
2638 self.assertEqual(dt.dst(), HOUR)
2639
2640 # Conversion to our own timezone is always an identity.
2641 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00002642
2643 asutc = dt.astimezone(utc)
2644 there_and_back = asutc.astimezone(tz)
2645
2646 # Conversion to UTC and back isn't always an identity here,
2647 # because there are redundant spellings (in local time) of
2648 # UTC time when DST begins: the clock jumps from 1:59:59
2649 # to 3:00:00, and a local time of 2:MM:SS doesn't really
2650 # make sense then. The classes above treat 2:MM:SS as
2651 # daylight time then (it's "after 2am"), really an alias
2652 # for 1:MM:SS standard time. The latter form is what
2653 # conversion back from UTC produces.
2654 if dt.date() == dston.date() and dt.hour == 2:
2655 # We're in the redundant hour, and coming back from
2656 # UTC gives the 1:MM:SS standard-time spelling.
2657 self.assertEqual(there_and_back + HOUR, dt)
2658 # Although during was considered to be in daylight
2659 # time, there_and_back is not.
2660 self.assertEqual(there_and_back.dst(), ZERO)
2661 # They're the same times in UTC.
2662 self.assertEqual(there_and_back.astimezone(utc),
2663 dt.astimezone(utc))
2664 else:
2665 # We're not in the redundant hour.
2666 self.assertEqual(dt, there_and_back)
2667
Tim Peters327098a2003-01-20 22:54:38 +00002668 # Because we have a redundant spelling when DST begins, there is
2669 # (unforunately) an hour when DST ends that can't be spelled at all in
2670 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
2671 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
2672 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
2673 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
2674 # expressed in local time. Nevertheless, we want conversion back
2675 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00002676 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00002677 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00002678 if dt.date() == dstoff.date() and dt.hour == 0:
2679 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00002680 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00002681 self.assertEqual(nexthour_tz, dt.replace(hour=1))
2682 nexthour_utc += HOUR
2683 nexthour_tz = nexthour_utc.astimezone(tz)
2684 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00002685 else:
Tim Peters327098a2003-01-20 22:54:38 +00002686 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00002687
2688 # Check a time that's outside DST.
2689 def checkoutside(self, dt, tz, utc):
2690 self.assertEqual(dt.dst(), ZERO)
2691
2692 # Conversion to our own timezone is always an identity.
2693 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00002694
2695 # Converting to UTC and back is an identity too.
2696 asutc = dt.astimezone(utc)
2697 there_and_back = asutc.astimezone(tz)
2698 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00002699
Tim Peters1024bf82002-12-30 17:09:40 +00002700 def convert_between_tz_and_utc(self, tz, utc):
2701 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00002702 # Because 1:MM on the day DST ends is taken as being standard time,
2703 # there is no spelling in tz for the last hour of daylight time.
2704 # For purposes of the test, the last hour of DST is 0:MM, which is
2705 # taken as being daylight time (and 1:MM is taken as being standard
2706 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00002707 dstoff = self.dstoff.replace(tzinfo=tz)
2708 for delta in (timedelta(weeks=13),
2709 DAY,
2710 HOUR,
2711 timedelta(minutes=1),
2712 timedelta(microseconds=1)):
2713
Tim Peters521fc152002-12-31 17:36:56 +00002714 self.checkinside(dston, tz, utc, dston, dstoff)
2715 for during in dston + delta, dstoff - delta:
2716 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00002717
Tim Peters521fc152002-12-31 17:36:56 +00002718 self.checkoutside(dstoff, tz, utc)
2719 for outside in dston - delta, dstoff + delta:
2720 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00002721
Tim Peters621818b2002-12-29 23:44:49 +00002722 def test_easy(self):
2723 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00002724 self.convert_between_tz_and_utc(Eastern, utc_real)
2725 self.convert_between_tz_and_utc(Pacific, utc_real)
2726 self.convert_between_tz_and_utc(Eastern, utc_fake)
2727 self.convert_between_tz_and_utc(Pacific, utc_fake)
2728 # The next is really dancing near the edge. It works because
2729 # Pacific and Eastern are far enough apart that their "problem
2730 # hours" don't overlap.
2731 self.convert_between_tz_and_utc(Eastern, Pacific)
2732 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00002733 # OTOH, these fail! Don't enable them. The difficulty is that
2734 # the edge case tests assume that every hour is representable in
2735 # the "utc" class. This is always true for a fixed-offset tzinfo
2736 # class (lke utc_real and utc_fake), but not for Eastern or Central.
2737 # For these adjacent DST-aware time zones, the range of time offsets
2738 # tested ends up creating hours in the one that aren't representable
2739 # in the other. For the same reason, we would see failures in the
2740 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
2741 # offset deltas in convert_between_tz_and_utc().
2742 #
2743 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
2744 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00002745
Tim Petersf3615152003-01-01 21:51:37 +00002746 def test_tricky(self):
2747 # 22:00 on day before daylight starts.
2748 fourback = self.dston - timedelta(hours=4)
2749 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00002750 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00002751 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
2752 # 2", we should get the 3 spelling.
2753 # If we plug 22:00 the day before into Eastern, it "looks like std
2754 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
2755 # to 22:00 lands on 2:00, which makes no sense in local time (the
2756 # local clock jumps from 1 to 3). The point here is to make sure we
2757 # get the 3 spelling.
2758 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00002759 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00002760 self.assertEqual(expected, got)
2761
2762 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
2763 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00002764 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00002765 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
2766 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
2767 # spelling.
2768 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00002769 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00002770 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00002771
Tim Petersadf64202003-01-04 06:03:15 +00002772 # Now on the day DST ends, we want "repeat an hour" behavior.
2773 # UTC 4:MM 5:MM 6:MM 7:MM checking these
2774 # EST 23:MM 0:MM 1:MM 2:MM
2775 # EDT 0:MM 1:MM 2:MM 3:MM
2776 # wall 0:MM 1:MM 1:MM 2:MM against these
2777 for utc in utc_real, utc_fake:
2778 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00002779 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00002780 # Convert that to UTC.
2781 first_std_hour -= tz.utcoffset(None)
2782 # Adjust for possibly fake UTC.
2783 asutc = first_std_hour + utc.utcoffset(None)
2784 # First UTC hour to convert; this is 4:00 when utc=utc_real &
2785 # tz=Eastern.
2786 asutcbase = asutc.replace(tzinfo=utc)
2787 for tzhour in (0, 1, 1, 2):
2788 expectedbase = self.dstoff.replace(hour=tzhour)
2789 for minute in 0, 30, 59:
2790 expected = expectedbase.replace(minute=minute)
2791 asutc = asutcbase.replace(minute=minute)
2792 astz = asutc.astimezone(tz)
2793 self.assertEqual(astz.replace(tzinfo=None), expected)
2794 asutcbase += HOUR
2795
2796
Tim Peters710fb152003-01-02 19:35:54 +00002797 def test_bogus_dst(self):
2798 class ok(tzinfo):
2799 def utcoffset(self, dt): return HOUR
2800 def dst(self, dt): return HOUR
2801
2802 now = self.theclass.now().replace(tzinfo=utc_real)
2803 # Doesn't blow up.
2804 now.astimezone(ok())
2805
2806 # Does blow up.
2807 class notok(ok):
2808 def dst(self, dt): return None
2809 self.assertRaises(ValueError, now.astimezone, notok())
2810
Tim Peters52dcce22003-01-23 16:36:11 +00002811 def test_fromutc(self):
2812 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
2813 now = datetime.utcnow().replace(tzinfo=utc_real)
2814 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
2815 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
2816 enow = Eastern.fromutc(now) # doesn't blow up
2817 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
2818 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
2819 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
2820
2821 # Always converts UTC to standard time.
2822 class FauxUSTimeZone(USTimeZone):
2823 def fromutc(self, dt):
2824 return dt + self.stdoffset
2825 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
2826
2827 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
2828 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
2829 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
2830
2831 # Check around DST start.
2832 start = self.dston.replace(hour=4, tzinfo=Eastern)
2833 fstart = start.replace(tzinfo=FEastern)
2834 for wall in 23, 0, 1, 3, 4, 5:
2835 expected = start.replace(hour=wall)
2836 if wall == 23:
2837 expected -= timedelta(days=1)
2838 got = Eastern.fromutc(start)
2839 self.assertEqual(expected, got)
2840
2841 expected = fstart + FEastern.stdoffset
2842 got = FEastern.fromutc(fstart)
2843 self.assertEqual(expected, got)
2844
2845 # Ensure astimezone() calls fromutc() too.
2846 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
2847 self.assertEqual(expected, got)
2848
2849 start += HOUR
2850 fstart += HOUR
2851
2852 # Check around DST end.
2853 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
2854 fstart = start.replace(tzinfo=FEastern)
2855 for wall in 0, 1, 1, 2, 3, 4:
2856 expected = start.replace(hour=wall)
2857 got = Eastern.fromutc(start)
2858 self.assertEqual(expected, got)
2859
2860 expected = fstart + FEastern.stdoffset
2861 got = FEastern.fromutc(fstart)
2862 self.assertEqual(expected, got)
2863
2864 # Ensure astimezone() calls fromutc() too.
2865 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
2866 self.assertEqual(expected, got)
2867
2868 start += HOUR
2869 fstart += HOUR
2870
Tim Peters710fb152003-01-02 19:35:54 +00002871
Tim Peterscfd4a8b2002-12-16 21:12:37 +00002872def test_suite():
Tim Peters2a799bf2002-12-16 20:18:38 +00002873 allsuites = [unittest.makeSuite(klass, 'test')
2874 for klass in (TestModule,
2875 TestTZInfo,
2876 TestTimeDelta,
2877 TestDateOnly,
2878 TestDate,
2879 TestDateTime,
2880 TestTime,
2881 TestTimeTZ,
2882 TestDateTimeTZ,
Tim Peters621818b2002-12-29 23:44:49 +00002883 TestTimezoneConversions,
Tim Peters2a799bf2002-12-16 20:18:38 +00002884 )
2885 ]
2886 return unittest.TestSuite(allsuites)
2887
2888def test_main():
2889 import gc
2890 import sys
2891
Tim Peterscfd4a8b2002-12-16 21:12:37 +00002892 thesuite = test_suite()
Tim Peters2a799bf2002-12-16 20:18:38 +00002893 lastrc = None
2894 while True:
2895 test_support.run_suite(thesuite)
2896 if 1: # change to 0, under a debug build, for some leak detection
2897 break
2898 gc.collect()
2899 if gc.garbage:
2900 raise SystemError("gc.garbage not empty after test run: %r" %
2901 gc.garbage)
2902 if hasattr(sys, 'gettotalrefcount'):
2903 thisrc = sys.gettotalrefcount()
2904 print >> sys.stderr, '*' * 10, 'total refs:', thisrc,
2905 if lastrc:
2906 print >> sys.stderr, 'delta:', thisrc - lastrc
2907 else:
2908 print >> sys.stderr
2909 lastrc = thisrc
2910
2911if __name__ == "__main__":
2912 test_main()