blob: b26c92e21e3fb1f73a58b4646d7808de5aa38e76 [file] [log] [blame]
Tim Peters0bf60bd2003-01-08 20:40:01 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
Tim Peters2a799bf2002-12-16 20:18:38 +00005
Guido van Rossumd8faa362007-04-27 19:54:29 +00006import os
Tim Peters2a799bf2002-12-16 20:18:38 +00007import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00008import pickle
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
Guido van Rossumbe6fe542007-07-19 23:55:34 +000019pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
20assert len(pickle_choices) == 3
Guido van Rossum177e41a2003-01-30 22:06:23 +000021
Tim Peters68124bb2003-02-08 03:46:31 +000022# An arbitrary collection of objects of non-datetime types, for testing
23# mixed-type comparisons.
Guido van Rossume2a383d2007-01-15 16:59:06 +000024OTHERSTUFF = (10, 10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000025
Tim Peters2a799bf2002-12-16 20:18:38 +000026
27#############################################################################
28# module tests
29
30class TestModule(unittest.TestCase):
31
32 def test_constants(self):
33 import datetime
34 self.assertEqual(datetime.MINYEAR, 1)
35 self.assertEqual(datetime.MAXYEAR, 9999)
36
37#############################################################################
38# tzinfo tests
39
40class FixedOffset(tzinfo):
41 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000042 if isinstance(offset, int):
43 offset = timedelta(minutes=offset)
44 if isinstance(dstoffset, int):
45 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000046 self.__offset = offset
47 self.__name = name
48 self.__dstoffset = dstoffset
49 def __repr__(self):
50 return self.__name.lower()
51 def utcoffset(self, dt):
52 return self.__offset
53 def tzname(self, dt):
54 return self.__name
55 def dst(self, dt):
56 return self.__dstoffset
57
Tim Petersfb8472c2002-12-21 05:04:42 +000058class PicklableFixedOffset(FixedOffset):
59 def __init__(self, offset=None, name=None, dstoffset=None):
60 FixedOffset.__init__(self, offset, name, dstoffset)
61
Tim Peters2a799bf2002-12-16 20:18:38 +000062class TestTZInfo(unittest.TestCase):
63
64 def test_non_abstractness(self):
65 # In order to allow subclasses to get pickled, the C implementation
66 # wasn't able to get away with having __init__ raise
67 # NotImplementedError.
68 useless = tzinfo()
69 dt = datetime.max
70 self.assertRaises(NotImplementedError, useless.tzname, dt)
71 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
72 self.assertRaises(NotImplementedError, useless.dst, dt)
73
74 def test_subclass_must_override(self):
75 class NotEnough(tzinfo):
76 def __init__(self, offset, name):
77 self.__offset = offset
78 self.__name = name
79 self.failUnless(issubclass(NotEnough, tzinfo))
80 ne = NotEnough(3, "NotByALongShot")
81 self.failUnless(isinstance(ne, tzinfo))
82
83 dt = datetime.now()
84 self.assertRaises(NotImplementedError, ne.tzname, dt)
85 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
86 self.assertRaises(NotImplementedError, ne.dst, dt)
87
88 def test_normal(self):
89 fo = FixedOffset(3, "Three")
90 self.failUnless(isinstance(fo, tzinfo))
91 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +000092 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +000093 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +000094 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +000095
96 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +000097 # There's no point to pickling tzinfo objects on their own (they
98 # carry no data), but they need to be picklable anyway else
99 # concrete subclasses can't be pickled.
100 orig = tzinfo.__new__(tzinfo)
101 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000102 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000103 green = pickler.dumps(orig, proto)
104 derived = unpickler.loads(green)
105 self.failUnless(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000106
107 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000108 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000109 offset = timedelta(minutes=-300)
110 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000111 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000112 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000113 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000114 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000115 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000116 green = pickler.dumps(orig, proto)
117 derived = unpickler.loads(green)
118 self.failUnless(isinstance(derived, tzinfo))
119 self.failUnless(type(derived) is PicklableFixedOffset)
120 self.assertEqual(derived.utcoffset(None), offset)
121 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000122
123#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000124# Base clase for testing a particular aspect of timedelta, time, date and
125# datetime comparisons.
126
Guido van Rossumd8faa362007-04-27 19:54:29 +0000127class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000128 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
129
130 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
131 # legit constructor.
132
133 def test_harmless_mixed_comparison(self):
134 me = self.theclass(1, 1, 1)
135
136 self.failIf(me == ())
137 self.failUnless(me != ())
138 self.failIf(() == me)
139 self.failUnless(() != me)
140
Guido van Rossume2a383d2007-01-15 16:59:06 +0000141 self.failUnless(me in [1, 20, [], me])
142 self.failIf(me not in [1, 20, [], me])
Tim Peters07534a62003-02-07 22:50:28 +0000143
Guido van Rossume2a383d2007-01-15 16:59:06 +0000144 self.failUnless([] in [me, 1, 20, []])
145 self.failIf([] not in [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000146
147 def test_harmful_mixed_comparison(self):
148 me = self.theclass(1, 1, 1)
149
150 self.assertRaises(TypeError, lambda: me < ())
151 self.assertRaises(TypeError, lambda: me <= ())
152 self.assertRaises(TypeError, lambda: me > ())
153 self.assertRaises(TypeError, lambda: me >= ())
154
155 self.assertRaises(TypeError, lambda: () < me)
156 self.assertRaises(TypeError, lambda: () <= me)
157 self.assertRaises(TypeError, lambda: () > me)
158 self.assertRaises(TypeError, lambda: () >= me)
159
160 self.assertRaises(TypeError, cmp, (), me)
161 self.assertRaises(TypeError, cmp, me, ())
162
163#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000164# timedelta tests
165
Guido van Rossumd8faa362007-04-27 19:54:29 +0000166class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000167
168 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000169
170 def test_constructor(self):
171 eq = self.assertEqual
172 td = timedelta
173
174 # Check keyword args to constructor
175 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
176 milliseconds=0, microseconds=0))
177 eq(td(1), td(days=1))
178 eq(td(0, 1), td(seconds=1))
179 eq(td(0, 0, 1), td(microseconds=1))
180 eq(td(weeks=1), td(days=7))
181 eq(td(days=1), td(hours=24))
182 eq(td(hours=1), td(minutes=60))
183 eq(td(minutes=1), td(seconds=60))
184 eq(td(seconds=1), td(milliseconds=1000))
185 eq(td(milliseconds=1), td(microseconds=1000))
186
187 # Check float args to constructor
188 eq(td(weeks=1.0/7), td(days=1))
189 eq(td(days=1.0/24), td(hours=1))
190 eq(td(hours=1.0/60), td(minutes=1))
191 eq(td(minutes=1.0/60), td(seconds=1))
192 eq(td(seconds=0.001), td(milliseconds=1))
193 eq(td(milliseconds=0.001), td(microseconds=1))
194
195 def test_computations(self):
196 eq = self.assertEqual
197 td = timedelta
198
199 a = td(7) # One week
200 b = td(0, 60) # One minute
201 c = td(0, 0, 1000) # One millisecond
202 eq(a+b+c, td(7, 60, 1000))
203 eq(a-b, td(6, 24*3600 - 60))
204 eq(-a, td(-7))
205 eq(+a, td(7))
206 eq(-b, td(-1, 24*3600 - 60))
207 eq(-c, td(-1, 24*3600 - 1, 999000))
208 eq(abs(a), a)
209 eq(abs(-a), a)
210 eq(td(6, 24*3600), a)
211 eq(td(0, 0, 60*1000000), b)
212 eq(a*10, td(70))
213 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000214 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000215 eq(b*10, td(0, 600))
216 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000217 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000218 eq(c*10, td(0, 0, 10000))
219 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000220 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000221 eq(a*-1, -a)
222 eq(b*-2, -b-b)
223 eq(c*-2, -c+-c)
224 eq(b*(60*24), (b*60)*24)
225 eq(b*(60*24), (60*b)*24)
226 eq(c*1000, td(0, 1))
227 eq(1000*c, td(0, 1))
228 eq(a//7, td(1))
229 eq(b//10, td(0, 6))
230 eq(c//1000, td(0, 0, 1))
231 eq(a//10, td(0, 7*24*360))
232 eq(a//3600000, td(0, 0, 7*24*1000))
233
234 def test_disallowed_computations(self):
235 a = timedelta(42)
236
237 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000238 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000239 self.assertRaises(TypeError, lambda: a+i)
240 self.assertRaises(TypeError, lambda: a-i)
241 self.assertRaises(TypeError, lambda: i+a)
242 self.assertRaises(TypeError, lambda: i-a)
243
244 # Mul/div by float isn't supported.
245 x = 2.3
246 self.assertRaises(TypeError, lambda: a*x)
247 self.assertRaises(TypeError, lambda: x*a)
248 self.assertRaises(TypeError, lambda: a/x)
249 self.assertRaises(TypeError, lambda: x/a)
250 self.assertRaises(TypeError, lambda: a // x)
251 self.assertRaises(TypeError, lambda: x // a)
252
253 # Divison of int by timedelta doesn't make sense.
254 # Division by zero doesn't make sense.
Guido van Rossume2a383d2007-01-15 16:59:06 +0000255 for zero in 0, 0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000256 self.assertRaises(TypeError, lambda: zero // a)
257 self.assertRaises(ZeroDivisionError, lambda: a // zero)
258
259 def test_basic_attributes(self):
260 days, seconds, us = 1, 7, 31
261 td = timedelta(days, seconds, us)
262 self.assertEqual(td.days, days)
263 self.assertEqual(td.seconds, seconds)
264 self.assertEqual(td.microseconds, us)
265
266 def test_carries(self):
267 t1 = timedelta(days=100,
268 weeks=-7,
269 hours=-24*(100-49),
270 minutes=-3,
271 seconds=12,
272 microseconds=(3*60 - 12) * 1e6 + 1)
273 t2 = timedelta(microseconds=1)
274 self.assertEqual(t1, t2)
275
276 def test_hash_equality(self):
277 t1 = timedelta(days=100,
278 weeks=-7,
279 hours=-24*(100-49),
280 minutes=-3,
281 seconds=12,
282 microseconds=(3*60 - 12) * 1000000)
283 t2 = timedelta()
284 self.assertEqual(hash(t1), hash(t2))
285
286 t1 += timedelta(weeks=7)
287 t2 += timedelta(days=7*7)
288 self.assertEqual(t1, t2)
289 self.assertEqual(hash(t1), hash(t2))
290
291 d = {t1: 1}
292 d[t2] = 2
293 self.assertEqual(len(d), 1)
294 self.assertEqual(d[t1], 2)
295
296 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000297 args = 12, 34, 56
298 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000299 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000300 green = pickler.dumps(orig, proto)
301 derived = unpickler.loads(green)
302 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000303
304 def test_compare(self):
305 t1 = timedelta(2, 3, 4)
306 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000307 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000308 self.failUnless(t1 <= t2)
309 self.failUnless(t1 >= t2)
310 self.failUnless(not t1 != t2)
311 self.failUnless(not t1 < t2)
312 self.failUnless(not t1 > t2)
313 self.assertEqual(cmp(t1, t2), 0)
314 self.assertEqual(cmp(t2, t1), 0)
315
316 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
317 t2 = timedelta(*args) # this is larger than t1
318 self.failUnless(t1 < t2)
319 self.failUnless(t2 > t1)
320 self.failUnless(t1 <= t2)
321 self.failUnless(t2 >= t1)
322 self.failUnless(t1 != t2)
323 self.failUnless(t2 != t1)
324 self.failUnless(not t1 == t2)
325 self.failUnless(not t2 == t1)
326 self.failUnless(not t1 > t2)
327 self.failUnless(not t2 < t1)
328 self.failUnless(not t1 >= t2)
329 self.failUnless(not t2 <= t1)
330 self.assertEqual(cmp(t1, t2), -1)
331 self.assertEqual(cmp(t2, t1), 1)
332
Tim Peters68124bb2003-02-08 03:46:31 +0000333 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000334 self.assertEqual(t1 == badarg, False)
335 self.assertEqual(t1 != badarg, True)
336 self.assertEqual(badarg == t1, False)
337 self.assertEqual(badarg != t1, True)
338
Tim Peters2a799bf2002-12-16 20:18:38 +0000339 self.assertRaises(TypeError, lambda: t1 <= badarg)
340 self.assertRaises(TypeError, lambda: t1 < badarg)
341 self.assertRaises(TypeError, lambda: t1 > badarg)
342 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000343 self.assertRaises(TypeError, lambda: badarg <= t1)
344 self.assertRaises(TypeError, lambda: badarg < t1)
345 self.assertRaises(TypeError, lambda: badarg > t1)
346 self.assertRaises(TypeError, lambda: badarg >= t1)
347
348 def test_str(self):
349 td = timedelta
350 eq = self.assertEqual
351
352 eq(str(td(1)), "1 day, 0:00:00")
353 eq(str(td(-1)), "-1 day, 0:00:00")
354 eq(str(td(2)), "2 days, 0:00:00")
355 eq(str(td(-2)), "-2 days, 0:00:00")
356
357 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
358 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
359 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
360 "-210 days, 23:12:34")
361
362 eq(str(td(milliseconds=1)), "0:00:00.001000")
363 eq(str(td(microseconds=3)), "0:00:00.000003")
364
365 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
366 microseconds=999999)),
367 "999999999 days, 23:59:59.999999")
368
369 def test_roundtrip(self):
370 for td in (timedelta(days=999999999, hours=23, minutes=59,
371 seconds=59, microseconds=999999),
372 timedelta(days=-999999999),
373 timedelta(days=1, seconds=2, microseconds=3)):
374
375 # Verify td -> string -> td identity.
376 s = repr(td)
377 self.failUnless(s.startswith('datetime.'))
378 s = s[9:]
379 td2 = eval(s)
380 self.assertEqual(td, td2)
381
382 # Verify identity via reconstructing from pieces.
383 td2 = timedelta(td.days, td.seconds, td.microseconds)
384 self.assertEqual(td, td2)
385
386 def test_resolution_info(self):
387 self.assert_(isinstance(timedelta.min, timedelta))
388 self.assert_(isinstance(timedelta.max, timedelta))
389 self.assert_(isinstance(timedelta.resolution, timedelta))
390 self.assert_(timedelta.max > timedelta.min)
391 self.assertEqual(timedelta.min, timedelta(-999999999))
392 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
393 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
394
395 def test_overflow(self):
396 tiny = timedelta.resolution
397
398 td = timedelta.min + tiny
399 td -= tiny # no problem
400 self.assertRaises(OverflowError, td.__sub__, tiny)
401 self.assertRaises(OverflowError, td.__add__, -tiny)
402
403 td = timedelta.max - tiny
404 td += tiny # no problem
405 self.assertRaises(OverflowError, td.__add__, tiny)
406 self.assertRaises(OverflowError, td.__sub__, -tiny)
407
408 self.assertRaises(OverflowError, lambda: -timedelta.max)
409
410 def test_microsecond_rounding(self):
411 td = timedelta
412 eq = self.assertEqual
413
414 # Single-field rounding.
415 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
416 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
417 eq(td(milliseconds=0.6/1000), td(microseconds=1))
418 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
419
420 # Rounding due to contributions from more than one field.
421 us_per_hour = 3600e6
422 us_per_day = us_per_hour * 24
423 eq(td(days=.4/us_per_day), td(0))
424 eq(td(hours=.2/us_per_hour), td(0))
425 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
426
427 eq(td(days=-.4/us_per_day), td(0))
428 eq(td(hours=-.2/us_per_hour), td(0))
429 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
430
431 def test_massive_normalization(self):
432 td = timedelta(microseconds=-1)
433 self.assertEqual((td.days, td.seconds, td.microseconds),
434 (-1, 24*3600-1, 999999))
435
436 def test_bool(self):
437 self.failUnless(timedelta(1))
438 self.failUnless(timedelta(0, 1))
439 self.failUnless(timedelta(0, 0, 1))
440 self.failUnless(timedelta(microseconds=1))
441 self.failUnless(not timedelta(0))
442
Tim Petersb0c854d2003-05-17 15:57:00 +0000443 def test_subclass_timedelta(self):
444
445 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000446 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000447 def from_td(td):
448 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000449
450 def as_hours(self):
451 sum = (self.days * 24 +
452 self.seconds / 3600.0 +
453 self.microseconds / 3600e6)
454 return round(sum)
455
456 t1 = T(days=1)
457 self.assert_(type(t1) is T)
458 self.assertEqual(t1.as_hours(), 24)
459
460 t2 = T(days=-1, seconds=-3600)
461 self.assert_(type(t2) is T)
462 self.assertEqual(t2.as_hours(), -25)
463
464 t3 = t1 + t2
465 self.assert_(type(t3) is timedelta)
466 t4 = T.from_td(t3)
467 self.assert_(type(t4) is T)
468 self.assertEqual(t3.days, t4.days)
469 self.assertEqual(t3.seconds, t4.seconds)
470 self.assertEqual(t3.microseconds, t4.microseconds)
471 self.assertEqual(str(t3), str(t4))
472 self.assertEqual(t4.as_hours(), -1)
473
Tim Peters2a799bf2002-12-16 20:18:38 +0000474#############################################################################
475# date tests
476
477class TestDateOnly(unittest.TestCase):
478 # Tests here won't pass if also run on datetime objects, so don't
479 # subclass this to test datetimes too.
480
481 def test_delta_non_days_ignored(self):
482 dt = date(2000, 1, 2)
483 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
484 microseconds=5)
485 days = timedelta(delta.days)
486 self.assertEqual(days, timedelta(1))
487
488 dt2 = dt + delta
489 self.assertEqual(dt2, dt + days)
490
491 dt2 = delta + dt
492 self.assertEqual(dt2, dt + days)
493
494 dt2 = dt - delta
495 self.assertEqual(dt2, dt - days)
496
497 delta = -delta
498 days = timedelta(delta.days)
499 self.assertEqual(days, timedelta(-2))
500
501 dt2 = dt + delta
502 self.assertEqual(dt2, dt + days)
503
504 dt2 = delta + dt
505 self.assertEqual(dt2, dt + days)
506
507 dt2 = dt - delta
508 self.assertEqual(dt2, dt - days)
509
Tim Peters604c0132004-06-07 23:04:33 +0000510class SubclassDate(date):
511 sub_var = 1
512
Guido van Rossumd8faa362007-04-27 19:54:29 +0000513class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000514 # Tests here should pass for both dates and datetimes, except for a
515 # few tests that TestDateTime overrides.
516
517 theclass = date
518
519 def test_basic_attributes(self):
520 dt = self.theclass(2002, 3, 1)
521 self.assertEqual(dt.year, 2002)
522 self.assertEqual(dt.month, 3)
523 self.assertEqual(dt.day, 1)
524
525 def test_roundtrip(self):
526 for dt in (self.theclass(1, 2, 3),
527 self.theclass.today()):
528 # Verify dt -> string -> date identity.
529 s = repr(dt)
530 self.failUnless(s.startswith('datetime.'))
531 s = s[9:]
532 dt2 = eval(s)
533 self.assertEqual(dt, dt2)
534
535 # Verify identity via reconstructing from pieces.
536 dt2 = self.theclass(dt.year, dt.month, dt.day)
537 self.assertEqual(dt, dt2)
538
539 def test_ordinal_conversions(self):
540 # Check some fixed values.
541 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
542 (1, 12, 31, 365),
543 (2, 1, 1, 366),
544 # first example from "Calendrical Calculations"
545 (1945, 11, 12, 710347)]:
546 d = self.theclass(y, m, d)
547 self.assertEqual(n, d.toordinal())
548 fromord = self.theclass.fromordinal(n)
549 self.assertEqual(d, fromord)
550 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000551 # if we're checking something fancier than a date, verify
552 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000553 self.assertEqual(fromord.hour, 0)
554 self.assertEqual(fromord.minute, 0)
555 self.assertEqual(fromord.second, 0)
556 self.assertEqual(fromord.microsecond, 0)
557
Tim Peters0bf60bd2003-01-08 20:40:01 +0000558 # Check first and last days of year spottily across the whole
559 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000560 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000561 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
562 d = self.theclass(year, 1, 1)
563 n = d.toordinal()
564 d2 = self.theclass.fromordinal(n)
565 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000566 # Verify that moving back a day gets to the end of year-1.
567 if year > 1:
568 d = self.theclass.fromordinal(n-1)
569 d2 = self.theclass(year-1, 12, 31)
570 self.assertEqual(d, d2)
571 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000572
573 # Test every day in a leap-year and a non-leap year.
574 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
575 for year, isleap in (2000, True), (2002, False):
576 n = self.theclass(year, 1, 1).toordinal()
577 for month, maxday in zip(range(1, 13), dim):
578 if month == 2 and isleap:
579 maxday += 1
580 for day in range(1, maxday+1):
581 d = self.theclass(year, month, day)
582 self.assertEqual(d.toordinal(), n)
583 self.assertEqual(d, self.theclass.fromordinal(n))
584 n += 1
585
586 def test_extreme_ordinals(self):
587 a = self.theclass.min
588 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
589 aord = a.toordinal()
590 b = a.fromordinal(aord)
591 self.assertEqual(a, b)
592
593 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
594
595 b = a + timedelta(days=1)
596 self.assertEqual(b.toordinal(), aord + 1)
597 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
598
599 a = self.theclass.max
600 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
601 aord = a.toordinal()
602 b = a.fromordinal(aord)
603 self.assertEqual(a, b)
604
605 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
606
607 b = a - timedelta(days=1)
608 self.assertEqual(b.toordinal(), aord - 1)
609 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
610
611 def test_bad_constructor_arguments(self):
612 # bad years
613 self.theclass(MINYEAR, 1, 1) # no exception
614 self.theclass(MAXYEAR, 1, 1) # no exception
615 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
616 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
617 # bad months
618 self.theclass(2000, 1, 1) # no exception
619 self.theclass(2000, 12, 1) # no exception
620 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
621 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
622 # bad days
623 self.theclass(2000, 2, 29) # no exception
624 self.theclass(2004, 2, 29) # no exception
625 self.theclass(2400, 2, 29) # no exception
626 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
627 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
628 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
629 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
630 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
631 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
632
633 def test_hash_equality(self):
634 d = self.theclass(2000, 12, 31)
635 # same thing
636 e = self.theclass(2000, 12, 31)
637 self.assertEqual(d, e)
638 self.assertEqual(hash(d), hash(e))
639
640 dic = {d: 1}
641 dic[e] = 2
642 self.assertEqual(len(dic), 1)
643 self.assertEqual(dic[d], 2)
644 self.assertEqual(dic[e], 2)
645
646 d = self.theclass(2001, 1, 1)
647 # same thing
648 e = self.theclass(2001, 1, 1)
649 self.assertEqual(d, e)
650 self.assertEqual(hash(d), hash(e))
651
652 dic = {d: 1}
653 dic[e] = 2
654 self.assertEqual(len(dic), 1)
655 self.assertEqual(dic[d], 2)
656 self.assertEqual(dic[e], 2)
657
658 def test_computations(self):
659 a = self.theclass(2002, 1, 31)
660 b = self.theclass(1956, 1, 31)
661
662 diff = a-b
663 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
664 self.assertEqual(diff.seconds, 0)
665 self.assertEqual(diff.microseconds, 0)
666
667 day = timedelta(1)
668 week = timedelta(7)
669 a = self.theclass(2002, 3, 2)
670 self.assertEqual(a + day, self.theclass(2002, 3, 3))
671 self.assertEqual(day + a, self.theclass(2002, 3, 3))
672 self.assertEqual(a - day, self.theclass(2002, 3, 1))
673 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
674 self.assertEqual(a + week, self.theclass(2002, 3, 9))
675 self.assertEqual(a - week, self.theclass(2002, 2, 23))
676 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
677 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
678 self.assertEqual((a + week) - a, week)
679 self.assertEqual((a + day) - a, day)
680 self.assertEqual((a - week) - a, -week)
681 self.assertEqual((a - day) - a, -day)
682 self.assertEqual(a - (a + week), -week)
683 self.assertEqual(a - (a + day), -day)
684 self.assertEqual(a - (a - week), week)
685 self.assertEqual(a - (a - day), day)
686
687 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000688 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000689 self.assertRaises(TypeError, lambda: a+i)
690 self.assertRaises(TypeError, lambda: a-i)
691 self.assertRaises(TypeError, lambda: i+a)
692 self.assertRaises(TypeError, lambda: i-a)
693
694 # delta - date is senseless.
695 self.assertRaises(TypeError, lambda: day - a)
696 # mixing date and (delta or date) via * or // is senseless
697 self.assertRaises(TypeError, lambda: day * a)
698 self.assertRaises(TypeError, lambda: a * day)
699 self.assertRaises(TypeError, lambda: day // a)
700 self.assertRaises(TypeError, lambda: a // day)
701 self.assertRaises(TypeError, lambda: a * a)
702 self.assertRaises(TypeError, lambda: a // a)
703 # date + date is senseless
704 self.assertRaises(TypeError, lambda: a + a)
705
706 def test_overflow(self):
707 tiny = self.theclass.resolution
708
709 dt = self.theclass.min + tiny
710 dt -= tiny # no problem
711 self.assertRaises(OverflowError, dt.__sub__, tiny)
712 self.assertRaises(OverflowError, dt.__add__, -tiny)
713
714 dt = self.theclass.max - tiny
715 dt += tiny # no problem
716 self.assertRaises(OverflowError, dt.__add__, tiny)
717 self.assertRaises(OverflowError, dt.__sub__, -tiny)
718
719 def test_fromtimestamp(self):
720 import time
721
722 # Try an arbitrary fixed value.
723 year, month, day = 1999, 9, 19
724 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
725 d = self.theclass.fromtimestamp(ts)
726 self.assertEqual(d.year, year)
727 self.assertEqual(d.month, month)
728 self.assertEqual(d.day, day)
729
Tim Peters1b6f7a92004-06-20 02:50:16 +0000730 def test_insane_fromtimestamp(self):
731 # It's possible that some platform maps time_t to double,
732 # and that this test will fail there. This test should
733 # exempt such platforms (provided they return reasonable
734 # results!).
735 for insane in -1e200, 1e200:
736 self.assertRaises(ValueError, self.theclass.fromtimestamp,
737 insane)
738
Tim Peters2a799bf2002-12-16 20:18:38 +0000739 def test_today(self):
740 import time
741
742 # We claim that today() is like fromtimestamp(time.time()), so
743 # prove it.
744 for dummy in range(3):
745 today = self.theclass.today()
746 ts = time.time()
747 todayagain = self.theclass.fromtimestamp(ts)
748 if today == todayagain:
749 break
750 # There are several legit reasons that could fail:
751 # 1. It recently became midnight, between the today() and the
752 # time() calls.
753 # 2. The platform time() has such fine resolution that we'll
754 # never get the same value twice.
755 # 3. The platform time() has poor resolution, and we just
756 # happened to call today() right before a resolution quantum
757 # boundary.
758 # 4. The system clock got fiddled between calls.
759 # In any case, wait a little while and try again.
760 time.sleep(0.1)
761
762 # It worked or it didn't. If it didn't, assume it's reason #2, and
763 # let the test pass if they're within half a second of each other.
764 self.failUnless(today == todayagain or
765 abs(todayagain - today) < timedelta(seconds=0.5))
766
767 def test_weekday(self):
768 for i in range(7):
769 # March 4, 2002 is a Monday
770 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
771 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
772 # January 2, 1956 is a Monday
773 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
774 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
775
776 def test_isocalendar(self):
777 # Check examples from
778 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
779 for i in range(7):
780 d = self.theclass(2003, 12, 22+i)
781 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
782 d = self.theclass(2003, 12, 29) + timedelta(i)
783 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
784 d = self.theclass(2004, 1, 5+i)
785 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
786 d = self.theclass(2009, 12, 21+i)
787 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
788 d = self.theclass(2009, 12, 28) + timedelta(i)
789 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
790 d = self.theclass(2010, 1, 4+i)
791 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
792
793 def test_iso_long_years(self):
794 # Calculate long ISO years and compare to table from
795 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
796 ISO_LONG_YEARS_TABLE = """
797 4 32 60 88
798 9 37 65 93
799 15 43 71 99
800 20 48 76
801 26 54 82
802
803 105 133 161 189
804 111 139 167 195
805 116 144 172
806 122 150 178
807 128 156 184
808
809 201 229 257 285
810 207 235 263 291
811 212 240 268 296
812 218 246 274
813 224 252 280
814
815 303 331 359 387
816 308 336 364 392
817 314 342 370 398
818 320 348 376
819 325 353 381
820 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000821 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000822 L = []
823 for i in range(400):
824 d = self.theclass(2000+i, 12, 31)
825 d1 = self.theclass(1600+i, 12, 31)
826 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
827 if d.isocalendar()[1] == 53:
828 L.append(i)
829 self.assertEqual(L, iso_long_years)
830
831 def test_isoformat(self):
832 t = self.theclass(2, 3, 2)
833 self.assertEqual(t.isoformat(), "0002-03-02")
834
835 def test_ctime(self):
836 t = self.theclass(2002, 3, 2)
837 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
838
839 def test_strftime(self):
840 t = self.theclass(2005, 3, 2)
841 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000842 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000843 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000844
845 self.assertRaises(TypeError, t.strftime) # needs an arg
846 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
847 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
848
849 # A naive object replaces %z and %Z w/ empty strings.
850 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
851
852 def test_resolution_info(self):
853 self.assert_(isinstance(self.theclass.min, self.theclass))
854 self.assert_(isinstance(self.theclass.max, self.theclass))
855 self.assert_(isinstance(self.theclass.resolution, timedelta))
856 self.assert_(self.theclass.max > self.theclass.min)
857
858 def test_extreme_timedelta(self):
859 big = self.theclass.max - self.theclass.min
860 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
861 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
862 # n == 315537897599999999 ~= 2**58.13
863 justasbig = timedelta(0, 0, n)
864 self.assertEqual(big, justasbig)
865 self.assertEqual(self.theclass.min + big, self.theclass.max)
866 self.assertEqual(self.theclass.max - big, self.theclass.min)
867
868 def test_timetuple(self):
869 for i in range(7):
870 # January 2, 1956 is a Monday (0)
871 d = self.theclass(1956, 1, 2+i)
872 t = d.timetuple()
873 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
874 # February 1, 1956 is a Wednesday (2)
875 d = self.theclass(1956, 2, 1+i)
876 t = d.timetuple()
877 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
878 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
879 # of the year.
880 d = self.theclass(1956, 3, 1+i)
881 t = d.timetuple()
882 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
883 self.assertEqual(t.tm_year, 1956)
884 self.assertEqual(t.tm_mon, 3)
885 self.assertEqual(t.tm_mday, 1+i)
886 self.assertEqual(t.tm_hour, 0)
887 self.assertEqual(t.tm_min, 0)
888 self.assertEqual(t.tm_sec, 0)
889 self.assertEqual(t.tm_wday, (3+i)%7)
890 self.assertEqual(t.tm_yday, 61+i)
891 self.assertEqual(t.tm_isdst, -1)
892
893 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000894 args = 6, 7, 23
895 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000896 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000897 green = pickler.dumps(orig, proto)
898 derived = unpickler.loads(green)
899 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000900
901 def test_compare(self):
902 t1 = self.theclass(2, 3, 4)
903 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000904 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000905 self.failUnless(t1 <= t2)
906 self.failUnless(t1 >= t2)
907 self.failUnless(not t1 != t2)
908 self.failUnless(not t1 < t2)
909 self.failUnless(not t1 > t2)
910 self.assertEqual(cmp(t1, t2), 0)
911 self.assertEqual(cmp(t2, t1), 0)
912
913 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
914 t2 = self.theclass(*args) # this is larger than t1
915 self.failUnless(t1 < t2)
916 self.failUnless(t2 > t1)
917 self.failUnless(t1 <= t2)
918 self.failUnless(t2 >= t1)
919 self.failUnless(t1 != t2)
920 self.failUnless(t2 != t1)
921 self.failUnless(not t1 == t2)
922 self.failUnless(not t2 == t1)
923 self.failUnless(not t1 > t2)
924 self.failUnless(not t2 < t1)
925 self.failUnless(not t1 >= t2)
926 self.failUnless(not t2 <= t1)
927 self.assertEqual(cmp(t1, t2), -1)
928 self.assertEqual(cmp(t2, t1), 1)
929
Tim Peters68124bb2003-02-08 03:46:31 +0000930 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000931 self.assertEqual(t1 == badarg, False)
932 self.assertEqual(t1 != badarg, True)
933 self.assertEqual(badarg == t1, False)
934 self.assertEqual(badarg != t1, True)
935
Tim Peters2a799bf2002-12-16 20:18:38 +0000936 self.assertRaises(TypeError, lambda: t1 < badarg)
937 self.assertRaises(TypeError, lambda: t1 > badarg)
938 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000939 self.assertRaises(TypeError, lambda: badarg <= t1)
940 self.assertRaises(TypeError, lambda: badarg < t1)
941 self.assertRaises(TypeError, lambda: badarg > t1)
942 self.assertRaises(TypeError, lambda: badarg >= t1)
943
Tim Peters8d81a012003-01-24 22:36:34 +0000944 def test_mixed_compare(self):
945 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000946
947 # Our class can be compared for equality to other classes
948 self.assertEqual(our == 1, False)
949 self.assertEqual(1 == our, False)
950 self.assertEqual(our != 1, True)
951 self.assertEqual(1 != our, True)
952
953 # But the ordering is undefined
954 self.assertRaises(TypeError, lambda: our < 1)
955 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000956 self.assertRaises(TypeError, cmp, our, 1)
957 self.assertRaises(TypeError, cmp, 1, our)
958
Guido van Rossum19960592006-08-24 17:29:38 +0000959 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +0000960
Guido van Rossum19960592006-08-24 17:29:38 +0000961 class SomeClass:
962 pass
963
964 their = SomeClass()
965 self.assertEqual(our == their, False)
966 self.assertEqual(their == our, False)
967 self.assertEqual(our != their, True)
968 self.assertEqual(their != our, True)
969 self.assertRaises(TypeError, lambda: our < their)
970 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000971 self.assertRaises(TypeError, cmp, our, their)
Guido van Rossum19960592006-08-24 17:29:38 +0000972 self.assertRaises(TypeError, cmp, their, our)
Tim Peters8d81a012003-01-24 22:36:34 +0000973
Guido van Rossum19960592006-08-24 17:29:38 +0000974 # However, if the other class explicitly defines ordering
975 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +0000976
Guido van Rossum19960592006-08-24 17:29:38 +0000977 class LargerThanAnything:
978 def __lt__(self, other):
979 return False
980 def __le__(self, other):
981 return isinstance(other, LargerThanAnything)
982 def __eq__(self, other):
983 return isinstance(other, LargerThanAnything)
984 def __ne__(self, other):
985 return not isinstance(other, LargerThanAnything)
986 def __gt__(self, other):
987 return not isinstance(other, LargerThanAnything)
988 def __ge__(self, other):
989 return True
990
991 their = LargerThanAnything()
992 self.assertEqual(our == their, False)
993 self.assertEqual(their == our, False)
994 self.assertEqual(our != their, True)
995 self.assertEqual(their != our, True)
996 self.assertEqual(our < their, True)
997 self.assertEqual(their < our, False)
998 self.assertEqual(cmp(our, their), -1)
999 self.assertEqual(cmp(their, our), 1)
Tim Peters8d81a012003-01-24 22:36:34 +00001000
Tim Peters2a799bf2002-12-16 20:18:38 +00001001 def test_bool(self):
1002 # All dates are considered true.
1003 self.failUnless(self.theclass.min)
1004 self.failUnless(self.theclass.max)
1005
Guido van Rossum04110fb2007-08-24 16:32:05 +00001006 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001007 # For nasty technical reasons, we can't handle years before 1900.
1008 cls = self.theclass
1009 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1010 for y in 1, 49, 51, 99, 100, 1000, 1899:
1011 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001012
1013 def test_replace(self):
1014 cls = self.theclass
1015 args = [1, 2, 3]
1016 base = cls(*args)
1017 self.assertEqual(base, base.replace())
1018
1019 i = 0
1020 for name, newval in (("year", 2),
1021 ("month", 3),
1022 ("day", 4)):
1023 newargs = args[:]
1024 newargs[i] = newval
1025 expected = cls(*newargs)
1026 got = base.replace(**{name: newval})
1027 self.assertEqual(expected, got)
1028 i += 1
1029
1030 # Out of bounds.
1031 base = cls(2000, 2, 29)
1032 self.assertRaises(ValueError, base.replace, year=2001)
1033
Tim Petersa98924a2003-05-17 05:55:19 +00001034 def test_subclass_date(self):
1035
1036 class C(self.theclass):
1037 theAnswer = 42
1038
1039 def __new__(cls, *args, **kws):
1040 temp = kws.copy()
1041 extra = temp.pop('extra')
1042 result = self.theclass.__new__(cls, *args, **temp)
1043 result.extra = extra
1044 return result
1045
1046 def newmeth(self, start):
1047 return start + self.year + self.month
1048
1049 args = 2003, 4, 14
1050
1051 dt1 = self.theclass(*args)
1052 dt2 = C(*args, **{'extra': 7})
1053
1054 self.assertEqual(dt2.__class__, C)
1055 self.assertEqual(dt2.theAnswer, 42)
1056 self.assertEqual(dt2.extra, 7)
1057 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1058 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1059
Tim Peters604c0132004-06-07 23:04:33 +00001060 def test_pickling_subclass_date(self):
1061
1062 args = 6, 7, 23
1063 orig = SubclassDate(*args)
1064 for pickler, unpickler, proto in pickle_choices:
1065 green = pickler.dumps(orig, proto)
1066 derived = unpickler.loads(green)
1067 self.assertEqual(orig, derived)
1068
Tim Peters3f606292004-03-21 23:38:41 +00001069 def test_backdoor_resistance(self):
1070 # For fast unpickling, the constructor accepts a pickle string.
1071 # This is a low-overhead backdoor. A user can (by intent or
1072 # mistake) pass a string directly, which (if it's the right length)
1073 # will get treated like a pickle, and bypass the normal sanity
1074 # checks in the constructor. This can create insane objects.
1075 # The constructor doesn't want to burn the time to validate all
1076 # fields, but does check the month field. This stops, e.g.,
1077 # datetime.datetime('1995-03-25') from yielding an insane object.
1078 base = '1995-03-25'
1079 if not issubclass(self.theclass, datetime):
1080 base = base[:4]
1081 for month_byte in '9', chr(0), chr(13), '\xff':
1082 self.assertRaises(TypeError, self.theclass,
1083 base[:2] + month_byte + base[3:])
1084 for ord_byte in range(1, 13):
1085 # This shouldn't blow up because of the month byte alone. If
1086 # the implementation changes to do more-careful checking, it may
1087 # blow up because other fields are insane.
Martin v. Löwis10a60b32007-07-18 02:28:27 +00001088 self.theclass(bytes(base[:2] + chr(ord_byte) + base[3:]))
Tim Peterseb1a4962003-05-17 02:25:20 +00001089
Tim Peters2a799bf2002-12-16 20:18:38 +00001090#############################################################################
1091# datetime tests
1092
Tim Peters604c0132004-06-07 23:04:33 +00001093class SubclassDatetime(datetime):
1094 sub_var = 1
1095
Tim Peters2a799bf2002-12-16 20:18:38 +00001096class TestDateTime(TestDate):
1097
1098 theclass = datetime
1099
1100 def test_basic_attributes(self):
1101 dt = self.theclass(2002, 3, 1, 12, 0)
1102 self.assertEqual(dt.year, 2002)
1103 self.assertEqual(dt.month, 3)
1104 self.assertEqual(dt.day, 1)
1105 self.assertEqual(dt.hour, 12)
1106 self.assertEqual(dt.minute, 0)
1107 self.assertEqual(dt.second, 0)
1108 self.assertEqual(dt.microsecond, 0)
1109
1110 def test_basic_attributes_nonzero(self):
1111 # Make sure all attributes are non-zero so bugs in
1112 # bit-shifting access show up.
1113 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1114 self.assertEqual(dt.year, 2002)
1115 self.assertEqual(dt.month, 3)
1116 self.assertEqual(dt.day, 1)
1117 self.assertEqual(dt.hour, 12)
1118 self.assertEqual(dt.minute, 59)
1119 self.assertEqual(dt.second, 59)
1120 self.assertEqual(dt.microsecond, 8000)
1121
1122 def test_roundtrip(self):
1123 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1124 self.theclass.now()):
1125 # Verify dt -> string -> datetime identity.
1126 s = repr(dt)
1127 self.failUnless(s.startswith('datetime.'))
1128 s = s[9:]
1129 dt2 = eval(s)
1130 self.assertEqual(dt, dt2)
1131
1132 # Verify identity via reconstructing from pieces.
1133 dt2 = self.theclass(dt.year, dt.month, dt.day,
1134 dt.hour, dt.minute, dt.second,
1135 dt.microsecond)
1136 self.assertEqual(dt, dt2)
1137
1138 def test_isoformat(self):
1139 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1140 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1141 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1142 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1143 # str is ISO format with the separator forced to a blank.
1144 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1145
1146 t = self.theclass(2, 3, 2)
1147 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1148 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1149 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1150 # str is ISO format with the separator forced to a blank.
1151 self.assertEqual(str(t), "0002-03-02 00:00:00")
1152
1153 def test_more_ctime(self):
1154 # Test fields that TestDate doesn't touch.
1155 import time
1156
1157 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1158 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1159 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1160 # out. The difference is that t.ctime() produces " 2" for the day,
1161 # but platform ctime() produces "02" for the day. According to
1162 # C99, t.ctime() is correct here.
1163 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1164
1165 # So test a case where that difference doesn't matter.
1166 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1167 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1168
1169 def test_tz_independent_comparing(self):
1170 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1171 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1172 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1173 self.assertEqual(dt1, dt3)
1174 self.assert_(dt2 > dt3)
1175
1176 # Make sure comparison doesn't forget microseconds, and isn't done
1177 # via comparing a float timestamp (an IEEE double doesn't have enough
1178 # precision to span microsecond resolution across years 1 thru 9999,
1179 # so comparing via timestamp necessarily calls some distinct values
1180 # equal).
1181 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1182 us = timedelta(microseconds=1)
1183 dt2 = dt1 + us
1184 self.assertEqual(dt2 - dt1, us)
1185 self.assert_(dt1 < dt2)
1186
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001187 def test_strftime_with_bad_tzname_replace(self):
1188 # verify ok if tzinfo.tzname().replace() returns a non-string
1189 class MyTzInfo(FixedOffset):
1190 def tzname(self, dt):
1191 class MyStr(str):
1192 def replace(self, *args):
1193 return None
1194 return MyStr('name')
1195 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1196 self.assertRaises(TypeError, t.strftime, '%Z')
1197
Tim Peters2a799bf2002-12-16 20:18:38 +00001198 def test_bad_constructor_arguments(self):
1199 # bad years
1200 self.theclass(MINYEAR, 1, 1) # no exception
1201 self.theclass(MAXYEAR, 1, 1) # no exception
1202 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1203 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1204 # bad months
1205 self.theclass(2000, 1, 1) # no exception
1206 self.theclass(2000, 12, 1) # no exception
1207 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1208 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1209 # bad days
1210 self.theclass(2000, 2, 29) # no exception
1211 self.theclass(2004, 2, 29) # no exception
1212 self.theclass(2400, 2, 29) # no exception
1213 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1214 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1215 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1216 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1217 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1218 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1219 # bad hours
1220 self.theclass(2000, 1, 31, 0) # no exception
1221 self.theclass(2000, 1, 31, 23) # no exception
1222 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1223 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1224 # bad minutes
1225 self.theclass(2000, 1, 31, 23, 0) # no exception
1226 self.theclass(2000, 1, 31, 23, 59) # no exception
1227 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1228 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1229 # bad seconds
1230 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1231 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1232 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1233 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1234 # bad microseconds
1235 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1236 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1237 self.assertRaises(ValueError, self.theclass,
1238 2000, 1, 31, 23, 59, 59, -1)
1239 self.assertRaises(ValueError, self.theclass,
1240 2000, 1, 31, 23, 59, 59,
1241 1000000)
1242
1243 def test_hash_equality(self):
1244 d = self.theclass(2000, 12, 31, 23, 30, 17)
1245 e = self.theclass(2000, 12, 31, 23, 30, 17)
1246 self.assertEqual(d, e)
1247 self.assertEqual(hash(d), hash(e))
1248
1249 dic = {d: 1}
1250 dic[e] = 2
1251 self.assertEqual(len(dic), 1)
1252 self.assertEqual(dic[d], 2)
1253 self.assertEqual(dic[e], 2)
1254
1255 d = self.theclass(2001, 1, 1, 0, 5, 17)
1256 e = self.theclass(2001, 1, 1, 0, 5, 17)
1257 self.assertEqual(d, e)
1258 self.assertEqual(hash(d), hash(e))
1259
1260 dic = {d: 1}
1261 dic[e] = 2
1262 self.assertEqual(len(dic), 1)
1263 self.assertEqual(dic[d], 2)
1264 self.assertEqual(dic[e], 2)
1265
1266 def test_computations(self):
1267 a = self.theclass(2002, 1, 31)
1268 b = self.theclass(1956, 1, 31)
1269 diff = a-b
1270 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1271 self.assertEqual(diff.seconds, 0)
1272 self.assertEqual(diff.microseconds, 0)
1273 a = self.theclass(2002, 3, 2, 17, 6)
1274 millisec = timedelta(0, 0, 1000)
1275 hour = timedelta(0, 3600)
1276 day = timedelta(1)
1277 week = timedelta(7)
1278 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1279 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1280 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1281 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1282 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1283 self.assertEqual(a - hour, a + -hour)
1284 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1285 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1286 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1287 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1288 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1289 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1290 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1291 self.assertEqual((a + week) - a, week)
1292 self.assertEqual((a + day) - a, day)
1293 self.assertEqual((a + hour) - a, hour)
1294 self.assertEqual((a + millisec) - a, millisec)
1295 self.assertEqual((a - week) - a, -week)
1296 self.assertEqual((a - day) - a, -day)
1297 self.assertEqual((a - hour) - a, -hour)
1298 self.assertEqual((a - millisec) - a, -millisec)
1299 self.assertEqual(a - (a + week), -week)
1300 self.assertEqual(a - (a + day), -day)
1301 self.assertEqual(a - (a + hour), -hour)
1302 self.assertEqual(a - (a + millisec), -millisec)
1303 self.assertEqual(a - (a - week), week)
1304 self.assertEqual(a - (a - day), day)
1305 self.assertEqual(a - (a - hour), hour)
1306 self.assertEqual(a - (a - millisec), millisec)
1307 self.assertEqual(a + (week + day + hour + millisec),
1308 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1309 self.assertEqual(a + (week + day + hour + millisec),
1310 (((a + week) + day) + hour) + millisec)
1311 self.assertEqual(a - (week + day + hour + millisec),
1312 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1313 self.assertEqual(a - (week + day + hour + millisec),
1314 (((a - week) - day) - hour) - millisec)
1315 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001316 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001317 self.assertRaises(TypeError, lambda: a+i)
1318 self.assertRaises(TypeError, lambda: a-i)
1319 self.assertRaises(TypeError, lambda: i+a)
1320 self.assertRaises(TypeError, lambda: i-a)
1321
1322 # delta - datetime is senseless.
1323 self.assertRaises(TypeError, lambda: day - a)
1324 # mixing datetime and (delta or datetime) via * or // is senseless
1325 self.assertRaises(TypeError, lambda: day * a)
1326 self.assertRaises(TypeError, lambda: a * day)
1327 self.assertRaises(TypeError, lambda: day // a)
1328 self.assertRaises(TypeError, lambda: a // day)
1329 self.assertRaises(TypeError, lambda: a * a)
1330 self.assertRaises(TypeError, lambda: a // a)
1331 # datetime + datetime is senseless
1332 self.assertRaises(TypeError, lambda: a + a)
1333
1334 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001335 args = 6, 7, 23, 20, 59, 1, 64**2
1336 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001337 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001338 green = pickler.dumps(orig, proto)
1339 derived = unpickler.loads(green)
1340 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001341
Guido van Rossum275666f2003-02-07 21:49:01 +00001342 def test_more_pickling(self):
1343 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1344 s = pickle.dumps(a)
1345 b = pickle.loads(s)
1346 self.assertEqual(b.year, 2003)
1347 self.assertEqual(b.month, 2)
1348 self.assertEqual(b.day, 7)
1349
Tim Peters604c0132004-06-07 23:04:33 +00001350 def test_pickling_subclass_datetime(self):
1351 args = 6, 7, 23, 20, 59, 1, 64**2
1352 orig = SubclassDatetime(*args)
1353 for pickler, unpickler, proto in pickle_choices:
1354 green = pickler.dumps(orig, proto)
1355 derived = unpickler.loads(green)
1356 self.assertEqual(orig, derived)
1357
Tim Peters2a799bf2002-12-16 20:18:38 +00001358 def test_more_compare(self):
1359 # The test_compare() inherited from TestDate covers the error cases.
1360 # We just want to test lexicographic ordering on the members datetime
1361 # has that date lacks.
1362 args = [2000, 11, 29, 20, 58, 16, 999998]
1363 t1 = self.theclass(*args)
1364 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001365 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001366 self.failUnless(t1 <= t2)
1367 self.failUnless(t1 >= t2)
1368 self.failUnless(not t1 != t2)
1369 self.failUnless(not t1 < t2)
1370 self.failUnless(not t1 > t2)
1371 self.assertEqual(cmp(t1, t2), 0)
1372 self.assertEqual(cmp(t2, t1), 0)
1373
1374 for i in range(len(args)):
1375 newargs = args[:]
1376 newargs[i] = args[i] + 1
1377 t2 = self.theclass(*newargs) # this is larger than t1
1378 self.failUnless(t1 < t2)
1379 self.failUnless(t2 > t1)
1380 self.failUnless(t1 <= t2)
1381 self.failUnless(t2 >= t1)
1382 self.failUnless(t1 != t2)
1383 self.failUnless(t2 != t1)
1384 self.failUnless(not t1 == t2)
1385 self.failUnless(not t2 == t1)
1386 self.failUnless(not t1 > t2)
1387 self.failUnless(not t2 < t1)
1388 self.failUnless(not t1 >= t2)
1389 self.failUnless(not t2 <= t1)
1390 self.assertEqual(cmp(t1, t2), -1)
1391 self.assertEqual(cmp(t2, t1), 1)
1392
1393
1394 # A helper for timestamp constructor tests.
1395 def verify_field_equality(self, expected, got):
1396 self.assertEqual(expected.tm_year, got.year)
1397 self.assertEqual(expected.tm_mon, got.month)
1398 self.assertEqual(expected.tm_mday, got.day)
1399 self.assertEqual(expected.tm_hour, got.hour)
1400 self.assertEqual(expected.tm_min, got.minute)
1401 self.assertEqual(expected.tm_sec, got.second)
1402
1403 def test_fromtimestamp(self):
1404 import time
1405
1406 ts = time.time()
1407 expected = time.localtime(ts)
1408 got = self.theclass.fromtimestamp(ts)
1409 self.verify_field_equality(expected, got)
1410
1411 def test_utcfromtimestamp(self):
1412 import time
1413
1414 ts = time.time()
1415 expected = time.gmtime(ts)
1416 got = self.theclass.utcfromtimestamp(ts)
1417 self.verify_field_equality(expected, got)
1418
Thomas Wouters477c8d52006-05-27 19:21:47 +00001419 def test_microsecond_rounding(self):
1420 # Test whether fromtimestamp "rounds up" floats that are less
1421 # than one microsecond smaller than an integer.
1422 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1423 self.theclass.fromtimestamp(1))
1424
Tim Peters1b6f7a92004-06-20 02:50:16 +00001425 def test_insane_fromtimestamp(self):
1426 # It's possible that some platform maps time_t to double,
1427 # and that this test will fail there. This test should
1428 # exempt such platforms (provided they return reasonable
1429 # results!).
1430 for insane in -1e200, 1e200:
1431 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1432 insane)
1433
1434 def test_insane_utcfromtimestamp(self):
1435 # It's possible that some platform maps time_t to double,
1436 # and that this test will fail there. This test should
1437 # exempt such platforms (provided they return reasonable
1438 # results!).
1439 for insane in -1e200, 1e200:
1440 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1441 insane)
1442
Guido van Rossumd8faa362007-04-27 19:54:29 +00001443 def test_negative_float_fromtimestamp(self):
1444 # Windows doesn't accept negative timestamps
1445 if os.name == "nt":
1446 return
1447 # The result is tz-dependent; at least test that this doesn't
1448 # fail (like it did before bug 1646728 was fixed).
1449 self.theclass.fromtimestamp(-1.05)
1450
1451 def test_negative_float_utcfromtimestamp(self):
1452 # Windows doesn't accept negative timestamps
1453 if os.name == "nt":
1454 return
1455 d = self.theclass.utcfromtimestamp(-1.05)
1456 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1457
Tim Peters2a799bf2002-12-16 20:18:38 +00001458 def test_utcnow(self):
1459 import time
1460
1461 # Call it a success if utcnow() and utcfromtimestamp() are within
1462 # a second of each other.
1463 tolerance = timedelta(seconds=1)
1464 for dummy in range(3):
1465 from_now = self.theclass.utcnow()
1466 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1467 if abs(from_timestamp - from_now) <= tolerance:
1468 break
1469 # Else try again a few times.
1470 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1471
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001472 def test_strptime(self):
1473 import time
1474
1475 string = '2004-12-01 13:02:47'
1476 format = '%Y-%m-%d %H:%M:%S'
1477 expected = self.theclass(*(time.strptime(string, format)[0:6]))
1478 got = self.theclass.strptime(string, format)
1479 self.assertEqual(expected, got)
1480
Tim Peters2a799bf2002-12-16 20:18:38 +00001481 def test_more_timetuple(self):
1482 # This tests fields beyond those tested by the TestDate.test_timetuple.
1483 t = self.theclass(2004, 12, 31, 6, 22, 33)
1484 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1485 self.assertEqual(t.timetuple(),
1486 (t.year, t.month, t.day,
1487 t.hour, t.minute, t.second,
1488 t.weekday(),
1489 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1490 -1))
1491 tt = t.timetuple()
1492 self.assertEqual(tt.tm_year, t.year)
1493 self.assertEqual(tt.tm_mon, t.month)
1494 self.assertEqual(tt.tm_mday, t.day)
1495 self.assertEqual(tt.tm_hour, t.hour)
1496 self.assertEqual(tt.tm_min, t.minute)
1497 self.assertEqual(tt.tm_sec, t.second)
1498 self.assertEqual(tt.tm_wday, t.weekday())
1499 self.assertEqual(tt.tm_yday, t.toordinal() -
1500 date(t.year, 1, 1).toordinal() + 1)
1501 self.assertEqual(tt.tm_isdst, -1)
1502
1503 def test_more_strftime(self):
1504 # This tests fields beyond those tested by the TestDate.test_strftime.
1505 t = self.theclass(2004, 12, 31, 6, 22, 33)
1506 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1507 "12 31 04 33 22 06 366")
1508
1509 def test_extract(self):
1510 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1511 self.assertEqual(dt.date(), date(2002, 3, 4))
1512 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1513
1514 def test_combine(self):
1515 d = date(2002, 3, 4)
1516 t = time(18, 45, 3, 1234)
1517 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1518 combine = self.theclass.combine
1519 dt = combine(d, t)
1520 self.assertEqual(dt, expected)
1521
1522 dt = combine(time=t, date=d)
1523 self.assertEqual(dt, expected)
1524
1525 self.assertEqual(d, dt.date())
1526 self.assertEqual(t, dt.time())
1527 self.assertEqual(dt, combine(dt.date(), dt.time()))
1528
1529 self.assertRaises(TypeError, combine) # need an arg
1530 self.assertRaises(TypeError, combine, d) # need two args
1531 self.assertRaises(TypeError, combine, t, d) # args reversed
1532 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1533 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1534
Tim Peters12bf3392002-12-24 05:41:27 +00001535 def test_replace(self):
1536 cls = self.theclass
1537 args = [1, 2, 3, 4, 5, 6, 7]
1538 base = cls(*args)
1539 self.assertEqual(base, base.replace())
1540
1541 i = 0
1542 for name, newval in (("year", 2),
1543 ("month", 3),
1544 ("day", 4),
1545 ("hour", 5),
1546 ("minute", 6),
1547 ("second", 7),
1548 ("microsecond", 8)):
1549 newargs = args[:]
1550 newargs[i] = newval
1551 expected = cls(*newargs)
1552 got = base.replace(**{name: newval})
1553 self.assertEqual(expected, got)
1554 i += 1
1555
1556 # Out of bounds.
1557 base = cls(2000, 2, 29)
1558 self.assertRaises(ValueError, base.replace, year=2001)
1559
Tim Peters80475bb2002-12-25 07:40:55 +00001560 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001561 # Pretty boring! The TZ test is more interesting here. astimezone()
1562 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001563 dt = self.theclass.now()
1564 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001565 self.assertRaises(TypeError, dt.astimezone) # not enough args
1566 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1567 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001568 self.assertRaises(ValueError, dt.astimezone, f) # naive
1569 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001570
Tim Peters52dcce22003-01-23 16:36:11 +00001571 class Bogus(tzinfo):
1572 def utcoffset(self, dt): return None
1573 def dst(self, dt): return timedelta(0)
1574 bog = Bogus()
1575 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1576
1577 class AlsoBogus(tzinfo):
1578 def utcoffset(self, dt): return timedelta(0)
1579 def dst(self, dt): return None
1580 alsobog = AlsoBogus()
1581 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001582
Tim Petersa98924a2003-05-17 05:55:19 +00001583 def test_subclass_datetime(self):
1584
1585 class C(self.theclass):
1586 theAnswer = 42
1587
1588 def __new__(cls, *args, **kws):
1589 temp = kws.copy()
1590 extra = temp.pop('extra')
1591 result = self.theclass.__new__(cls, *args, **temp)
1592 result.extra = extra
1593 return result
1594
1595 def newmeth(self, start):
1596 return start + self.year + self.month + self.second
1597
1598 args = 2003, 4, 14, 12, 13, 41
1599
1600 dt1 = self.theclass(*args)
1601 dt2 = C(*args, **{'extra': 7})
1602
1603 self.assertEqual(dt2.__class__, C)
1604 self.assertEqual(dt2.theAnswer, 42)
1605 self.assertEqual(dt2.extra, 7)
1606 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1607 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1608 dt1.second - 7)
1609
Tim Peters604c0132004-06-07 23:04:33 +00001610class SubclassTime(time):
1611 sub_var = 1
1612
Guido van Rossumd8faa362007-04-27 19:54:29 +00001613class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001614
1615 theclass = time
1616
1617 def test_basic_attributes(self):
1618 t = self.theclass(12, 0)
1619 self.assertEqual(t.hour, 12)
1620 self.assertEqual(t.minute, 0)
1621 self.assertEqual(t.second, 0)
1622 self.assertEqual(t.microsecond, 0)
1623
1624 def test_basic_attributes_nonzero(self):
1625 # Make sure all attributes are non-zero so bugs in
1626 # bit-shifting access show up.
1627 t = self.theclass(12, 59, 59, 8000)
1628 self.assertEqual(t.hour, 12)
1629 self.assertEqual(t.minute, 59)
1630 self.assertEqual(t.second, 59)
1631 self.assertEqual(t.microsecond, 8000)
1632
1633 def test_roundtrip(self):
1634 t = self.theclass(1, 2, 3, 4)
1635
1636 # Verify t -> string -> time identity.
1637 s = repr(t)
1638 self.failUnless(s.startswith('datetime.'))
1639 s = s[9:]
1640 t2 = eval(s)
1641 self.assertEqual(t, t2)
1642
1643 # Verify identity via reconstructing from pieces.
1644 t2 = self.theclass(t.hour, t.minute, t.second,
1645 t.microsecond)
1646 self.assertEqual(t, t2)
1647
1648 def test_comparing(self):
1649 args = [1, 2, 3, 4]
1650 t1 = self.theclass(*args)
1651 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001652 self.assertEqual(t1, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001653 self.failUnless(t1 <= t2)
1654 self.failUnless(t1 >= t2)
1655 self.failUnless(not t1 != t2)
1656 self.failUnless(not t1 < t2)
1657 self.failUnless(not t1 > t2)
1658 self.assertEqual(cmp(t1, t2), 0)
1659 self.assertEqual(cmp(t2, t1), 0)
1660
1661 for i in range(len(args)):
1662 newargs = args[:]
1663 newargs[i] = args[i] + 1
1664 t2 = self.theclass(*newargs) # this is larger than t1
1665 self.failUnless(t1 < t2)
1666 self.failUnless(t2 > t1)
1667 self.failUnless(t1 <= t2)
1668 self.failUnless(t2 >= t1)
1669 self.failUnless(t1 != t2)
1670 self.failUnless(t2 != t1)
1671 self.failUnless(not t1 == t2)
1672 self.failUnless(not t2 == t1)
1673 self.failUnless(not t1 > t2)
1674 self.failUnless(not t2 < t1)
1675 self.failUnless(not t1 >= t2)
1676 self.failUnless(not t2 <= t1)
1677 self.assertEqual(cmp(t1, t2), -1)
1678 self.assertEqual(cmp(t2, t1), 1)
1679
Tim Peters68124bb2003-02-08 03:46:31 +00001680 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001681 self.assertEqual(t1 == badarg, False)
1682 self.assertEqual(t1 != badarg, True)
1683 self.assertEqual(badarg == t1, False)
1684 self.assertEqual(badarg != t1, True)
1685
Tim Peters2a799bf2002-12-16 20:18:38 +00001686 self.assertRaises(TypeError, lambda: t1 <= badarg)
1687 self.assertRaises(TypeError, lambda: t1 < badarg)
1688 self.assertRaises(TypeError, lambda: t1 > badarg)
1689 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001690 self.assertRaises(TypeError, lambda: badarg <= t1)
1691 self.assertRaises(TypeError, lambda: badarg < t1)
1692 self.assertRaises(TypeError, lambda: badarg > t1)
1693 self.assertRaises(TypeError, lambda: badarg >= t1)
1694
1695 def test_bad_constructor_arguments(self):
1696 # bad hours
1697 self.theclass(0, 0) # no exception
1698 self.theclass(23, 0) # no exception
1699 self.assertRaises(ValueError, self.theclass, -1, 0)
1700 self.assertRaises(ValueError, self.theclass, 24, 0)
1701 # bad minutes
1702 self.theclass(23, 0) # no exception
1703 self.theclass(23, 59) # no exception
1704 self.assertRaises(ValueError, self.theclass, 23, -1)
1705 self.assertRaises(ValueError, self.theclass, 23, 60)
1706 # bad seconds
1707 self.theclass(23, 59, 0) # no exception
1708 self.theclass(23, 59, 59) # no exception
1709 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1710 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1711 # bad microseconds
1712 self.theclass(23, 59, 59, 0) # no exception
1713 self.theclass(23, 59, 59, 999999) # no exception
1714 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1715 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1716
1717 def test_hash_equality(self):
1718 d = self.theclass(23, 30, 17)
1719 e = self.theclass(23, 30, 17)
1720 self.assertEqual(d, e)
1721 self.assertEqual(hash(d), hash(e))
1722
1723 dic = {d: 1}
1724 dic[e] = 2
1725 self.assertEqual(len(dic), 1)
1726 self.assertEqual(dic[d], 2)
1727 self.assertEqual(dic[e], 2)
1728
1729 d = self.theclass(0, 5, 17)
1730 e = self.theclass(0, 5, 17)
1731 self.assertEqual(d, e)
1732 self.assertEqual(hash(d), hash(e))
1733
1734 dic = {d: 1}
1735 dic[e] = 2
1736 self.assertEqual(len(dic), 1)
1737 self.assertEqual(dic[d], 2)
1738 self.assertEqual(dic[e], 2)
1739
1740 def test_isoformat(self):
1741 t = self.theclass(4, 5, 1, 123)
1742 self.assertEqual(t.isoformat(), "04:05:01.000123")
1743 self.assertEqual(t.isoformat(), str(t))
1744
1745 t = self.theclass()
1746 self.assertEqual(t.isoformat(), "00:00:00")
1747 self.assertEqual(t.isoformat(), str(t))
1748
1749 t = self.theclass(microsecond=1)
1750 self.assertEqual(t.isoformat(), "00:00:00.000001")
1751 self.assertEqual(t.isoformat(), str(t))
1752
1753 t = self.theclass(microsecond=10)
1754 self.assertEqual(t.isoformat(), "00:00:00.000010")
1755 self.assertEqual(t.isoformat(), str(t))
1756
1757 t = self.theclass(microsecond=100)
1758 self.assertEqual(t.isoformat(), "00:00:00.000100")
1759 self.assertEqual(t.isoformat(), str(t))
1760
1761 t = self.theclass(microsecond=1000)
1762 self.assertEqual(t.isoformat(), "00:00:00.001000")
1763 self.assertEqual(t.isoformat(), str(t))
1764
1765 t = self.theclass(microsecond=10000)
1766 self.assertEqual(t.isoformat(), "00:00:00.010000")
1767 self.assertEqual(t.isoformat(), str(t))
1768
1769 t = self.theclass(microsecond=100000)
1770 self.assertEqual(t.isoformat(), "00:00:00.100000")
1771 self.assertEqual(t.isoformat(), str(t))
1772
Thomas Wouterscf297e42007-02-23 15:07:44 +00001773 def test_1653736(self):
1774 # verify it doesn't accept extra keyword arguments
1775 t = self.theclass(second=1)
1776 self.assertRaises(TypeError, t.isoformat, foo=3)
1777
Tim Peters2a799bf2002-12-16 20:18:38 +00001778 def test_strftime(self):
1779 t = self.theclass(1, 2, 3, 4)
1780 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1781 # A naive object replaces %z and %Z with empty strings.
1782 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1783
1784 def test_str(self):
1785 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1786 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1787 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1788 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1789 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1790
1791 def test_repr(self):
1792 name = 'datetime.' + self.theclass.__name__
1793 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1794 "%s(1, 2, 3, 4)" % name)
1795 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1796 "%s(10, 2, 3, 4000)" % name)
1797 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1798 "%s(0, 2, 3, 400000)" % name)
1799 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1800 "%s(12, 2, 3)" % name)
1801 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1802 "%s(23, 15)" % name)
1803
1804 def test_resolution_info(self):
1805 self.assert_(isinstance(self.theclass.min, self.theclass))
1806 self.assert_(isinstance(self.theclass.max, self.theclass))
1807 self.assert_(isinstance(self.theclass.resolution, timedelta))
1808 self.assert_(self.theclass.max > self.theclass.min)
1809
1810 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001811 args = 20, 59, 16, 64**2
1812 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001813 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001814 green = pickler.dumps(orig, proto)
1815 derived = unpickler.loads(green)
1816 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001817
Tim Peters604c0132004-06-07 23:04:33 +00001818 def test_pickling_subclass_time(self):
1819 args = 20, 59, 16, 64**2
1820 orig = SubclassTime(*args)
1821 for pickler, unpickler, proto in pickle_choices:
1822 green = pickler.dumps(orig, proto)
1823 derived = unpickler.loads(green)
1824 self.assertEqual(orig, derived)
1825
Tim Peters2a799bf2002-12-16 20:18:38 +00001826 def test_bool(self):
1827 cls = self.theclass
1828 self.failUnless(cls(1))
1829 self.failUnless(cls(0, 1))
1830 self.failUnless(cls(0, 0, 1))
1831 self.failUnless(cls(0, 0, 0, 1))
1832 self.failUnless(not cls(0))
1833 self.failUnless(not cls())
1834
Tim Peters12bf3392002-12-24 05:41:27 +00001835 def test_replace(self):
1836 cls = self.theclass
1837 args = [1, 2, 3, 4]
1838 base = cls(*args)
1839 self.assertEqual(base, base.replace())
1840
1841 i = 0
1842 for name, newval in (("hour", 5),
1843 ("minute", 6),
1844 ("second", 7),
1845 ("microsecond", 8)):
1846 newargs = args[:]
1847 newargs[i] = newval
1848 expected = cls(*newargs)
1849 got = base.replace(**{name: newval})
1850 self.assertEqual(expected, got)
1851 i += 1
1852
1853 # Out of bounds.
1854 base = cls(1)
1855 self.assertRaises(ValueError, base.replace, hour=24)
1856 self.assertRaises(ValueError, base.replace, minute=-1)
1857 self.assertRaises(ValueError, base.replace, second=100)
1858 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1859
Tim Petersa98924a2003-05-17 05:55:19 +00001860 def test_subclass_time(self):
1861
1862 class C(self.theclass):
1863 theAnswer = 42
1864
1865 def __new__(cls, *args, **kws):
1866 temp = kws.copy()
1867 extra = temp.pop('extra')
1868 result = self.theclass.__new__(cls, *args, **temp)
1869 result.extra = extra
1870 return result
1871
1872 def newmeth(self, start):
1873 return start + self.hour + self.second
1874
1875 args = 4, 5, 6
1876
1877 dt1 = self.theclass(*args)
1878 dt2 = C(*args, **{'extra': 7})
1879
1880 self.assertEqual(dt2.__class__, C)
1881 self.assertEqual(dt2.theAnswer, 42)
1882 self.assertEqual(dt2.extra, 7)
1883 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1884 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1885
Armin Rigof4afb212005-11-07 07:15:48 +00001886 def test_backdoor_resistance(self):
1887 # see TestDate.test_backdoor_resistance().
1888 base = '2:59.0'
1889 for hour_byte in ' ', '9', chr(24), '\xff':
1890 self.assertRaises(TypeError, self.theclass,
1891 hour_byte + base[1:])
1892
Tim Peters855fe882002-12-22 03:43:39 +00001893# A mixin for classes with a tzinfo= argument. Subclasses must define
1894# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001895# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001896class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001897
Tim Petersbad8ff02002-12-30 20:52:32 +00001898 def test_argument_passing(self):
1899 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001900 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001901 class introspective(tzinfo):
1902 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001903 def utcoffset(self, dt):
1904 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001905 dst = utcoffset
1906
1907 obj = cls(1, 2, 3, tzinfo=introspective())
1908
Tim Peters0bf60bd2003-01-08 20:40:01 +00001909 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001910 self.assertEqual(obj.tzname(), expected)
1911
Tim Peters0bf60bd2003-01-08 20:40:01 +00001912 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001913 self.assertEqual(obj.utcoffset(), expected)
1914 self.assertEqual(obj.dst(), expected)
1915
Tim Peters855fe882002-12-22 03:43:39 +00001916 def test_bad_tzinfo_classes(self):
1917 cls = self.theclass
1918 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001919
Tim Peters855fe882002-12-22 03:43:39 +00001920 class NiceTry(object):
1921 def __init__(self): pass
1922 def utcoffset(self, dt): pass
1923 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1924
1925 class BetterTry(tzinfo):
1926 def __init__(self): pass
1927 def utcoffset(self, dt): pass
1928 b = BetterTry()
1929 t = cls(1, 1, 1, tzinfo=b)
1930 self.failUnless(t.tzinfo is b)
1931
1932 def test_utc_offset_out_of_bounds(self):
1933 class Edgy(tzinfo):
1934 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00001935 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00001936 def utcoffset(self, dt):
1937 return self.offset
1938
1939 cls = self.theclass
1940 for offset, legit in ((-1440, False),
1941 (-1439, True),
1942 (1439, True),
1943 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001944 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00001945 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001946 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00001947 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001948 else:
1949 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00001950 if legit:
1951 aofs = abs(offset)
1952 h, m = divmod(aofs, 60)
1953 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001954 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00001955 t = t.timetz()
1956 self.assertEqual(str(t), "01:02:03" + tag)
1957 else:
1958 self.assertRaises(ValueError, str, t)
1959
1960 def test_tzinfo_classes(self):
1961 cls = self.theclass
1962 class C1(tzinfo):
1963 def utcoffset(self, dt): return None
1964 def dst(self, dt): return None
1965 def tzname(self, dt): return None
1966 for t in (cls(1, 1, 1),
1967 cls(1, 1, 1, tzinfo=None),
1968 cls(1, 1, 1, tzinfo=C1())):
1969 self.failUnless(t.utcoffset() is None)
1970 self.failUnless(t.dst() is None)
1971 self.failUnless(t.tzname() is None)
1972
Tim Peters855fe882002-12-22 03:43:39 +00001973 class C3(tzinfo):
1974 def utcoffset(self, dt): return timedelta(minutes=-1439)
1975 def dst(self, dt): return timedelta(minutes=1439)
1976 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001977 t = cls(1, 1, 1, tzinfo=C3())
1978 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
1979 self.assertEqual(t.dst(), timedelta(minutes=1439))
1980 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00001981
1982 # Wrong types.
1983 class C4(tzinfo):
1984 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001985 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00001986 def tzname(self, dt): return 0
1987 t = cls(1, 1, 1, tzinfo=C4())
1988 self.assertRaises(TypeError, t.utcoffset)
1989 self.assertRaises(TypeError, t.dst)
1990 self.assertRaises(TypeError, t.tzname)
1991
1992 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00001993 class C6(tzinfo):
1994 def utcoffset(self, dt): return timedelta(hours=-24)
1995 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00001996 t = cls(1, 1, 1, tzinfo=C6())
1997 self.assertRaises(ValueError, t.utcoffset)
1998 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00001999
2000 # Not a whole number of minutes.
2001 class C7(tzinfo):
2002 def utcoffset(self, dt): return timedelta(seconds=61)
2003 def dst(self, dt): return timedelta(microseconds=-81)
2004 t = cls(1, 1, 1, tzinfo=C7())
2005 self.assertRaises(ValueError, t.utcoffset)
2006 self.assertRaises(ValueError, t.dst)
2007
Tim Peters4c0db782002-12-26 05:01:19 +00002008 def test_aware_compare(self):
2009 cls = self.theclass
2010
Tim Peters60c76e42002-12-27 00:41:11 +00002011 # Ensure that utcoffset() gets ignored if the comparands have
2012 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002013 class OperandDependentOffset(tzinfo):
2014 def utcoffset(self, t):
2015 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002016 # d0 and d1 equal after adjustment
2017 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002018 else:
Tim Peters397301e2003-01-02 21:28:08 +00002019 # d2 off in the weeds
2020 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002021
2022 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2023 d0 = base.replace(minute=3)
2024 d1 = base.replace(minute=9)
2025 d2 = base.replace(minute=11)
2026 for x in d0, d1, d2:
2027 for y in d0, d1, d2:
2028 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002029 expected = cmp(x.minute, y.minute)
2030 self.assertEqual(got, expected)
2031
2032 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002033 # Note that a time can't actually have an operand-depedent offset,
2034 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2035 # so skip this test for time.
2036 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002037 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2038 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2039 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2040 for x in d0, d1, d2:
2041 for y in d0, d1, d2:
2042 got = cmp(x, y)
2043 if (x is d0 or x is d1) and (y is d0 or y is d1):
2044 expected = 0
2045 elif x is y is d2:
2046 expected = 0
2047 elif x is d2:
2048 expected = -1
2049 else:
2050 assert y is d2
2051 expected = 1
2052 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002053
Tim Peters855fe882002-12-22 03:43:39 +00002054
Tim Peters0bf60bd2003-01-08 20:40:01 +00002055# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002056class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002057 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002058
2059 def test_empty(self):
2060 t = self.theclass()
2061 self.assertEqual(t.hour, 0)
2062 self.assertEqual(t.minute, 0)
2063 self.assertEqual(t.second, 0)
2064 self.assertEqual(t.microsecond, 0)
2065 self.failUnless(t.tzinfo is None)
2066
Tim Peters2a799bf2002-12-16 20:18:38 +00002067 def test_zones(self):
2068 est = FixedOffset(-300, "EST", 1)
2069 utc = FixedOffset(0, "UTC", -2)
2070 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002071 t1 = time( 7, 47, tzinfo=est)
2072 t2 = time(12, 47, tzinfo=utc)
2073 t3 = time(13, 47, tzinfo=met)
2074 t4 = time(microsecond=40)
2075 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002076
2077 self.assertEqual(t1.tzinfo, est)
2078 self.assertEqual(t2.tzinfo, utc)
2079 self.assertEqual(t3.tzinfo, met)
2080 self.failUnless(t4.tzinfo is None)
2081 self.assertEqual(t5.tzinfo, utc)
2082
Tim Peters855fe882002-12-22 03:43:39 +00002083 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2084 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2085 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002086 self.failUnless(t4.utcoffset() is None)
2087 self.assertRaises(TypeError, t1.utcoffset, "no args")
2088
2089 self.assertEqual(t1.tzname(), "EST")
2090 self.assertEqual(t2.tzname(), "UTC")
2091 self.assertEqual(t3.tzname(), "MET")
2092 self.failUnless(t4.tzname() is None)
2093 self.assertRaises(TypeError, t1.tzname, "no args")
2094
Tim Peters855fe882002-12-22 03:43:39 +00002095 self.assertEqual(t1.dst(), timedelta(minutes=1))
2096 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2097 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002098 self.failUnless(t4.dst() is None)
2099 self.assertRaises(TypeError, t1.dst, "no args")
2100
2101 self.assertEqual(hash(t1), hash(t2))
2102 self.assertEqual(hash(t1), hash(t3))
2103 self.assertEqual(hash(t2), hash(t3))
2104
2105 self.assertEqual(t1, t2)
2106 self.assertEqual(t1, t3)
2107 self.assertEqual(t2, t3)
2108 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2109 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2110 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2111
2112 self.assertEqual(str(t1), "07:47:00-05:00")
2113 self.assertEqual(str(t2), "12:47:00+00:00")
2114 self.assertEqual(str(t3), "13:47:00+01:00")
2115 self.assertEqual(str(t4), "00:00:00.000040")
2116 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2117
2118 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2119 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2120 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2121 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2122 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2123
Tim Peters0bf60bd2003-01-08 20:40:01 +00002124 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002125 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2126 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2127 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2128 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2129 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2130
2131 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2132 "07:47:00 %Z=EST %z=-0500")
2133 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2134 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2135
2136 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002137 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002138 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2139 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2140
Tim Petersb92bb712002-12-21 17:44:07 +00002141 # Check that an invalid tzname result raises an exception.
2142 class Badtzname(tzinfo):
2143 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002144 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002145 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2146 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002147
2148 def test_hash_edge_cases(self):
2149 # Offsets that overflow a basic time.
2150 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2151 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2152 self.assertEqual(hash(t1), hash(t2))
2153
2154 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2155 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2156 self.assertEqual(hash(t1), hash(t2))
2157
Tim Peters2a799bf2002-12-16 20:18:38 +00002158 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002159 # Try one without a tzinfo.
2160 args = 20, 59, 16, 64**2
2161 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002162 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002163 green = pickler.dumps(orig, proto)
2164 derived = unpickler.loads(green)
2165 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002166
2167 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002168 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002169 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002170 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002171 green = pickler.dumps(orig, proto)
2172 derived = unpickler.loads(green)
2173 self.assertEqual(orig, derived)
2174 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2175 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2176 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002177
2178 def test_more_bool(self):
2179 # Test cases with non-None tzinfo.
2180 cls = self.theclass
2181
2182 t = cls(0, tzinfo=FixedOffset(-300, ""))
2183 self.failUnless(t)
2184
2185 t = cls(5, tzinfo=FixedOffset(-300, ""))
2186 self.failUnless(t)
2187
2188 t = cls(5, tzinfo=FixedOffset(300, ""))
2189 self.failUnless(not t)
2190
2191 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2192 self.failUnless(not t)
2193
2194 # Mostly ensuring this doesn't overflow internally.
2195 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2196 self.failUnless(t)
2197
2198 # But this should yield a value error -- the utcoffset is bogus.
2199 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2200 self.assertRaises(ValueError, lambda: bool(t))
2201
2202 # Likewise.
2203 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2204 self.assertRaises(ValueError, lambda: bool(t))
2205
Tim Peters12bf3392002-12-24 05:41:27 +00002206 def test_replace(self):
2207 cls = self.theclass
2208 z100 = FixedOffset(100, "+100")
2209 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2210 args = [1, 2, 3, 4, z100]
2211 base = cls(*args)
2212 self.assertEqual(base, base.replace())
2213
2214 i = 0
2215 for name, newval in (("hour", 5),
2216 ("minute", 6),
2217 ("second", 7),
2218 ("microsecond", 8),
2219 ("tzinfo", zm200)):
2220 newargs = args[:]
2221 newargs[i] = newval
2222 expected = cls(*newargs)
2223 got = base.replace(**{name: newval})
2224 self.assertEqual(expected, got)
2225 i += 1
2226
2227 # Ensure we can get rid of a tzinfo.
2228 self.assertEqual(base.tzname(), "+100")
2229 base2 = base.replace(tzinfo=None)
2230 self.failUnless(base2.tzinfo is None)
2231 self.failUnless(base2.tzname() is None)
2232
2233 # Ensure we can add one.
2234 base3 = base2.replace(tzinfo=z100)
2235 self.assertEqual(base, base3)
2236 self.failUnless(base.tzinfo is base3.tzinfo)
2237
2238 # Out of bounds.
2239 base = cls(1)
2240 self.assertRaises(ValueError, base.replace, hour=24)
2241 self.assertRaises(ValueError, base.replace, minute=-1)
2242 self.assertRaises(ValueError, base.replace, second=100)
2243 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2244
Tim Peters60c76e42002-12-27 00:41:11 +00002245 def test_mixed_compare(self):
2246 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002247 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002248 self.assertEqual(t1, t2)
2249 t2 = t2.replace(tzinfo=None)
2250 self.assertEqual(t1, t2)
2251 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2252 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002253 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2254 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002255
Tim Peters0bf60bd2003-01-08 20:40:01 +00002256 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002257 class Varies(tzinfo):
2258 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002259 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002260 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002261 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002262 return self.offset
2263
2264 v = Varies()
2265 t1 = t2.replace(tzinfo=v)
2266 t2 = t2.replace(tzinfo=v)
2267 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2268 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2269 self.assertEqual(t1, t2)
2270
2271 # But if they're not identical, it isn't ignored.
2272 t2 = t2.replace(tzinfo=Varies())
2273 self.failUnless(t1 < t2) # t1's offset counter still going up
2274
Tim Petersa98924a2003-05-17 05:55:19 +00002275 def test_subclass_timetz(self):
2276
2277 class C(self.theclass):
2278 theAnswer = 42
2279
2280 def __new__(cls, *args, **kws):
2281 temp = kws.copy()
2282 extra = temp.pop('extra')
2283 result = self.theclass.__new__(cls, *args, **temp)
2284 result.extra = extra
2285 return result
2286
2287 def newmeth(self, start):
2288 return start + self.hour + self.second
2289
2290 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2291
2292 dt1 = self.theclass(*args)
2293 dt2 = C(*args, **{'extra': 7})
2294
2295 self.assertEqual(dt2.__class__, C)
2296 self.assertEqual(dt2.theAnswer, 42)
2297 self.assertEqual(dt2.extra, 7)
2298 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2299 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2300
Tim Peters4c0db782002-12-26 05:01:19 +00002301
Tim Peters0bf60bd2003-01-08 20:40:01 +00002302# Testing datetime objects with a non-None tzinfo.
2303
Guido van Rossumd8faa362007-04-27 19:54:29 +00002304class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002305 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002306
2307 def test_trivial(self):
2308 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2309 self.assertEqual(dt.year, 1)
2310 self.assertEqual(dt.month, 2)
2311 self.assertEqual(dt.day, 3)
2312 self.assertEqual(dt.hour, 4)
2313 self.assertEqual(dt.minute, 5)
2314 self.assertEqual(dt.second, 6)
2315 self.assertEqual(dt.microsecond, 7)
2316 self.assertEqual(dt.tzinfo, None)
2317
2318 def test_even_more_compare(self):
2319 # The test_compare() and test_more_compare() inherited from TestDate
2320 # and TestDateTime covered non-tzinfo cases.
2321
2322 # Smallest possible after UTC adjustment.
2323 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2324 # Largest possible after UTC adjustment.
2325 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2326 tzinfo=FixedOffset(-1439, ""))
2327
2328 # Make sure those compare correctly, and w/o overflow.
2329 self.failUnless(t1 < t2)
2330 self.failUnless(t1 != t2)
2331 self.failUnless(t2 > t1)
2332
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002333 self.assertEqual(t1, t1)
2334 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002335
2336 # Equal afer adjustment.
2337 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2338 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2339 self.assertEqual(t1, t2)
2340
2341 # Change t1 not to subtract a minute, and t1 should be larger.
2342 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2343 self.failUnless(t1 > t2)
2344
2345 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2346 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2347 self.failUnless(t1 < t2)
2348
2349 # Back to the original t1, but make seconds resolve it.
2350 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2351 second=1)
2352 self.failUnless(t1 > t2)
2353
2354 # Likewise, but make microseconds resolve it.
2355 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2356 microsecond=1)
2357 self.failUnless(t1 > t2)
2358
2359 # Make t2 naive and it should fail.
2360 t2 = self.theclass.min
2361 self.assertRaises(TypeError, lambda: t1 == t2)
2362 self.assertEqual(t2, t2)
2363
2364 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2365 class Naive(tzinfo):
2366 def utcoffset(self, dt): return None
2367 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2368 self.assertRaises(TypeError, lambda: t1 == t2)
2369 self.assertEqual(t2, t2)
2370
2371 # OTOH, it's OK to compare two of these mixing the two ways of being
2372 # naive.
2373 t1 = self.theclass(5, 6, 7)
2374 self.assertEqual(t1, t2)
2375
2376 # Try a bogus uctoffset.
2377 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002378 def utcoffset(self, dt):
2379 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002380 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2381 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002382 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002383
Tim Peters2a799bf2002-12-16 20:18:38 +00002384 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002385 # Try one without a tzinfo.
2386 args = 6, 7, 23, 20, 59, 1, 64**2
2387 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002388 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002389 green = pickler.dumps(orig, proto)
2390 derived = unpickler.loads(green)
2391 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002392
2393 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002394 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002395 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002396 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002397 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002398 green = pickler.dumps(orig, proto)
2399 derived = unpickler.loads(green)
2400 self.assertEqual(orig, derived)
2401 self.failUnless(isinstance(derived.tzinfo,
2402 PicklableFixedOffset))
2403 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2404 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002405
2406 def test_extreme_hashes(self):
2407 # If an attempt is made to hash these via subtracting the offset
2408 # then hashing a datetime object, OverflowError results. The
2409 # Python implementation used to blow up here.
2410 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2411 hash(t)
2412 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2413 tzinfo=FixedOffset(-1439, ""))
2414 hash(t)
2415
2416 # OTOH, an OOB offset should blow up.
2417 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2418 self.assertRaises(ValueError, hash, t)
2419
2420 def test_zones(self):
2421 est = FixedOffset(-300, "EST")
2422 utc = FixedOffset(0, "UTC")
2423 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002424 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2425 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2426 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002427 self.assertEqual(t1.tzinfo, est)
2428 self.assertEqual(t2.tzinfo, utc)
2429 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002430 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2431 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2432 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002433 self.assertEqual(t1.tzname(), "EST")
2434 self.assertEqual(t2.tzname(), "UTC")
2435 self.assertEqual(t3.tzname(), "MET")
2436 self.assertEqual(hash(t1), hash(t2))
2437 self.assertEqual(hash(t1), hash(t3))
2438 self.assertEqual(hash(t2), hash(t3))
2439 self.assertEqual(t1, t2)
2440 self.assertEqual(t1, t3)
2441 self.assertEqual(t2, t3)
2442 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2443 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2444 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002445 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002446 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2447 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2448 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2449
2450 def test_combine(self):
2451 met = FixedOffset(60, "MET")
2452 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002453 tz = time(18, 45, 3, 1234, tzinfo=met)
2454 dt = datetime.combine(d, tz)
2455 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002456 tzinfo=met))
2457
2458 def test_extract(self):
2459 met = FixedOffset(60, "MET")
2460 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2461 self.assertEqual(dt.date(), date(2002, 3, 4))
2462 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002463 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002464
2465 def test_tz_aware_arithmetic(self):
2466 import random
2467
2468 now = self.theclass.now()
2469 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002470 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002471 nowaware = self.theclass.combine(now.date(), timeaware)
2472 self.failUnless(nowaware.tzinfo is tz55)
2473 self.assertEqual(nowaware.timetz(), timeaware)
2474
2475 # Can't mix aware and non-aware.
2476 self.assertRaises(TypeError, lambda: now - nowaware)
2477 self.assertRaises(TypeError, lambda: nowaware - now)
2478
Tim Peters0bf60bd2003-01-08 20:40:01 +00002479 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002480 self.assertRaises(TypeError, lambda: now + nowaware)
2481 self.assertRaises(TypeError, lambda: nowaware + now)
2482 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2483
2484 # Subtracting should yield 0.
2485 self.assertEqual(now - now, timedelta(0))
2486 self.assertEqual(nowaware - nowaware, timedelta(0))
2487
2488 # Adding a delta should preserve tzinfo.
2489 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2490 nowawareplus = nowaware + delta
2491 self.failUnless(nowaware.tzinfo is tz55)
2492 nowawareplus2 = delta + nowaware
2493 self.failUnless(nowawareplus2.tzinfo is tz55)
2494 self.assertEqual(nowawareplus, nowawareplus2)
2495
2496 # that - delta should be what we started with, and that - what we
2497 # started with should be delta.
2498 diff = nowawareplus - delta
2499 self.failUnless(diff.tzinfo is tz55)
2500 self.assertEqual(nowaware, diff)
2501 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2502 self.assertEqual(nowawareplus - nowaware, delta)
2503
2504 # Make up a random timezone.
2505 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002506 # Attach it to nowawareplus.
2507 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002508 self.failUnless(nowawareplus.tzinfo is tzr)
2509 # Make sure the difference takes the timezone adjustments into account.
2510 got = nowaware - nowawareplus
2511 # Expected: (nowaware base - nowaware offset) -
2512 # (nowawareplus base - nowawareplus offset) =
2513 # (nowaware base - nowawareplus base) +
2514 # (nowawareplus offset - nowaware offset) =
2515 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002516 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002517 self.assertEqual(got, expected)
2518
2519 # Try max possible difference.
2520 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2521 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2522 tzinfo=FixedOffset(-1439, "max"))
2523 maxdiff = max - min
2524 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2525 timedelta(minutes=2*1439))
2526
2527 def test_tzinfo_now(self):
2528 meth = self.theclass.now
2529 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2530 base = meth()
2531 # Try with and without naming the keyword.
2532 off42 = FixedOffset(42, "42")
2533 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002534 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002535 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002536 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002537 # Bad argument with and w/o naming the keyword.
2538 self.assertRaises(TypeError, meth, 16)
2539 self.assertRaises(TypeError, meth, tzinfo=16)
2540 # Bad keyword name.
2541 self.assertRaises(TypeError, meth, tinfo=off42)
2542 # Too many args.
2543 self.assertRaises(TypeError, meth, off42, off42)
2544
Tim Peters10cadce2003-01-23 19:58:02 +00002545 # We don't know which time zone we're in, and don't have a tzinfo
2546 # class to represent it, so seeing whether a tz argument actually
2547 # does a conversion is tricky.
2548 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2549 utc = FixedOffset(0, "utc", 0)
2550 for dummy in range(3):
2551 now = datetime.now(weirdtz)
2552 self.failUnless(now.tzinfo is weirdtz)
2553 utcnow = datetime.utcnow().replace(tzinfo=utc)
2554 now2 = utcnow.astimezone(weirdtz)
2555 if abs(now - now2) < timedelta(seconds=30):
2556 break
2557 # Else the code is broken, or more than 30 seconds passed between
2558 # calls; assuming the latter, just try again.
2559 else:
2560 # Three strikes and we're out.
2561 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2562
Tim Peters2a799bf2002-12-16 20:18:38 +00002563 def test_tzinfo_fromtimestamp(self):
2564 import time
2565 meth = self.theclass.fromtimestamp
2566 ts = time.time()
2567 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2568 base = meth(ts)
2569 # Try with and without naming the keyword.
2570 off42 = FixedOffset(42, "42")
2571 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002572 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002573 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002574 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002575 # Bad argument with and w/o naming the keyword.
2576 self.assertRaises(TypeError, meth, ts, 16)
2577 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2578 # Bad keyword name.
2579 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2580 # Too many args.
2581 self.assertRaises(TypeError, meth, ts, off42, off42)
2582 # Too few args.
2583 self.assertRaises(TypeError, meth)
2584
Tim Peters2a44a8d2003-01-23 20:53:10 +00002585 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002586 timestamp = 1000000000
2587 utcdatetime = datetime.utcfromtimestamp(timestamp)
2588 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2589 # But on some flavor of Mac, it's nowhere near that. So we can't have
2590 # any idea here what time that actually is, we can only test that
2591 # relative changes match.
2592 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2593 tz = FixedOffset(utcoffset, "tz", 0)
2594 expected = utcdatetime + utcoffset
2595 got = datetime.fromtimestamp(timestamp, tz)
2596 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002597
Tim Peters2a799bf2002-12-16 20:18:38 +00002598 def test_tzinfo_utcnow(self):
2599 meth = self.theclass.utcnow
2600 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2601 base = meth()
2602 # Try with and without naming the keyword; for whatever reason,
2603 # utcnow() doesn't accept a tzinfo argument.
2604 off42 = FixedOffset(42, "42")
2605 self.assertRaises(TypeError, meth, off42)
2606 self.assertRaises(TypeError, meth, tzinfo=off42)
2607
2608 def test_tzinfo_utcfromtimestamp(self):
2609 import time
2610 meth = self.theclass.utcfromtimestamp
2611 ts = time.time()
2612 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2613 base = meth(ts)
2614 # Try with and without naming the keyword; for whatever reason,
2615 # utcfromtimestamp() doesn't accept a tzinfo argument.
2616 off42 = FixedOffset(42, "42")
2617 self.assertRaises(TypeError, meth, ts, off42)
2618 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2619
2620 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002621 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002622 # DST flag.
2623 class DST(tzinfo):
2624 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002625 if isinstance(dstvalue, int):
2626 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002627 self.dstvalue = dstvalue
2628 def dst(self, dt):
2629 return self.dstvalue
2630
2631 cls = self.theclass
2632 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2633 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2634 t = d.timetuple()
2635 self.assertEqual(1, t.tm_year)
2636 self.assertEqual(1, t.tm_mon)
2637 self.assertEqual(1, t.tm_mday)
2638 self.assertEqual(10, t.tm_hour)
2639 self.assertEqual(20, t.tm_min)
2640 self.assertEqual(30, t.tm_sec)
2641 self.assertEqual(0, t.tm_wday)
2642 self.assertEqual(1, t.tm_yday)
2643 self.assertEqual(flag, t.tm_isdst)
2644
2645 # dst() returns wrong type.
2646 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2647
2648 # dst() at the edge.
2649 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2650 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2651
2652 # dst() out of range.
2653 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2654 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2655
2656 def test_utctimetuple(self):
2657 class DST(tzinfo):
2658 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002659 if isinstance(dstvalue, int):
2660 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002661 self.dstvalue = dstvalue
2662 def dst(self, dt):
2663 return self.dstvalue
2664
2665 cls = self.theclass
2666 # This can't work: DST didn't implement utcoffset.
2667 self.assertRaises(NotImplementedError,
2668 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2669
2670 class UOFS(DST):
2671 def __init__(self, uofs, dofs=None):
2672 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002673 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002674 def utcoffset(self, dt):
2675 return self.uofs
2676
2677 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2678 # in effect for a UTC time.
2679 for dstvalue in -33, 33, 0, None:
2680 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2681 t = d.utctimetuple()
2682 self.assertEqual(d.year, t.tm_year)
2683 self.assertEqual(d.month, t.tm_mon)
2684 self.assertEqual(d.day, t.tm_mday)
2685 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2686 self.assertEqual(13, t.tm_min)
2687 self.assertEqual(d.second, t.tm_sec)
2688 self.assertEqual(d.weekday(), t.tm_wday)
2689 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2690 t.tm_yday)
2691 self.assertEqual(0, t.tm_isdst)
2692
2693 # At the edges, UTC adjustment can normalize into years out-of-range
2694 # for a datetime object. Ensure that a correct timetuple is
2695 # created anyway.
2696 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2697 # That goes back 1 minute less than a full day.
2698 t = tiny.utctimetuple()
2699 self.assertEqual(t.tm_year, MINYEAR-1)
2700 self.assertEqual(t.tm_mon, 12)
2701 self.assertEqual(t.tm_mday, 31)
2702 self.assertEqual(t.tm_hour, 0)
2703 self.assertEqual(t.tm_min, 1)
2704 self.assertEqual(t.tm_sec, 37)
2705 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2706 self.assertEqual(t.tm_isdst, 0)
2707
2708 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2709 # That goes forward 1 minute less than a full day.
2710 t = huge.utctimetuple()
2711 self.assertEqual(t.tm_year, MAXYEAR+1)
2712 self.assertEqual(t.tm_mon, 1)
2713 self.assertEqual(t.tm_mday, 1)
2714 self.assertEqual(t.tm_hour, 23)
2715 self.assertEqual(t.tm_min, 58)
2716 self.assertEqual(t.tm_sec, 37)
2717 self.assertEqual(t.tm_yday, 1)
2718 self.assertEqual(t.tm_isdst, 0)
2719
2720 def test_tzinfo_isoformat(self):
2721 zero = FixedOffset(0, "+00:00")
2722 plus = FixedOffset(220, "+03:40")
2723 minus = FixedOffset(-231, "-03:51")
2724 unknown = FixedOffset(None, "")
2725
2726 cls = self.theclass
2727 datestr = '0001-02-03'
2728 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002729 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002730 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2731 timestr = '04:05:59' + (us and '.987001' or '')
2732 ofsstr = ofs is not None and d.tzname() or ''
2733 tailstr = timestr + ofsstr
2734 iso = d.isoformat()
2735 self.assertEqual(iso, datestr + 'T' + tailstr)
2736 self.assertEqual(iso, d.isoformat('T'))
2737 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002738 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002739 self.assertEqual(str(d), datestr + ' ' + tailstr)
2740
Tim Peters12bf3392002-12-24 05:41:27 +00002741 def test_replace(self):
2742 cls = self.theclass
2743 z100 = FixedOffset(100, "+100")
2744 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2745 args = [1, 2, 3, 4, 5, 6, 7, z100]
2746 base = cls(*args)
2747 self.assertEqual(base, base.replace())
2748
2749 i = 0
2750 for name, newval in (("year", 2),
2751 ("month", 3),
2752 ("day", 4),
2753 ("hour", 5),
2754 ("minute", 6),
2755 ("second", 7),
2756 ("microsecond", 8),
2757 ("tzinfo", zm200)):
2758 newargs = args[:]
2759 newargs[i] = newval
2760 expected = cls(*newargs)
2761 got = base.replace(**{name: newval})
2762 self.assertEqual(expected, got)
2763 i += 1
2764
2765 # Ensure we can get rid of a tzinfo.
2766 self.assertEqual(base.tzname(), "+100")
2767 base2 = base.replace(tzinfo=None)
2768 self.failUnless(base2.tzinfo is None)
2769 self.failUnless(base2.tzname() is None)
2770
2771 # Ensure we can add one.
2772 base3 = base2.replace(tzinfo=z100)
2773 self.assertEqual(base, base3)
2774 self.failUnless(base.tzinfo is base3.tzinfo)
2775
2776 # Out of bounds.
2777 base = cls(2000, 2, 29)
2778 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002779
Tim Peters80475bb2002-12-25 07:40:55 +00002780 def test_more_astimezone(self):
2781 # The inherited test_astimezone covered some trivial and error cases.
2782 fnone = FixedOffset(None, "None")
2783 f44m = FixedOffset(44, "44")
2784 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2785
Tim Peters10cadce2003-01-23 19:58:02 +00002786 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002787 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002788 # Replacing with degenerate tzinfo raises an exception.
2789 self.assertRaises(ValueError, dt.astimezone, fnone)
2790 # Ditto with None tz.
2791 self.assertRaises(TypeError, dt.astimezone, None)
2792 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002793 x = dt.astimezone(dt.tzinfo)
2794 self.failUnless(x.tzinfo is f44m)
2795 self.assertEqual(x.date(), dt.date())
2796 self.assertEqual(x.time(), dt.time())
2797
2798 # Replacing with different tzinfo does adjust.
2799 got = dt.astimezone(fm5h)
2800 self.failUnless(got.tzinfo is fm5h)
2801 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2802 expected = dt - dt.utcoffset() # in effect, convert to UTC
2803 expected += fm5h.utcoffset(dt) # and from there to local time
2804 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2805 self.assertEqual(got.date(), expected.date())
2806 self.assertEqual(got.time(), expected.time())
2807 self.assertEqual(got.timetz(), expected.timetz())
2808 self.failUnless(got.tzinfo is expected.tzinfo)
2809 self.assertEqual(got, expected)
2810
Tim Peters4c0db782002-12-26 05:01:19 +00002811 def test_aware_subtract(self):
2812 cls = self.theclass
2813
Tim Peters60c76e42002-12-27 00:41:11 +00002814 # Ensure that utcoffset() is ignored when the operands have the
2815 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002816 class OperandDependentOffset(tzinfo):
2817 def utcoffset(self, t):
2818 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002819 # d0 and d1 equal after adjustment
2820 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002821 else:
Tim Peters397301e2003-01-02 21:28:08 +00002822 # d2 off in the weeds
2823 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002824
2825 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2826 d0 = base.replace(minute=3)
2827 d1 = base.replace(minute=9)
2828 d2 = base.replace(minute=11)
2829 for x in d0, d1, d2:
2830 for y in d0, d1, d2:
2831 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002832 expected = timedelta(minutes=x.minute - y.minute)
2833 self.assertEqual(got, expected)
2834
2835 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2836 # ignored.
2837 base = cls(8, 9, 10, 11, 12, 13, 14)
2838 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2839 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2840 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2841 for x in d0, d1, d2:
2842 for y in d0, d1, d2:
2843 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002844 if (x is d0 or x is d1) and (y is d0 or y is d1):
2845 expected = timedelta(0)
2846 elif x is y is d2:
2847 expected = timedelta(0)
2848 elif x is d2:
2849 expected = timedelta(minutes=(11-59)-0)
2850 else:
2851 assert y is d2
2852 expected = timedelta(minutes=0-(11-59))
2853 self.assertEqual(got, expected)
2854
Tim Peters60c76e42002-12-27 00:41:11 +00002855 def test_mixed_compare(self):
2856 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002857 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002858 self.assertEqual(t1, t2)
2859 t2 = t2.replace(tzinfo=None)
2860 self.assertEqual(t1, t2)
2861 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2862 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002863 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2864 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002865
Tim Peters0bf60bd2003-01-08 20:40:01 +00002866 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002867 class Varies(tzinfo):
2868 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002869 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002870 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002871 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002872 return self.offset
2873
2874 v = Varies()
2875 t1 = t2.replace(tzinfo=v)
2876 t2 = t2.replace(tzinfo=v)
2877 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2878 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2879 self.assertEqual(t1, t2)
2880
2881 # But if they're not identical, it isn't ignored.
2882 t2 = t2.replace(tzinfo=Varies())
2883 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002884
Tim Petersa98924a2003-05-17 05:55:19 +00002885 def test_subclass_datetimetz(self):
2886
2887 class C(self.theclass):
2888 theAnswer = 42
2889
2890 def __new__(cls, *args, **kws):
2891 temp = kws.copy()
2892 extra = temp.pop('extra')
2893 result = self.theclass.__new__(cls, *args, **temp)
2894 result.extra = extra
2895 return result
2896
2897 def newmeth(self, start):
2898 return start + self.hour + self.year
2899
2900 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2901
2902 dt1 = self.theclass(*args)
2903 dt2 = C(*args, **{'extra': 7})
2904
2905 self.assertEqual(dt2.__class__, C)
2906 self.assertEqual(dt2.theAnswer, 42)
2907 self.assertEqual(dt2.extra, 7)
2908 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2909 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2910
Tim Peters621818b2002-12-29 23:44:49 +00002911# Pain to set up DST-aware tzinfo classes.
2912
2913def first_sunday_on_or_after(dt):
2914 days_to_go = 6 - dt.weekday()
2915 if days_to_go:
2916 dt += timedelta(days_to_go)
2917 return dt
2918
2919ZERO = timedelta(0)
2920HOUR = timedelta(hours=1)
2921DAY = timedelta(days=1)
2922# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2923DSTSTART = datetime(1, 4, 1, 2)
2924# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002925# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2926# being standard time on that day, there is no spelling in local time of
2927# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2928DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002929
2930class USTimeZone(tzinfo):
2931
2932 def __init__(self, hours, reprname, stdname, dstname):
2933 self.stdoffset = timedelta(hours=hours)
2934 self.reprname = reprname
2935 self.stdname = stdname
2936 self.dstname = dstname
2937
2938 def __repr__(self):
2939 return self.reprname
2940
2941 def tzname(self, dt):
2942 if self.dst(dt):
2943 return self.dstname
2944 else:
2945 return self.stdname
2946
2947 def utcoffset(self, dt):
2948 return self.stdoffset + self.dst(dt)
2949
2950 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00002951 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00002952 # An exception instead may be sensible here, in one or more of
2953 # the cases.
2954 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00002955 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00002956
2957 # Find first Sunday in April.
2958 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
2959 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
2960
2961 # Find last Sunday in October.
2962 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
2963 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
2964
Tim Peters621818b2002-12-29 23:44:49 +00002965 # Can't compare naive to aware objects, so strip the timezone from
2966 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00002967 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00002968 return HOUR
2969 else:
2970 return ZERO
2971
Tim Peters521fc152002-12-31 17:36:56 +00002972Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
2973Central = USTimeZone(-6, "Central", "CST", "CDT")
2974Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
2975Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00002976utc_real = FixedOffset(0, "UTC", 0)
2977# For better test coverage, we want another flavor of UTC that's west of
2978# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00002979utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00002980
2981class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00002982 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002983 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00002984 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002985
Tim Peters0bf60bd2003-01-08 20:40:01 +00002986 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00002987
Tim Peters521fc152002-12-31 17:36:56 +00002988 # Check a time that's inside DST.
2989 def checkinside(self, dt, tz, utc, dston, dstoff):
2990 self.assertEqual(dt.dst(), HOUR)
2991
2992 # Conversion to our own timezone is always an identity.
2993 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00002994
2995 asutc = dt.astimezone(utc)
2996 there_and_back = asutc.astimezone(tz)
2997
2998 # Conversion to UTC and back isn't always an identity here,
2999 # because there are redundant spellings (in local time) of
3000 # UTC time when DST begins: the clock jumps from 1:59:59
3001 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3002 # make sense then. The classes above treat 2:MM:SS as
3003 # daylight time then (it's "after 2am"), really an alias
3004 # for 1:MM:SS standard time. The latter form is what
3005 # conversion back from UTC produces.
3006 if dt.date() == dston.date() and dt.hour == 2:
3007 # We're in the redundant hour, and coming back from
3008 # UTC gives the 1:MM:SS standard-time spelling.
3009 self.assertEqual(there_and_back + HOUR, dt)
3010 # Although during was considered to be in daylight
3011 # time, there_and_back is not.
3012 self.assertEqual(there_and_back.dst(), ZERO)
3013 # They're the same times in UTC.
3014 self.assertEqual(there_and_back.astimezone(utc),
3015 dt.astimezone(utc))
3016 else:
3017 # We're not in the redundant hour.
3018 self.assertEqual(dt, there_and_back)
3019
Tim Peters327098a2003-01-20 22:54:38 +00003020 # Because we have a redundant spelling when DST begins, there is
3021 # (unforunately) an hour when DST ends that can't be spelled at all in
3022 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3023 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3024 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3025 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3026 # expressed in local time. Nevertheless, we want conversion back
3027 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003028 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003029 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003030 if dt.date() == dstoff.date() and dt.hour == 0:
3031 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003032 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003033 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3034 nexthour_utc += HOUR
3035 nexthour_tz = nexthour_utc.astimezone(tz)
3036 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003037 else:
Tim Peters327098a2003-01-20 22:54:38 +00003038 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003039
3040 # Check a time that's outside DST.
3041 def checkoutside(self, dt, tz, utc):
3042 self.assertEqual(dt.dst(), ZERO)
3043
3044 # Conversion to our own timezone is always an identity.
3045 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003046
3047 # Converting to UTC and back is an identity too.
3048 asutc = dt.astimezone(utc)
3049 there_and_back = asutc.astimezone(tz)
3050 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003051
Tim Peters1024bf82002-12-30 17:09:40 +00003052 def convert_between_tz_and_utc(self, tz, utc):
3053 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003054 # Because 1:MM on the day DST ends is taken as being standard time,
3055 # there is no spelling in tz for the last hour of daylight time.
3056 # For purposes of the test, the last hour of DST is 0:MM, which is
3057 # taken as being daylight time (and 1:MM is taken as being standard
3058 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003059 dstoff = self.dstoff.replace(tzinfo=tz)
3060 for delta in (timedelta(weeks=13),
3061 DAY,
3062 HOUR,
3063 timedelta(minutes=1),
3064 timedelta(microseconds=1)):
3065
Tim Peters521fc152002-12-31 17:36:56 +00003066 self.checkinside(dston, tz, utc, dston, dstoff)
3067 for during in dston + delta, dstoff - delta:
3068 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003069
Tim Peters521fc152002-12-31 17:36:56 +00003070 self.checkoutside(dstoff, tz, utc)
3071 for outside in dston - delta, dstoff + delta:
3072 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003073
Tim Peters621818b2002-12-29 23:44:49 +00003074 def test_easy(self):
3075 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003076 self.convert_between_tz_and_utc(Eastern, utc_real)
3077 self.convert_between_tz_and_utc(Pacific, utc_real)
3078 self.convert_between_tz_and_utc(Eastern, utc_fake)
3079 self.convert_between_tz_and_utc(Pacific, utc_fake)
3080 # The next is really dancing near the edge. It works because
3081 # Pacific and Eastern are far enough apart that their "problem
3082 # hours" don't overlap.
3083 self.convert_between_tz_and_utc(Eastern, Pacific)
3084 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003085 # OTOH, these fail! Don't enable them. The difficulty is that
3086 # the edge case tests assume that every hour is representable in
3087 # the "utc" class. This is always true for a fixed-offset tzinfo
3088 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3089 # For these adjacent DST-aware time zones, the range of time offsets
3090 # tested ends up creating hours in the one that aren't representable
3091 # in the other. For the same reason, we would see failures in the
3092 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3093 # offset deltas in convert_between_tz_and_utc().
3094 #
3095 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3096 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003097
Tim Petersf3615152003-01-01 21:51:37 +00003098 def test_tricky(self):
3099 # 22:00 on day before daylight starts.
3100 fourback = self.dston - timedelta(hours=4)
3101 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003102 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003103 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3104 # 2", we should get the 3 spelling.
3105 # If we plug 22:00 the day before into Eastern, it "looks like std
3106 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3107 # to 22:00 lands on 2:00, which makes no sense in local time (the
3108 # local clock jumps from 1 to 3). The point here is to make sure we
3109 # get the 3 spelling.
3110 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003111 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003112 self.assertEqual(expected, got)
3113
3114 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3115 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003116 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003117 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3118 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3119 # spelling.
3120 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003121 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003122 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003123
Tim Petersadf64202003-01-04 06:03:15 +00003124 # Now on the day DST ends, we want "repeat an hour" behavior.
3125 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3126 # EST 23:MM 0:MM 1:MM 2:MM
3127 # EDT 0:MM 1:MM 2:MM 3:MM
3128 # wall 0:MM 1:MM 1:MM 2:MM against these
3129 for utc in utc_real, utc_fake:
3130 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003131 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003132 # Convert that to UTC.
3133 first_std_hour -= tz.utcoffset(None)
3134 # Adjust for possibly fake UTC.
3135 asutc = first_std_hour + utc.utcoffset(None)
3136 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3137 # tz=Eastern.
3138 asutcbase = asutc.replace(tzinfo=utc)
3139 for tzhour in (0, 1, 1, 2):
3140 expectedbase = self.dstoff.replace(hour=tzhour)
3141 for minute in 0, 30, 59:
3142 expected = expectedbase.replace(minute=minute)
3143 asutc = asutcbase.replace(minute=minute)
3144 astz = asutc.astimezone(tz)
3145 self.assertEqual(astz.replace(tzinfo=None), expected)
3146 asutcbase += HOUR
3147
3148
Tim Peters710fb152003-01-02 19:35:54 +00003149 def test_bogus_dst(self):
3150 class ok(tzinfo):
3151 def utcoffset(self, dt): return HOUR
3152 def dst(self, dt): return HOUR
3153
3154 now = self.theclass.now().replace(tzinfo=utc_real)
3155 # Doesn't blow up.
3156 now.astimezone(ok())
3157
3158 # Does blow up.
3159 class notok(ok):
3160 def dst(self, dt): return None
3161 self.assertRaises(ValueError, now.astimezone, notok())
3162
Tim Peters52dcce22003-01-23 16:36:11 +00003163 def test_fromutc(self):
3164 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3165 now = datetime.utcnow().replace(tzinfo=utc_real)
3166 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3167 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3168 enow = Eastern.fromutc(now) # doesn't blow up
3169 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3170 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3171 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3172
3173 # Always converts UTC to standard time.
3174 class FauxUSTimeZone(USTimeZone):
3175 def fromutc(self, dt):
3176 return dt + self.stdoffset
3177 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3178
3179 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3180 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3181 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3182
3183 # Check around DST start.
3184 start = self.dston.replace(hour=4, tzinfo=Eastern)
3185 fstart = start.replace(tzinfo=FEastern)
3186 for wall in 23, 0, 1, 3, 4, 5:
3187 expected = start.replace(hour=wall)
3188 if wall == 23:
3189 expected -= timedelta(days=1)
3190 got = Eastern.fromutc(start)
3191 self.assertEqual(expected, got)
3192
3193 expected = fstart + FEastern.stdoffset
3194 got = FEastern.fromutc(fstart)
3195 self.assertEqual(expected, got)
3196
3197 # Ensure astimezone() calls fromutc() too.
3198 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3199 self.assertEqual(expected, got)
3200
3201 start += HOUR
3202 fstart += HOUR
3203
3204 # Check around DST end.
3205 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3206 fstart = start.replace(tzinfo=FEastern)
3207 for wall in 0, 1, 1, 2, 3, 4:
3208 expected = start.replace(hour=wall)
3209 got = Eastern.fromutc(start)
3210 self.assertEqual(expected, got)
3211
3212 expected = fstart + FEastern.stdoffset
3213 got = FEastern.fromutc(fstart)
3214 self.assertEqual(expected, got)
3215
3216 # Ensure astimezone() calls fromutc() too.
3217 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3218 self.assertEqual(expected, got)
3219
3220 start += HOUR
3221 fstart += HOUR
3222
Tim Peters710fb152003-01-02 19:35:54 +00003223
Tim Peters528ca532004-09-16 01:30:50 +00003224#############################################################################
3225# oddballs
3226
3227class Oddballs(unittest.TestCase):
3228
3229 def test_bug_1028306(self):
3230 # Trying to compare a date to a datetime should act like a mixed-
3231 # type comparison, despite that datetime is a subclass of date.
3232 as_date = date.today()
3233 as_datetime = datetime.combine(as_date, time())
3234 self.assert_(as_date != as_datetime)
3235 self.assert_(as_datetime != as_date)
3236 self.assert_(not as_date == as_datetime)
3237 self.assert_(not as_datetime == as_date)
3238 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3239 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3240 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3241 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3242 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3243 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3244 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3245 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3246
3247 # Neverthelss, comparison should work with the base-class (date)
3248 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003249 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003250 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003251 as_different = as_datetime.replace(day= different_day)
3252 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003253
3254 # And date should compare with other subclasses of date. If a
3255 # subclass wants to stop this, it's up to the subclass to do so.
3256 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3257 self.assertEqual(as_date, date_sc)
3258 self.assertEqual(date_sc, as_date)
3259
3260 # Ditto for datetimes.
3261 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3262 as_date.day, 0, 0, 0)
3263 self.assertEqual(as_datetime, datetime_sc)
3264 self.assertEqual(datetime_sc, as_datetime)
3265
Tim Peters2a799bf2002-12-16 20:18:38 +00003266def test_main():
Guido van Rossumd8faa362007-04-27 19:54:29 +00003267 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003268
3269if __name__ == "__main__":
3270 test_main()