blob: 3a0b7af5f83d74c61b1e568f0ac329b845b6e1c0 [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
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000010try:
11 import cPickle
12except ImportError:
13 cPickle = None
Tim Peters2a799bf2002-12-16 20:18:38 +000014
15from test import test_support
16
17from datetime import MINYEAR, MAXYEAR
18from datetime import timedelta
19from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000020from datetime import time
21from datetime import date, datetime
22
Tim Peters35ad6412003-02-05 04:08:07 +000023pickle_choices = [(pickler, unpickler, proto)
Nick Coghlan650f0d02007-04-15 12:05:43 +000024 for pickler in (pickle, cPickle)
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000025 if pickler is not None
Nick Coghlan650f0d02007-04-15 12:05:43 +000026 for unpickler in (pickle, cPickle)
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000027 if unpickler is not None
Tim Peters35ad6412003-02-05 04:08:07 +000028 for proto in range(3)]
Guido van Rossumbf12cdb2006-08-17 20:24:18 +000029if cPickle is None:
30 assert len(pickle_choices) == 3
31else:
32 assert len(pickle_choices) == 2*2*3
Guido van Rossum177e41a2003-01-30 22:06:23 +000033
Tim Peters68124bb2003-02-08 03:46:31 +000034# An arbitrary collection of objects of non-datetime types, for testing
35# mixed-type comparisons.
Guido van Rossume2a383d2007-01-15 16:59:06 +000036OTHERSTUFF = (10, 10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000037
Tim Peters2a799bf2002-12-16 20:18:38 +000038
39#############################################################################
40# module tests
41
42class TestModule(unittest.TestCase):
43
44 def test_constants(self):
45 import datetime
46 self.assertEqual(datetime.MINYEAR, 1)
47 self.assertEqual(datetime.MAXYEAR, 9999)
48
49#############################################################################
50# tzinfo tests
51
52class FixedOffset(tzinfo):
53 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000054 if isinstance(offset, int):
55 offset = timedelta(minutes=offset)
56 if isinstance(dstoffset, int):
57 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000058 self.__offset = offset
59 self.__name = name
60 self.__dstoffset = dstoffset
61 def __repr__(self):
62 return self.__name.lower()
63 def utcoffset(self, dt):
64 return self.__offset
65 def tzname(self, dt):
66 return self.__name
67 def dst(self, dt):
68 return self.__dstoffset
69
Tim Petersfb8472c2002-12-21 05:04:42 +000070class PicklableFixedOffset(FixedOffset):
71 def __init__(self, offset=None, name=None, dstoffset=None):
72 FixedOffset.__init__(self, offset, name, dstoffset)
73
Tim Peters2a799bf2002-12-16 20:18:38 +000074class TestTZInfo(unittest.TestCase):
75
76 def test_non_abstractness(self):
77 # In order to allow subclasses to get pickled, the C implementation
78 # wasn't able to get away with having __init__ raise
79 # NotImplementedError.
80 useless = tzinfo()
81 dt = datetime.max
82 self.assertRaises(NotImplementedError, useless.tzname, dt)
83 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
84 self.assertRaises(NotImplementedError, useless.dst, dt)
85
86 def test_subclass_must_override(self):
87 class NotEnough(tzinfo):
88 def __init__(self, offset, name):
89 self.__offset = offset
90 self.__name = name
91 self.failUnless(issubclass(NotEnough, tzinfo))
92 ne = NotEnough(3, "NotByALongShot")
93 self.failUnless(isinstance(ne, tzinfo))
94
95 dt = datetime.now()
96 self.assertRaises(NotImplementedError, ne.tzname, dt)
97 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
98 self.assertRaises(NotImplementedError, ne.dst, dt)
99
100 def test_normal(self):
101 fo = FixedOffset(3, "Three")
102 self.failUnless(isinstance(fo, tzinfo))
103 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000104 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000105 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000106 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000107
108 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000109 # There's no point to pickling tzinfo objects on their own (they
110 # carry no data), but they need to be picklable anyway else
111 # concrete subclasses can't be pickled.
112 orig = tzinfo.__new__(tzinfo)
113 self.failUnless(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000114 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000115 green = pickler.dumps(orig, proto)
116 derived = unpickler.loads(green)
117 self.failUnless(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000118
119 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000120 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000121 offset = timedelta(minutes=-300)
122 orig = PicklableFixedOffset(offset, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000123 self.failUnless(isinstance(orig, tzinfo))
Tim Petersfb8472c2002-12-21 05:04:42 +0000124 self.failUnless(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000125 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000126 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000127 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000128 green = pickler.dumps(orig, proto)
129 derived = unpickler.loads(green)
130 self.failUnless(isinstance(derived, tzinfo))
131 self.failUnless(type(derived) is PicklableFixedOffset)
132 self.assertEqual(derived.utcoffset(None), offset)
133 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000134
135#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000136# Base clase for testing a particular aspect of timedelta, time, date and
137# datetime comparisons.
138
Guido van Rossumd8faa362007-04-27 19:54:29 +0000139class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000140 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
141
142 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
143 # legit constructor.
144
145 def test_harmless_mixed_comparison(self):
146 me = self.theclass(1, 1, 1)
147
148 self.failIf(me == ())
149 self.failUnless(me != ())
150 self.failIf(() == me)
151 self.failUnless(() != me)
152
Guido van Rossume2a383d2007-01-15 16:59:06 +0000153 self.failUnless(me in [1, 20, [], me])
154 self.failIf(me not in [1, 20, [], me])
Tim Peters07534a62003-02-07 22:50:28 +0000155
Guido van Rossume2a383d2007-01-15 16:59:06 +0000156 self.failUnless([] in [me, 1, 20, []])
157 self.failIf([] not in [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000158
159 def test_harmful_mixed_comparison(self):
160 me = self.theclass(1, 1, 1)
161
162 self.assertRaises(TypeError, lambda: me < ())
163 self.assertRaises(TypeError, lambda: me <= ())
164 self.assertRaises(TypeError, lambda: me > ())
165 self.assertRaises(TypeError, lambda: me >= ())
166
167 self.assertRaises(TypeError, lambda: () < me)
168 self.assertRaises(TypeError, lambda: () <= me)
169 self.assertRaises(TypeError, lambda: () > me)
170 self.assertRaises(TypeError, lambda: () >= me)
171
172 self.assertRaises(TypeError, cmp, (), me)
173 self.assertRaises(TypeError, cmp, me, ())
174
175#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000176# timedelta tests
177
Guido van Rossumd8faa362007-04-27 19:54:29 +0000178class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000179
180 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000181
182 def test_constructor(self):
183 eq = self.assertEqual
184 td = timedelta
185
186 # Check keyword args to constructor
187 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
188 milliseconds=0, microseconds=0))
189 eq(td(1), td(days=1))
190 eq(td(0, 1), td(seconds=1))
191 eq(td(0, 0, 1), td(microseconds=1))
192 eq(td(weeks=1), td(days=7))
193 eq(td(days=1), td(hours=24))
194 eq(td(hours=1), td(minutes=60))
195 eq(td(minutes=1), td(seconds=60))
196 eq(td(seconds=1), td(milliseconds=1000))
197 eq(td(milliseconds=1), td(microseconds=1000))
198
199 # Check float args to constructor
200 eq(td(weeks=1.0/7), td(days=1))
201 eq(td(days=1.0/24), td(hours=1))
202 eq(td(hours=1.0/60), td(minutes=1))
203 eq(td(minutes=1.0/60), td(seconds=1))
204 eq(td(seconds=0.001), td(milliseconds=1))
205 eq(td(milliseconds=0.001), td(microseconds=1))
206
207 def test_computations(self):
208 eq = self.assertEqual
209 td = timedelta
210
211 a = td(7) # One week
212 b = td(0, 60) # One minute
213 c = td(0, 0, 1000) # One millisecond
214 eq(a+b+c, td(7, 60, 1000))
215 eq(a-b, td(6, 24*3600 - 60))
216 eq(-a, td(-7))
217 eq(+a, td(7))
218 eq(-b, td(-1, 24*3600 - 60))
219 eq(-c, td(-1, 24*3600 - 1, 999000))
220 eq(abs(a), a)
221 eq(abs(-a), a)
222 eq(td(6, 24*3600), a)
223 eq(td(0, 0, 60*1000000), b)
224 eq(a*10, td(70))
225 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000226 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000227 eq(b*10, td(0, 600))
228 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000229 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000230 eq(c*10, td(0, 0, 10000))
231 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000232 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000233 eq(a*-1, -a)
234 eq(b*-2, -b-b)
235 eq(c*-2, -c+-c)
236 eq(b*(60*24), (b*60)*24)
237 eq(b*(60*24), (60*b)*24)
238 eq(c*1000, td(0, 1))
239 eq(1000*c, td(0, 1))
240 eq(a//7, td(1))
241 eq(b//10, td(0, 6))
242 eq(c//1000, td(0, 0, 1))
243 eq(a//10, td(0, 7*24*360))
244 eq(a//3600000, td(0, 0, 7*24*1000))
245
246 def test_disallowed_computations(self):
247 a = timedelta(42)
248
249 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000250 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000251 self.assertRaises(TypeError, lambda: a+i)
252 self.assertRaises(TypeError, lambda: a-i)
253 self.assertRaises(TypeError, lambda: i+a)
254 self.assertRaises(TypeError, lambda: i-a)
255
256 # Mul/div by float isn't supported.
257 x = 2.3
258 self.assertRaises(TypeError, lambda: a*x)
259 self.assertRaises(TypeError, lambda: x*a)
260 self.assertRaises(TypeError, lambda: a/x)
261 self.assertRaises(TypeError, lambda: x/a)
262 self.assertRaises(TypeError, lambda: a // x)
263 self.assertRaises(TypeError, lambda: x // a)
264
265 # Divison of int by timedelta doesn't make sense.
266 # Division by zero doesn't make sense.
Guido van Rossume2a383d2007-01-15 16:59:06 +0000267 for zero in 0, 0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000268 self.assertRaises(TypeError, lambda: zero // a)
269 self.assertRaises(ZeroDivisionError, lambda: a // zero)
270
271 def test_basic_attributes(self):
272 days, seconds, us = 1, 7, 31
273 td = timedelta(days, seconds, us)
274 self.assertEqual(td.days, days)
275 self.assertEqual(td.seconds, seconds)
276 self.assertEqual(td.microseconds, us)
277
278 def test_carries(self):
279 t1 = timedelta(days=100,
280 weeks=-7,
281 hours=-24*(100-49),
282 minutes=-3,
283 seconds=12,
284 microseconds=(3*60 - 12) * 1e6 + 1)
285 t2 = timedelta(microseconds=1)
286 self.assertEqual(t1, t2)
287
288 def test_hash_equality(self):
289 t1 = timedelta(days=100,
290 weeks=-7,
291 hours=-24*(100-49),
292 minutes=-3,
293 seconds=12,
294 microseconds=(3*60 - 12) * 1000000)
295 t2 = timedelta()
296 self.assertEqual(hash(t1), hash(t2))
297
298 t1 += timedelta(weeks=7)
299 t2 += timedelta(days=7*7)
300 self.assertEqual(t1, t2)
301 self.assertEqual(hash(t1), hash(t2))
302
303 d = {t1: 1}
304 d[t2] = 2
305 self.assertEqual(len(d), 1)
306 self.assertEqual(d[t1], 2)
307
308 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000309 args = 12, 34, 56
310 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000311 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000312 green = pickler.dumps(orig, proto)
313 derived = unpickler.loads(green)
314 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000315
316 def test_compare(self):
317 t1 = timedelta(2, 3, 4)
318 t2 = timedelta(2, 3, 4)
319 self.failUnless(t1 == t2)
320 self.failUnless(t1 <= t2)
321 self.failUnless(t1 >= t2)
322 self.failUnless(not t1 != t2)
323 self.failUnless(not t1 < t2)
324 self.failUnless(not t1 > t2)
325 self.assertEqual(cmp(t1, t2), 0)
326 self.assertEqual(cmp(t2, t1), 0)
327
328 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
329 t2 = timedelta(*args) # this is larger than t1
330 self.failUnless(t1 < t2)
331 self.failUnless(t2 > t1)
332 self.failUnless(t1 <= t2)
333 self.failUnless(t2 >= t1)
334 self.failUnless(t1 != t2)
335 self.failUnless(t2 != t1)
336 self.failUnless(not t1 == t2)
337 self.failUnless(not t2 == t1)
338 self.failUnless(not t1 > t2)
339 self.failUnless(not t2 < t1)
340 self.failUnless(not t1 >= t2)
341 self.failUnless(not t2 <= t1)
342 self.assertEqual(cmp(t1, t2), -1)
343 self.assertEqual(cmp(t2, t1), 1)
344
Tim Peters68124bb2003-02-08 03:46:31 +0000345 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000346 self.assertEqual(t1 == badarg, False)
347 self.assertEqual(t1 != badarg, True)
348 self.assertEqual(badarg == t1, False)
349 self.assertEqual(badarg != t1, True)
350
Tim Peters2a799bf2002-12-16 20:18:38 +0000351 self.assertRaises(TypeError, lambda: t1 <= badarg)
352 self.assertRaises(TypeError, lambda: t1 < badarg)
353 self.assertRaises(TypeError, lambda: t1 > badarg)
354 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000355 self.assertRaises(TypeError, lambda: badarg <= t1)
356 self.assertRaises(TypeError, lambda: badarg < t1)
357 self.assertRaises(TypeError, lambda: badarg > t1)
358 self.assertRaises(TypeError, lambda: badarg >= t1)
359
360 def test_str(self):
361 td = timedelta
362 eq = self.assertEqual
363
364 eq(str(td(1)), "1 day, 0:00:00")
365 eq(str(td(-1)), "-1 day, 0:00:00")
366 eq(str(td(2)), "2 days, 0:00:00")
367 eq(str(td(-2)), "-2 days, 0:00:00")
368
369 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
370 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
371 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
372 "-210 days, 23:12:34")
373
374 eq(str(td(milliseconds=1)), "0:00:00.001000")
375 eq(str(td(microseconds=3)), "0:00:00.000003")
376
377 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
378 microseconds=999999)),
379 "999999999 days, 23:59:59.999999")
380
381 def test_roundtrip(self):
382 for td in (timedelta(days=999999999, hours=23, minutes=59,
383 seconds=59, microseconds=999999),
384 timedelta(days=-999999999),
385 timedelta(days=1, seconds=2, microseconds=3)):
386
387 # Verify td -> string -> td identity.
388 s = repr(td)
389 self.failUnless(s.startswith('datetime.'))
390 s = s[9:]
391 td2 = eval(s)
392 self.assertEqual(td, td2)
393
394 # Verify identity via reconstructing from pieces.
395 td2 = timedelta(td.days, td.seconds, td.microseconds)
396 self.assertEqual(td, td2)
397
398 def test_resolution_info(self):
399 self.assert_(isinstance(timedelta.min, timedelta))
400 self.assert_(isinstance(timedelta.max, timedelta))
401 self.assert_(isinstance(timedelta.resolution, timedelta))
402 self.assert_(timedelta.max > timedelta.min)
403 self.assertEqual(timedelta.min, timedelta(-999999999))
404 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
405 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
406
407 def test_overflow(self):
408 tiny = timedelta.resolution
409
410 td = timedelta.min + tiny
411 td -= tiny # no problem
412 self.assertRaises(OverflowError, td.__sub__, tiny)
413 self.assertRaises(OverflowError, td.__add__, -tiny)
414
415 td = timedelta.max - tiny
416 td += tiny # no problem
417 self.assertRaises(OverflowError, td.__add__, tiny)
418 self.assertRaises(OverflowError, td.__sub__, -tiny)
419
420 self.assertRaises(OverflowError, lambda: -timedelta.max)
421
422 def test_microsecond_rounding(self):
423 td = timedelta
424 eq = self.assertEqual
425
426 # Single-field rounding.
427 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
428 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
429 eq(td(milliseconds=0.6/1000), td(microseconds=1))
430 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
431
432 # Rounding due to contributions from more than one field.
433 us_per_hour = 3600e6
434 us_per_day = us_per_hour * 24
435 eq(td(days=.4/us_per_day), td(0))
436 eq(td(hours=.2/us_per_hour), td(0))
437 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
438
439 eq(td(days=-.4/us_per_day), td(0))
440 eq(td(hours=-.2/us_per_hour), td(0))
441 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
442
443 def test_massive_normalization(self):
444 td = timedelta(microseconds=-1)
445 self.assertEqual((td.days, td.seconds, td.microseconds),
446 (-1, 24*3600-1, 999999))
447
448 def test_bool(self):
449 self.failUnless(timedelta(1))
450 self.failUnless(timedelta(0, 1))
451 self.failUnless(timedelta(0, 0, 1))
452 self.failUnless(timedelta(microseconds=1))
453 self.failUnless(not timedelta(0))
454
Tim Petersb0c854d2003-05-17 15:57:00 +0000455 def test_subclass_timedelta(self):
456
457 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000458 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000459 def from_td(td):
460 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000461
462 def as_hours(self):
463 sum = (self.days * 24 +
464 self.seconds / 3600.0 +
465 self.microseconds / 3600e6)
466 return round(sum)
467
468 t1 = T(days=1)
469 self.assert_(type(t1) is T)
470 self.assertEqual(t1.as_hours(), 24)
471
472 t2 = T(days=-1, seconds=-3600)
473 self.assert_(type(t2) is T)
474 self.assertEqual(t2.as_hours(), -25)
475
476 t3 = t1 + t2
477 self.assert_(type(t3) is timedelta)
478 t4 = T.from_td(t3)
479 self.assert_(type(t4) is T)
480 self.assertEqual(t3.days, t4.days)
481 self.assertEqual(t3.seconds, t4.seconds)
482 self.assertEqual(t3.microseconds, t4.microseconds)
483 self.assertEqual(str(t3), str(t4))
484 self.assertEqual(t4.as_hours(), -1)
485
Tim Peters2a799bf2002-12-16 20:18:38 +0000486#############################################################################
487# date tests
488
489class TestDateOnly(unittest.TestCase):
490 # Tests here won't pass if also run on datetime objects, so don't
491 # subclass this to test datetimes too.
492
493 def test_delta_non_days_ignored(self):
494 dt = date(2000, 1, 2)
495 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
496 microseconds=5)
497 days = timedelta(delta.days)
498 self.assertEqual(days, timedelta(1))
499
500 dt2 = dt + delta
501 self.assertEqual(dt2, dt + days)
502
503 dt2 = delta + dt
504 self.assertEqual(dt2, dt + days)
505
506 dt2 = dt - delta
507 self.assertEqual(dt2, dt - days)
508
509 delta = -delta
510 days = timedelta(delta.days)
511 self.assertEqual(days, timedelta(-2))
512
513 dt2 = dt + delta
514 self.assertEqual(dt2, dt + days)
515
516 dt2 = delta + dt
517 self.assertEqual(dt2, dt + days)
518
519 dt2 = dt - delta
520 self.assertEqual(dt2, dt - days)
521
Tim Peters604c0132004-06-07 23:04:33 +0000522class SubclassDate(date):
523 sub_var = 1
524
Guido van Rossumd8faa362007-04-27 19:54:29 +0000525class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000526 # Tests here should pass for both dates and datetimes, except for a
527 # few tests that TestDateTime overrides.
528
529 theclass = date
530
531 def test_basic_attributes(self):
532 dt = self.theclass(2002, 3, 1)
533 self.assertEqual(dt.year, 2002)
534 self.assertEqual(dt.month, 3)
535 self.assertEqual(dt.day, 1)
536
537 def test_roundtrip(self):
538 for dt in (self.theclass(1, 2, 3),
539 self.theclass.today()):
540 # Verify dt -> string -> date identity.
541 s = repr(dt)
542 self.failUnless(s.startswith('datetime.'))
543 s = s[9:]
544 dt2 = eval(s)
545 self.assertEqual(dt, dt2)
546
547 # Verify identity via reconstructing from pieces.
548 dt2 = self.theclass(dt.year, dt.month, dt.day)
549 self.assertEqual(dt, dt2)
550
551 def test_ordinal_conversions(self):
552 # Check some fixed values.
553 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
554 (1, 12, 31, 365),
555 (2, 1, 1, 366),
556 # first example from "Calendrical Calculations"
557 (1945, 11, 12, 710347)]:
558 d = self.theclass(y, m, d)
559 self.assertEqual(n, d.toordinal())
560 fromord = self.theclass.fromordinal(n)
561 self.assertEqual(d, fromord)
562 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000563 # if we're checking something fancier than a date, verify
564 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000565 self.assertEqual(fromord.hour, 0)
566 self.assertEqual(fromord.minute, 0)
567 self.assertEqual(fromord.second, 0)
568 self.assertEqual(fromord.microsecond, 0)
569
Tim Peters0bf60bd2003-01-08 20:40:01 +0000570 # Check first and last days of year spottily across the whole
571 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000572 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000573 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
574 d = self.theclass(year, 1, 1)
575 n = d.toordinal()
576 d2 = self.theclass.fromordinal(n)
577 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000578 # Verify that moving back a day gets to the end of year-1.
579 if year > 1:
580 d = self.theclass.fromordinal(n-1)
581 d2 = self.theclass(year-1, 12, 31)
582 self.assertEqual(d, d2)
583 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000584
585 # Test every day in a leap-year and a non-leap year.
586 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
587 for year, isleap in (2000, True), (2002, False):
588 n = self.theclass(year, 1, 1).toordinal()
589 for month, maxday in zip(range(1, 13), dim):
590 if month == 2 and isleap:
591 maxday += 1
592 for day in range(1, maxday+1):
593 d = self.theclass(year, month, day)
594 self.assertEqual(d.toordinal(), n)
595 self.assertEqual(d, self.theclass.fromordinal(n))
596 n += 1
597
598 def test_extreme_ordinals(self):
599 a = self.theclass.min
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 a = self.theclass.max
612 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
613 aord = a.toordinal()
614 b = a.fromordinal(aord)
615 self.assertEqual(a, b)
616
617 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
618
619 b = a - timedelta(days=1)
620 self.assertEqual(b.toordinal(), aord - 1)
621 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
622
623 def test_bad_constructor_arguments(self):
624 # bad years
625 self.theclass(MINYEAR, 1, 1) # no exception
626 self.theclass(MAXYEAR, 1, 1) # no exception
627 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
628 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
629 # bad months
630 self.theclass(2000, 1, 1) # no exception
631 self.theclass(2000, 12, 1) # no exception
632 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
633 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
634 # bad days
635 self.theclass(2000, 2, 29) # no exception
636 self.theclass(2004, 2, 29) # no exception
637 self.theclass(2400, 2, 29) # no exception
638 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
639 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
640 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
641 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
642 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
643 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
644
645 def test_hash_equality(self):
646 d = self.theclass(2000, 12, 31)
647 # same thing
648 e = self.theclass(2000, 12, 31)
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 d = self.theclass(2001, 1, 1)
659 # same thing
660 e = self.theclass(2001, 1, 1)
661 self.assertEqual(d, e)
662 self.assertEqual(hash(d), hash(e))
663
664 dic = {d: 1}
665 dic[e] = 2
666 self.assertEqual(len(dic), 1)
667 self.assertEqual(dic[d], 2)
668 self.assertEqual(dic[e], 2)
669
670 def test_computations(self):
671 a = self.theclass(2002, 1, 31)
672 b = self.theclass(1956, 1, 31)
673
674 diff = a-b
675 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
676 self.assertEqual(diff.seconds, 0)
677 self.assertEqual(diff.microseconds, 0)
678
679 day = timedelta(1)
680 week = timedelta(7)
681 a = self.theclass(2002, 3, 2)
682 self.assertEqual(a + day, self.theclass(2002, 3, 3))
683 self.assertEqual(day + a, self.theclass(2002, 3, 3))
684 self.assertEqual(a - day, self.theclass(2002, 3, 1))
685 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
686 self.assertEqual(a + week, self.theclass(2002, 3, 9))
687 self.assertEqual(a - week, self.theclass(2002, 2, 23))
688 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
689 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
690 self.assertEqual((a + week) - a, week)
691 self.assertEqual((a + day) - a, day)
692 self.assertEqual((a - week) - a, -week)
693 self.assertEqual((a - day) - a, -day)
694 self.assertEqual(a - (a + week), -week)
695 self.assertEqual(a - (a + day), -day)
696 self.assertEqual(a - (a - week), week)
697 self.assertEqual(a - (a - day), day)
698
699 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000700 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000701 self.assertRaises(TypeError, lambda: a+i)
702 self.assertRaises(TypeError, lambda: a-i)
703 self.assertRaises(TypeError, lambda: i+a)
704 self.assertRaises(TypeError, lambda: i-a)
705
706 # delta - date is senseless.
707 self.assertRaises(TypeError, lambda: day - a)
708 # mixing date and (delta or date) via * or // is senseless
709 self.assertRaises(TypeError, lambda: day * a)
710 self.assertRaises(TypeError, lambda: a * day)
711 self.assertRaises(TypeError, lambda: day // a)
712 self.assertRaises(TypeError, lambda: a // day)
713 self.assertRaises(TypeError, lambda: a * a)
714 self.assertRaises(TypeError, lambda: a // a)
715 # date + date is senseless
716 self.assertRaises(TypeError, lambda: a + a)
717
718 def test_overflow(self):
719 tiny = self.theclass.resolution
720
721 dt = self.theclass.min + tiny
722 dt -= tiny # no problem
723 self.assertRaises(OverflowError, dt.__sub__, tiny)
724 self.assertRaises(OverflowError, dt.__add__, -tiny)
725
726 dt = self.theclass.max - tiny
727 dt += tiny # no problem
728 self.assertRaises(OverflowError, dt.__add__, tiny)
729 self.assertRaises(OverflowError, dt.__sub__, -tiny)
730
731 def test_fromtimestamp(self):
732 import time
733
734 # Try an arbitrary fixed value.
735 year, month, day = 1999, 9, 19
736 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
737 d = self.theclass.fromtimestamp(ts)
738 self.assertEqual(d.year, year)
739 self.assertEqual(d.month, month)
740 self.assertEqual(d.day, day)
741
Tim Peters1b6f7a92004-06-20 02:50:16 +0000742 def test_insane_fromtimestamp(self):
743 # It's possible that some platform maps time_t to double,
744 # and that this test will fail there. This test should
745 # exempt such platforms (provided they return reasonable
746 # results!).
747 for insane in -1e200, 1e200:
748 self.assertRaises(ValueError, self.theclass.fromtimestamp,
749 insane)
750
Tim Peters2a799bf2002-12-16 20:18:38 +0000751 def test_today(self):
752 import time
753
754 # We claim that today() is like fromtimestamp(time.time()), so
755 # prove it.
756 for dummy in range(3):
757 today = self.theclass.today()
758 ts = time.time()
759 todayagain = self.theclass.fromtimestamp(ts)
760 if today == todayagain:
761 break
762 # There are several legit reasons that could fail:
763 # 1. It recently became midnight, between the today() and the
764 # time() calls.
765 # 2. The platform time() has such fine resolution that we'll
766 # never get the same value twice.
767 # 3. The platform time() has poor resolution, and we just
768 # happened to call today() right before a resolution quantum
769 # boundary.
770 # 4. The system clock got fiddled between calls.
771 # In any case, wait a little while and try again.
772 time.sleep(0.1)
773
774 # It worked or it didn't. If it didn't, assume it's reason #2, and
775 # let the test pass if they're within half a second of each other.
776 self.failUnless(today == todayagain or
777 abs(todayagain - today) < timedelta(seconds=0.5))
778
779 def test_weekday(self):
780 for i in range(7):
781 # March 4, 2002 is a Monday
782 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
783 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
784 # January 2, 1956 is a Monday
785 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
786 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
787
788 def test_isocalendar(self):
789 # Check examples from
790 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
791 for i in range(7):
792 d = self.theclass(2003, 12, 22+i)
793 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
794 d = self.theclass(2003, 12, 29) + timedelta(i)
795 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
796 d = self.theclass(2004, 1, 5+i)
797 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
798 d = self.theclass(2009, 12, 21+i)
799 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
800 d = self.theclass(2009, 12, 28) + timedelta(i)
801 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
802 d = self.theclass(2010, 1, 4+i)
803 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
804
805 def test_iso_long_years(self):
806 # Calculate long ISO years and compare to table from
807 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
808 ISO_LONG_YEARS_TABLE = """
809 4 32 60 88
810 9 37 65 93
811 15 43 71 99
812 20 48 76
813 26 54 82
814
815 105 133 161 189
816 111 139 167 195
817 116 144 172
818 122 150 178
819 128 156 184
820
821 201 229 257 285
822 207 235 263 291
823 212 240 268 296
824 218 246 274
825 224 252 280
826
827 303 331 359 387
828 308 336 364 392
829 314 342 370 398
830 320 348 376
831 325 353 381
832 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000833 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000834 L = []
835 for i in range(400):
836 d = self.theclass(2000+i, 12, 31)
837 d1 = self.theclass(1600+i, 12, 31)
838 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
839 if d.isocalendar()[1] == 53:
840 L.append(i)
841 self.assertEqual(L, iso_long_years)
842
843 def test_isoformat(self):
844 t = self.theclass(2, 3, 2)
845 self.assertEqual(t.isoformat(), "0002-03-02")
846
847 def test_ctime(self):
848 t = self.theclass(2002, 3, 2)
849 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
850
851 def test_strftime(self):
852 t = self.theclass(2005, 3, 2)
853 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000854 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000855 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000856
857 self.assertRaises(TypeError, t.strftime) # needs an arg
858 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
859 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
860
861 # A naive object replaces %z and %Z w/ empty strings.
862 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
863
864 def test_resolution_info(self):
865 self.assert_(isinstance(self.theclass.min, self.theclass))
866 self.assert_(isinstance(self.theclass.max, self.theclass))
867 self.assert_(isinstance(self.theclass.resolution, timedelta))
868 self.assert_(self.theclass.max > self.theclass.min)
869
870 def test_extreme_timedelta(self):
871 big = self.theclass.max - self.theclass.min
872 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
873 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
874 # n == 315537897599999999 ~= 2**58.13
875 justasbig = timedelta(0, 0, n)
876 self.assertEqual(big, justasbig)
877 self.assertEqual(self.theclass.min + big, self.theclass.max)
878 self.assertEqual(self.theclass.max - big, self.theclass.min)
879
880 def test_timetuple(self):
881 for i in range(7):
882 # January 2, 1956 is a Monday (0)
883 d = self.theclass(1956, 1, 2+i)
884 t = d.timetuple()
885 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
886 # February 1, 1956 is a Wednesday (2)
887 d = self.theclass(1956, 2, 1+i)
888 t = d.timetuple()
889 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
890 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
891 # of the year.
892 d = self.theclass(1956, 3, 1+i)
893 t = d.timetuple()
894 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
895 self.assertEqual(t.tm_year, 1956)
896 self.assertEqual(t.tm_mon, 3)
897 self.assertEqual(t.tm_mday, 1+i)
898 self.assertEqual(t.tm_hour, 0)
899 self.assertEqual(t.tm_min, 0)
900 self.assertEqual(t.tm_sec, 0)
901 self.assertEqual(t.tm_wday, (3+i)%7)
902 self.assertEqual(t.tm_yday, 61+i)
903 self.assertEqual(t.tm_isdst, -1)
904
905 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000906 args = 6, 7, 23
907 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000908 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000909 green = pickler.dumps(orig, proto)
910 derived = unpickler.loads(green)
911 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000912
913 def test_compare(self):
914 t1 = self.theclass(2, 3, 4)
915 t2 = self.theclass(2, 3, 4)
916 self.failUnless(t1 == t2)
917 self.failUnless(t1 <= t2)
918 self.failUnless(t1 >= t2)
919 self.failUnless(not t1 != t2)
920 self.failUnless(not t1 < t2)
921 self.failUnless(not t1 > t2)
922 self.assertEqual(cmp(t1, t2), 0)
923 self.assertEqual(cmp(t2, t1), 0)
924
925 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
926 t2 = self.theclass(*args) # this is larger than t1
927 self.failUnless(t1 < t2)
928 self.failUnless(t2 > t1)
929 self.failUnless(t1 <= t2)
930 self.failUnless(t2 >= t1)
931 self.failUnless(t1 != t2)
932 self.failUnless(t2 != t1)
933 self.failUnless(not t1 == t2)
934 self.failUnless(not t2 == t1)
935 self.failUnless(not t1 > t2)
936 self.failUnless(not t2 < t1)
937 self.failUnless(not t1 >= t2)
938 self.failUnless(not t2 <= t1)
939 self.assertEqual(cmp(t1, t2), -1)
940 self.assertEqual(cmp(t2, t1), 1)
941
Tim Peters68124bb2003-02-08 03:46:31 +0000942 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000943 self.assertEqual(t1 == badarg, False)
944 self.assertEqual(t1 != badarg, True)
945 self.assertEqual(badarg == t1, False)
946 self.assertEqual(badarg != t1, True)
947
Tim Peters2a799bf2002-12-16 20:18:38 +0000948 self.assertRaises(TypeError, lambda: t1 < badarg)
949 self.assertRaises(TypeError, lambda: t1 > badarg)
950 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000951 self.assertRaises(TypeError, lambda: badarg <= t1)
952 self.assertRaises(TypeError, lambda: badarg < t1)
953 self.assertRaises(TypeError, lambda: badarg > t1)
954 self.assertRaises(TypeError, lambda: badarg >= t1)
955
Tim Peters8d81a012003-01-24 22:36:34 +0000956 def test_mixed_compare(self):
957 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000958
959 # Our class can be compared for equality to other classes
960 self.assertEqual(our == 1, False)
961 self.assertEqual(1 == our, False)
962 self.assertEqual(our != 1, True)
963 self.assertEqual(1 != our, True)
964
965 # But the ordering is undefined
966 self.assertRaises(TypeError, lambda: our < 1)
967 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000968 self.assertRaises(TypeError, cmp, our, 1)
969 self.assertRaises(TypeError, cmp, 1, our)
970
Guido van Rossum19960592006-08-24 17:29:38 +0000971 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +0000972
Guido van Rossum19960592006-08-24 17:29:38 +0000973 class SomeClass:
974 pass
975
976 their = SomeClass()
977 self.assertEqual(our == their, False)
978 self.assertEqual(their == our, False)
979 self.assertEqual(our != their, True)
980 self.assertEqual(their != our, True)
981 self.assertRaises(TypeError, lambda: our < their)
982 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000983 self.assertRaises(TypeError, cmp, our, their)
Guido van Rossum19960592006-08-24 17:29:38 +0000984 self.assertRaises(TypeError, cmp, their, our)
Tim Peters8d81a012003-01-24 22:36:34 +0000985
Guido van Rossum19960592006-08-24 17:29:38 +0000986 # However, if the other class explicitly defines ordering
987 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +0000988
Guido van Rossum19960592006-08-24 17:29:38 +0000989 class LargerThanAnything:
990 def __lt__(self, other):
991 return False
992 def __le__(self, other):
993 return isinstance(other, LargerThanAnything)
994 def __eq__(self, other):
995 return isinstance(other, LargerThanAnything)
996 def __ne__(self, other):
997 return not isinstance(other, LargerThanAnything)
998 def __gt__(self, other):
999 return not isinstance(other, LargerThanAnything)
1000 def __ge__(self, other):
1001 return True
1002
1003 their = LargerThanAnything()
1004 self.assertEqual(our == their, False)
1005 self.assertEqual(their == our, False)
1006 self.assertEqual(our != their, True)
1007 self.assertEqual(their != our, True)
1008 self.assertEqual(our < their, True)
1009 self.assertEqual(their < our, False)
1010 self.assertEqual(cmp(our, their), -1)
1011 self.assertEqual(cmp(their, our), 1)
Tim Peters8d81a012003-01-24 22:36:34 +00001012
Tim Peters2a799bf2002-12-16 20:18:38 +00001013 def test_bool(self):
1014 # All dates are considered true.
1015 self.failUnless(self.theclass.min)
1016 self.failUnless(self.theclass.max)
1017
Tim Petersd6844152002-12-22 20:58:42 +00001018 def test_srftime_out_of_range(self):
1019 # For nasty technical reasons, we can't handle years before 1900.
1020 cls = self.theclass
1021 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1022 for y in 1, 49, 51, 99, 100, 1000, 1899:
1023 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001024
1025 def test_replace(self):
1026 cls = self.theclass
1027 args = [1, 2, 3]
1028 base = cls(*args)
1029 self.assertEqual(base, base.replace())
1030
1031 i = 0
1032 for name, newval in (("year", 2),
1033 ("month", 3),
1034 ("day", 4)):
1035 newargs = args[:]
1036 newargs[i] = newval
1037 expected = cls(*newargs)
1038 got = base.replace(**{name: newval})
1039 self.assertEqual(expected, got)
1040 i += 1
1041
1042 # Out of bounds.
1043 base = cls(2000, 2, 29)
1044 self.assertRaises(ValueError, base.replace, year=2001)
1045
Tim Petersa98924a2003-05-17 05:55:19 +00001046 def test_subclass_date(self):
1047
1048 class C(self.theclass):
1049 theAnswer = 42
1050
1051 def __new__(cls, *args, **kws):
1052 temp = kws.copy()
1053 extra = temp.pop('extra')
1054 result = self.theclass.__new__(cls, *args, **temp)
1055 result.extra = extra
1056 return result
1057
1058 def newmeth(self, start):
1059 return start + self.year + self.month
1060
1061 args = 2003, 4, 14
1062
1063 dt1 = self.theclass(*args)
1064 dt2 = C(*args, **{'extra': 7})
1065
1066 self.assertEqual(dt2.__class__, C)
1067 self.assertEqual(dt2.theAnswer, 42)
1068 self.assertEqual(dt2.extra, 7)
1069 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1070 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1071
Tim Peters604c0132004-06-07 23:04:33 +00001072 def test_pickling_subclass_date(self):
1073
1074 args = 6, 7, 23
1075 orig = SubclassDate(*args)
1076 for pickler, unpickler, proto in pickle_choices:
1077 green = pickler.dumps(orig, proto)
1078 derived = unpickler.loads(green)
1079 self.assertEqual(orig, derived)
1080
Tim Peters3f606292004-03-21 23:38:41 +00001081 def test_backdoor_resistance(self):
1082 # For fast unpickling, the constructor accepts a pickle string.
1083 # This is a low-overhead backdoor. A user can (by intent or
1084 # mistake) pass a string directly, which (if it's the right length)
1085 # will get treated like a pickle, and bypass the normal sanity
1086 # checks in the constructor. This can create insane objects.
1087 # The constructor doesn't want to burn the time to validate all
1088 # fields, but does check the month field. This stops, e.g.,
1089 # datetime.datetime('1995-03-25') from yielding an insane object.
1090 base = '1995-03-25'
1091 if not issubclass(self.theclass, datetime):
1092 base = base[:4]
1093 for month_byte in '9', chr(0), chr(13), '\xff':
1094 self.assertRaises(TypeError, self.theclass,
1095 base[:2] + month_byte + base[3:])
1096 for ord_byte in range(1, 13):
1097 # This shouldn't blow up because of the month byte alone. If
1098 # the implementation changes to do more-careful checking, it may
1099 # blow up because other fields are insane.
Guido van Rossume3d1d412007-05-23 21:24:35 +00001100 # XXX Maybe this will have to become bytes?
1101 self.theclass(str8(base[:2] + chr(ord_byte) + base[3:]))
Tim Peterseb1a4962003-05-17 02:25:20 +00001102
Tim Peters2a799bf2002-12-16 20:18:38 +00001103#############################################################################
1104# datetime tests
1105
Tim Peters604c0132004-06-07 23:04:33 +00001106class SubclassDatetime(datetime):
1107 sub_var = 1
1108
Tim Peters2a799bf2002-12-16 20:18:38 +00001109class TestDateTime(TestDate):
1110
1111 theclass = datetime
1112
1113 def test_basic_attributes(self):
1114 dt = self.theclass(2002, 3, 1, 12, 0)
1115 self.assertEqual(dt.year, 2002)
1116 self.assertEqual(dt.month, 3)
1117 self.assertEqual(dt.day, 1)
1118 self.assertEqual(dt.hour, 12)
1119 self.assertEqual(dt.minute, 0)
1120 self.assertEqual(dt.second, 0)
1121 self.assertEqual(dt.microsecond, 0)
1122
1123 def test_basic_attributes_nonzero(self):
1124 # Make sure all attributes are non-zero so bugs in
1125 # bit-shifting access show up.
1126 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1127 self.assertEqual(dt.year, 2002)
1128 self.assertEqual(dt.month, 3)
1129 self.assertEqual(dt.day, 1)
1130 self.assertEqual(dt.hour, 12)
1131 self.assertEqual(dt.minute, 59)
1132 self.assertEqual(dt.second, 59)
1133 self.assertEqual(dt.microsecond, 8000)
1134
1135 def test_roundtrip(self):
1136 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1137 self.theclass.now()):
1138 # Verify dt -> string -> datetime identity.
1139 s = repr(dt)
1140 self.failUnless(s.startswith('datetime.'))
1141 s = s[9:]
1142 dt2 = eval(s)
1143 self.assertEqual(dt, dt2)
1144
1145 # Verify identity via reconstructing from pieces.
1146 dt2 = self.theclass(dt.year, dt.month, dt.day,
1147 dt.hour, dt.minute, dt.second,
1148 dt.microsecond)
1149 self.assertEqual(dt, dt2)
1150
1151 def test_isoformat(self):
1152 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1153 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1154 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1155 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1156 # str is ISO format with the separator forced to a blank.
1157 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1158
1159 t = self.theclass(2, 3, 2)
1160 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1161 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1162 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1163 # str is ISO format with the separator forced to a blank.
1164 self.assertEqual(str(t), "0002-03-02 00:00:00")
1165
1166 def test_more_ctime(self):
1167 # Test fields that TestDate doesn't touch.
1168 import time
1169
1170 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1171 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1172 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1173 # out. The difference is that t.ctime() produces " 2" for the day,
1174 # but platform ctime() produces "02" for the day. According to
1175 # C99, t.ctime() is correct here.
1176 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1177
1178 # So test a case where that difference doesn't matter.
1179 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1180 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1181
1182 def test_tz_independent_comparing(self):
1183 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1184 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1185 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1186 self.assertEqual(dt1, dt3)
1187 self.assert_(dt2 > dt3)
1188
1189 # Make sure comparison doesn't forget microseconds, and isn't done
1190 # via comparing a float timestamp (an IEEE double doesn't have enough
1191 # precision to span microsecond resolution across years 1 thru 9999,
1192 # so comparing via timestamp necessarily calls some distinct values
1193 # equal).
1194 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1195 us = timedelta(microseconds=1)
1196 dt2 = dt1 + us
1197 self.assertEqual(dt2 - dt1, us)
1198 self.assert_(dt1 < dt2)
1199
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001200 def test_strftime_with_bad_tzname_replace(self):
1201 # verify ok if tzinfo.tzname().replace() returns a non-string
1202 class MyTzInfo(FixedOffset):
1203 def tzname(self, dt):
1204 class MyStr(str):
1205 def replace(self, *args):
1206 return None
1207 return MyStr('name')
1208 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1209 self.assertRaises(TypeError, t.strftime, '%Z')
1210
Tim Peters2a799bf2002-12-16 20:18:38 +00001211 def test_bad_constructor_arguments(self):
1212 # bad years
1213 self.theclass(MINYEAR, 1, 1) # no exception
1214 self.theclass(MAXYEAR, 1, 1) # no exception
1215 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1216 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1217 # bad months
1218 self.theclass(2000, 1, 1) # no exception
1219 self.theclass(2000, 12, 1) # no exception
1220 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1221 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1222 # bad days
1223 self.theclass(2000, 2, 29) # no exception
1224 self.theclass(2004, 2, 29) # no exception
1225 self.theclass(2400, 2, 29) # no exception
1226 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1227 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1228 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1229 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1230 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1231 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1232 # bad hours
1233 self.theclass(2000, 1, 31, 0) # no exception
1234 self.theclass(2000, 1, 31, 23) # no exception
1235 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1236 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1237 # bad minutes
1238 self.theclass(2000, 1, 31, 23, 0) # no exception
1239 self.theclass(2000, 1, 31, 23, 59) # no exception
1240 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1241 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1242 # bad seconds
1243 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1244 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1245 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1246 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1247 # bad microseconds
1248 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1249 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1250 self.assertRaises(ValueError, self.theclass,
1251 2000, 1, 31, 23, 59, 59, -1)
1252 self.assertRaises(ValueError, self.theclass,
1253 2000, 1, 31, 23, 59, 59,
1254 1000000)
1255
1256 def test_hash_equality(self):
1257 d = self.theclass(2000, 12, 31, 23, 30, 17)
1258 e = self.theclass(2000, 12, 31, 23, 30, 17)
1259 self.assertEqual(d, e)
1260 self.assertEqual(hash(d), hash(e))
1261
1262 dic = {d: 1}
1263 dic[e] = 2
1264 self.assertEqual(len(dic), 1)
1265 self.assertEqual(dic[d], 2)
1266 self.assertEqual(dic[e], 2)
1267
1268 d = self.theclass(2001, 1, 1, 0, 5, 17)
1269 e = self.theclass(2001, 1, 1, 0, 5, 17)
1270 self.assertEqual(d, e)
1271 self.assertEqual(hash(d), hash(e))
1272
1273 dic = {d: 1}
1274 dic[e] = 2
1275 self.assertEqual(len(dic), 1)
1276 self.assertEqual(dic[d], 2)
1277 self.assertEqual(dic[e], 2)
1278
1279 def test_computations(self):
1280 a = self.theclass(2002, 1, 31)
1281 b = self.theclass(1956, 1, 31)
1282 diff = a-b
1283 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1284 self.assertEqual(diff.seconds, 0)
1285 self.assertEqual(diff.microseconds, 0)
1286 a = self.theclass(2002, 3, 2, 17, 6)
1287 millisec = timedelta(0, 0, 1000)
1288 hour = timedelta(0, 3600)
1289 day = timedelta(1)
1290 week = timedelta(7)
1291 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1292 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1293 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1294 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1295 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1296 self.assertEqual(a - hour, a + -hour)
1297 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1298 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1299 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1300 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1301 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1302 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1303 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1304 self.assertEqual((a + week) - a, week)
1305 self.assertEqual((a + day) - a, day)
1306 self.assertEqual((a + hour) - a, hour)
1307 self.assertEqual((a + millisec) - a, millisec)
1308 self.assertEqual((a - week) - a, -week)
1309 self.assertEqual((a - day) - a, -day)
1310 self.assertEqual((a - hour) - a, -hour)
1311 self.assertEqual((a - millisec) - a, -millisec)
1312 self.assertEqual(a - (a + week), -week)
1313 self.assertEqual(a - (a + day), -day)
1314 self.assertEqual(a - (a + hour), -hour)
1315 self.assertEqual(a - (a + millisec), -millisec)
1316 self.assertEqual(a - (a - week), week)
1317 self.assertEqual(a - (a - day), day)
1318 self.assertEqual(a - (a - hour), hour)
1319 self.assertEqual(a - (a - millisec), millisec)
1320 self.assertEqual(a + (week + day + hour + millisec),
1321 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1322 self.assertEqual(a + (week + day + hour + millisec),
1323 (((a + week) + day) + hour) + millisec)
1324 self.assertEqual(a - (week + day + hour + millisec),
1325 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1326 self.assertEqual(a - (week + day + hour + millisec),
1327 (((a - week) - day) - hour) - millisec)
1328 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001329 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001330 self.assertRaises(TypeError, lambda: a+i)
1331 self.assertRaises(TypeError, lambda: a-i)
1332 self.assertRaises(TypeError, lambda: i+a)
1333 self.assertRaises(TypeError, lambda: i-a)
1334
1335 # delta - datetime is senseless.
1336 self.assertRaises(TypeError, lambda: day - a)
1337 # mixing datetime and (delta or datetime) via * or // is senseless
1338 self.assertRaises(TypeError, lambda: day * a)
1339 self.assertRaises(TypeError, lambda: a * day)
1340 self.assertRaises(TypeError, lambda: day // a)
1341 self.assertRaises(TypeError, lambda: a // day)
1342 self.assertRaises(TypeError, lambda: a * a)
1343 self.assertRaises(TypeError, lambda: a // a)
1344 # datetime + datetime is senseless
1345 self.assertRaises(TypeError, lambda: a + a)
1346
1347 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001348 args = 6, 7, 23, 20, 59, 1, 64**2
1349 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001350 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001351 green = pickler.dumps(orig, proto)
1352 derived = unpickler.loads(green)
1353 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001354
Guido van Rossum275666f2003-02-07 21:49:01 +00001355 def test_more_pickling(self):
1356 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1357 s = pickle.dumps(a)
1358 b = pickle.loads(s)
1359 self.assertEqual(b.year, 2003)
1360 self.assertEqual(b.month, 2)
1361 self.assertEqual(b.day, 7)
1362
Tim Peters604c0132004-06-07 23:04:33 +00001363 def test_pickling_subclass_datetime(self):
1364 args = 6, 7, 23, 20, 59, 1, 64**2
1365 orig = SubclassDatetime(*args)
1366 for pickler, unpickler, proto in pickle_choices:
1367 green = pickler.dumps(orig, proto)
1368 derived = unpickler.loads(green)
1369 self.assertEqual(orig, derived)
1370
Tim Peters2a799bf2002-12-16 20:18:38 +00001371 def test_more_compare(self):
1372 # The test_compare() inherited from TestDate covers the error cases.
1373 # We just want to test lexicographic ordering on the members datetime
1374 # has that date lacks.
1375 args = [2000, 11, 29, 20, 58, 16, 999998]
1376 t1 = self.theclass(*args)
1377 t2 = self.theclass(*args)
1378 self.failUnless(t1 == t2)
1379 self.failUnless(t1 <= t2)
1380 self.failUnless(t1 >= t2)
1381 self.failUnless(not t1 != t2)
1382 self.failUnless(not t1 < t2)
1383 self.failUnless(not t1 > t2)
1384 self.assertEqual(cmp(t1, t2), 0)
1385 self.assertEqual(cmp(t2, t1), 0)
1386
1387 for i in range(len(args)):
1388 newargs = args[:]
1389 newargs[i] = args[i] + 1
1390 t2 = self.theclass(*newargs) # this is larger than t1
1391 self.failUnless(t1 < t2)
1392 self.failUnless(t2 > t1)
1393 self.failUnless(t1 <= t2)
1394 self.failUnless(t2 >= t1)
1395 self.failUnless(t1 != t2)
1396 self.failUnless(t2 != t1)
1397 self.failUnless(not t1 == t2)
1398 self.failUnless(not t2 == t1)
1399 self.failUnless(not t1 > t2)
1400 self.failUnless(not t2 < t1)
1401 self.failUnless(not t1 >= t2)
1402 self.failUnless(not t2 <= t1)
1403 self.assertEqual(cmp(t1, t2), -1)
1404 self.assertEqual(cmp(t2, t1), 1)
1405
1406
1407 # A helper for timestamp constructor tests.
1408 def verify_field_equality(self, expected, got):
1409 self.assertEqual(expected.tm_year, got.year)
1410 self.assertEqual(expected.tm_mon, got.month)
1411 self.assertEqual(expected.tm_mday, got.day)
1412 self.assertEqual(expected.tm_hour, got.hour)
1413 self.assertEqual(expected.tm_min, got.minute)
1414 self.assertEqual(expected.tm_sec, got.second)
1415
1416 def test_fromtimestamp(self):
1417 import time
1418
1419 ts = time.time()
1420 expected = time.localtime(ts)
1421 got = self.theclass.fromtimestamp(ts)
1422 self.verify_field_equality(expected, got)
1423
1424 def test_utcfromtimestamp(self):
1425 import time
1426
1427 ts = time.time()
1428 expected = time.gmtime(ts)
1429 got = self.theclass.utcfromtimestamp(ts)
1430 self.verify_field_equality(expected, got)
1431
Thomas Wouters477c8d52006-05-27 19:21:47 +00001432 def test_microsecond_rounding(self):
1433 # Test whether fromtimestamp "rounds up" floats that are less
1434 # than one microsecond smaller than an integer.
1435 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1436 self.theclass.fromtimestamp(1))
1437
Tim Peters1b6f7a92004-06-20 02:50:16 +00001438 def test_insane_fromtimestamp(self):
1439 # It's possible that some platform maps time_t to double,
1440 # and that this test will fail there. This test should
1441 # exempt such platforms (provided they return reasonable
1442 # results!).
1443 for insane in -1e200, 1e200:
1444 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1445 insane)
1446
1447 def test_insane_utcfromtimestamp(self):
1448 # It's possible that some platform maps time_t to double,
1449 # and that this test will fail there. This test should
1450 # exempt such platforms (provided they return reasonable
1451 # results!).
1452 for insane in -1e200, 1e200:
1453 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1454 insane)
1455
Guido van Rossumd8faa362007-04-27 19:54:29 +00001456 def test_negative_float_fromtimestamp(self):
1457 # Windows doesn't accept negative timestamps
1458 if os.name == "nt":
1459 return
1460 # The result is tz-dependent; at least test that this doesn't
1461 # fail (like it did before bug 1646728 was fixed).
1462 self.theclass.fromtimestamp(-1.05)
1463
1464 def test_negative_float_utcfromtimestamp(self):
1465 # Windows doesn't accept negative timestamps
1466 if os.name == "nt":
1467 return
1468 d = self.theclass.utcfromtimestamp(-1.05)
1469 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1470
Tim Peters2a799bf2002-12-16 20:18:38 +00001471 def test_utcnow(self):
1472 import time
1473
1474 # Call it a success if utcnow() and utcfromtimestamp() are within
1475 # a second of each other.
1476 tolerance = timedelta(seconds=1)
1477 for dummy in range(3):
1478 from_now = self.theclass.utcnow()
1479 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1480 if abs(from_timestamp - from_now) <= tolerance:
1481 break
1482 # Else try again a few times.
1483 self.failUnless(abs(from_timestamp - from_now) <= tolerance)
1484
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001485 def test_strptime(self):
1486 import time
1487
1488 string = '2004-12-01 13:02:47'
1489 format = '%Y-%m-%d %H:%M:%S'
1490 expected = self.theclass(*(time.strptime(string, format)[0:6]))
1491 got = self.theclass.strptime(string, format)
1492 self.assertEqual(expected, got)
1493
Tim Peters2a799bf2002-12-16 20:18:38 +00001494 def test_more_timetuple(self):
1495 # This tests fields beyond those tested by the TestDate.test_timetuple.
1496 t = self.theclass(2004, 12, 31, 6, 22, 33)
1497 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1498 self.assertEqual(t.timetuple(),
1499 (t.year, t.month, t.day,
1500 t.hour, t.minute, t.second,
1501 t.weekday(),
1502 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1503 -1))
1504 tt = t.timetuple()
1505 self.assertEqual(tt.tm_year, t.year)
1506 self.assertEqual(tt.tm_mon, t.month)
1507 self.assertEqual(tt.tm_mday, t.day)
1508 self.assertEqual(tt.tm_hour, t.hour)
1509 self.assertEqual(tt.tm_min, t.minute)
1510 self.assertEqual(tt.tm_sec, t.second)
1511 self.assertEqual(tt.tm_wday, t.weekday())
1512 self.assertEqual(tt.tm_yday, t.toordinal() -
1513 date(t.year, 1, 1).toordinal() + 1)
1514 self.assertEqual(tt.tm_isdst, -1)
1515
1516 def test_more_strftime(self):
1517 # This tests fields beyond those tested by the TestDate.test_strftime.
1518 t = self.theclass(2004, 12, 31, 6, 22, 33)
1519 self.assertEqual(t.strftime("%m %d %y %S %M %H %j"),
1520 "12 31 04 33 22 06 366")
1521
1522 def test_extract(self):
1523 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1524 self.assertEqual(dt.date(), date(2002, 3, 4))
1525 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1526
1527 def test_combine(self):
1528 d = date(2002, 3, 4)
1529 t = time(18, 45, 3, 1234)
1530 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1531 combine = self.theclass.combine
1532 dt = combine(d, t)
1533 self.assertEqual(dt, expected)
1534
1535 dt = combine(time=t, date=d)
1536 self.assertEqual(dt, expected)
1537
1538 self.assertEqual(d, dt.date())
1539 self.assertEqual(t, dt.time())
1540 self.assertEqual(dt, combine(dt.date(), dt.time()))
1541
1542 self.assertRaises(TypeError, combine) # need an arg
1543 self.assertRaises(TypeError, combine, d) # need two args
1544 self.assertRaises(TypeError, combine, t, d) # args reversed
1545 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1546 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1547
Tim Peters12bf3392002-12-24 05:41:27 +00001548 def test_replace(self):
1549 cls = self.theclass
1550 args = [1, 2, 3, 4, 5, 6, 7]
1551 base = cls(*args)
1552 self.assertEqual(base, base.replace())
1553
1554 i = 0
1555 for name, newval in (("year", 2),
1556 ("month", 3),
1557 ("day", 4),
1558 ("hour", 5),
1559 ("minute", 6),
1560 ("second", 7),
1561 ("microsecond", 8)):
1562 newargs = args[:]
1563 newargs[i] = newval
1564 expected = cls(*newargs)
1565 got = base.replace(**{name: newval})
1566 self.assertEqual(expected, got)
1567 i += 1
1568
1569 # Out of bounds.
1570 base = cls(2000, 2, 29)
1571 self.assertRaises(ValueError, base.replace, year=2001)
1572
Tim Peters80475bb2002-12-25 07:40:55 +00001573 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001574 # Pretty boring! The TZ test is more interesting here. astimezone()
1575 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001576 dt = self.theclass.now()
1577 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001578 self.assertRaises(TypeError, dt.astimezone) # not enough args
1579 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1580 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001581 self.assertRaises(ValueError, dt.astimezone, f) # naive
1582 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001583
Tim Peters52dcce22003-01-23 16:36:11 +00001584 class Bogus(tzinfo):
1585 def utcoffset(self, dt): return None
1586 def dst(self, dt): return timedelta(0)
1587 bog = Bogus()
1588 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1589
1590 class AlsoBogus(tzinfo):
1591 def utcoffset(self, dt): return timedelta(0)
1592 def dst(self, dt): return None
1593 alsobog = AlsoBogus()
1594 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001595
Tim Petersa98924a2003-05-17 05:55:19 +00001596 def test_subclass_datetime(self):
1597
1598 class C(self.theclass):
1599 theAnswer = 42
1600
1601 def __new__(cls, *args, **kws):
1602 temp = kws.copy()
1603 extra = temp.pop('extra')
1604 result = self.theclass.__new__(cls, *args, **temp)
1605 result.extra = extra
1606 return result
1607
1608 def newmeth(self, start):
1609 return start + self.year + self.month + self.second
1610
1611 args = 2003, 4, 14, 12, 13, 41
1612
1613 dt1 = self.theclass(*args)
1614 dt2 = C(*args, **{'extra': 7})
1615
1616 self.assertEqual(dt2.__class__, C)
1617 self.assertEqual(dt2.theAnswer, 42)
1618 self.assertEqual(dt2.extra, 7)
1619 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1620 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1621 dt1.second - 7)
1622
Tim Peters604c0132004-06-07 23:04:33 +00001623class SubclassTime(time):
1624 sub_var = 1
1625
Guido van Rossumd8faa362007-04-27 19:54:29 +00001626class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001627
1628 theclass = time
1629
1630 def test_basic_attributes(self):
1631 t = self.theclass(12, 0)
1632 self.assertEqual(t.hour, 12)
1633 self.assertEqual(t.minute, 0)
1634 self.assertEqual(t.second, 0)
1635 self.assertEqual(t.microsecond, 0)
1636
1637 def test_basic_attributes_nonzero(self):
1638 # Make sure all attributes are non-zero so bugs in
1639 # bit-shifting access show up.
1640 t = self.theclass(12, 59, 59, 8000)
1641 self.assertEqual(t.hour, 12)
1642 self.assertEqual(t.minute, 59)
1643 self.assertEqual(t.second, 59)
1644 self.assertEqual(t.microsecond, 8000)
1645
1646 def test_roundtrip(self):
1647 t = self.theclass(1, 2, 3, 4)
1648
1649 # Verify t -> string -> time identity.
1650 s = repr(t)
1651 self.failUnless(s.startswith('datetime.'))
1652 s = s[9:]
1653 t2 = eval(s)
1654 self.assertEqual(t, t2)
1655
1656 # Verify identity via reconstructing from pieces.
1657 t2 = self.theclass(t.hour, t.minute, t.second,
1658 t.microsecond)
1659 self.assertEqual(t, t2)
1660
1661 def test_comparing(self):
1662 args = [1, 2, 3, 4]
1663 t1 = self.theclass(*args)
1664 t2 = self.theclass(*args)
1665 self.failUnless(t1 == t2)
1666 self.failUnless(t1 <= t2)
1667 self.failUnless(t1 >= t2)
1668 self.failUnless(not t1 != t2)
1669 self.failUnless(not t1 < t2)
1670 self.failUnless(not t1 > t2)
1671 self.assertEqual(cmp(t1, t2), 0)
1672 self.assertEqual(cmp(t2, t1), 0)
1673
1674 for i in range(len(args)):
1675 newargs = args[:]
1676 newargs[i] = args[i] + 1
1677 t2 = self.theclass(*newargs) # this is larger than t1
1678 self.failUnless(t1 < t2)
1679 self.failUnless(t2 > t1)
1680 self.failUnless(t1 <= t2)
1681 self.failUnless(t2 >= t1)
1682 self.failUnless(t1 != t2)
1683 self.failUnless(t2 != t1)
1684 self.failUnless(not t1 == t2)
1685 self.failUnless(not t2 == t1)
1686 self.failUnless(not t1 > t2)
1687 self.failUnless(not t2 < t1)
1688 self.failUnless(not t1 >= t2)
1689 self.failUnless(not t2 <= t1)
1690 self.assertEqual(cmp(t1, t2), -1)
1691 self.assertEqual(cmp(t2, t1), 1)
1692
Tim Peters68124bb2003-02-08 03:46:31 +00001693 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001694 self.assertEqual(t1 == badarg, False)
1695 self.assertEqual(t1 != badarg, True)
1696 self.assertEqual(badarg == t1, False)
1697 self.assertEqual(badarg != t1, True)
1698
Tim Peters2a799bf2002-12-16 20:18:38 +00001699 self.assertRaises(TypeError, lambda: t1 <= badarg)
1700 self.assertRaises(TypeError, lambda: t1 < badarg)
1701 self.assertRaises(TypeError, lambda: t1 > badarg)
1702 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001703 self.assertRaises(TypeError, lambda: badarg <= t1)
1704 self.assertRaises(TypeError, lambda: badarg < t1)
1705 self.assertRaises(TypeError, lambda: badarg > t1)
1706 self.assertRaises(TypeError, lambda: badarg >= t1)
1707
1708 def test_bad_constructor_arguments(self):
1709 # bad hours
1710 self.theclass(0, 0) # no exception
1711 self.theclass(23, 0) # no exception
1712 self.assertRaises(ValueError, self.theclass, -1, 0)
1713 self.assertRaises(ValueError, self.theclass, 24, 0)
1714 # bad minutes
1715 self.theclass(23, 0) # no exception
1716 self.theclass(23, 59) # no exception
1717 self.assertRaises(ValueError, self.theclass, 23, -1)
1718 self.assertRaises(ValueError, self.theclass, 23, 60)
1719 # bad seconds
1720 self.theclass(23, 59, 0) # no exception
1721 self.theclass(23, 59, 59) # no exception
1722 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1723 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1724 # bad microseconds
1725 self.theclass(23, 59, 59, 0) # no exception
1726 self.theclass(23, 59, 59, 999999) # no exception
1727 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1728 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1729
1730 def test_hash_equality(self):
1731 d = self.theclass(23, 30, 17)
1732 e = self.theclass(23, 30, 17)
1733 self.assertEqual(d, e)
1734 self.assertEqual(hash(d), hash(e))
1735
1736 dic = {d: 1}
1737 dic[e] = 2
1738 self.assertEqual(len(dic), 1)
1739 self.assertEqual(dic[d], 2)
1740 self.assertEqual(dic[e], 2)
1741
1742 d = self.theclass(0, 5, 17)
1743 e = self.theclass(0, 5, 17)
1744 self.assertEqual(d, e)
1745 self.assertEqual(hash(d), hash(e))
1746
1747 dic = {d: 1}
1748 dic[e] = 2
1749 self.assertEqual(len(dic), 1)
1750 self.assertEqual(dic[d], 2)
1751 self.assertEqual(dic[e], 2)
1752
1753 def test_isoformat(self):
1754 t = self.theclass(4, 5, 1, 123)
1755 self.assertEqual(t.isoformat(), "04:05:01.000123")
1756 self.assertEqual(t.isoformat(), str(t))
1757
1758 t = self.theclass()
1759 self.assertEqual(t.isoformat(), "00:00:00")
1760 self.assertEqual(t.isoformat(), str(t))
1761
1762 t = self.theclass(microsecond=1)
1763 self.assertEqual(t.isoformat(), "00:00:00.000001")
1764 self.assertEqual(t.isoformat(), str(t))
1765
1766 t = self.theclass(microsecond=10)
1767 self.assertEqual(t.isoformat(), "00:00:00.000010")
1768 self.assertEqual(t.isoformat(), str(t))
1769
1770 t = self.theclass(microsecond=100)
1771 self.assertEqual(t.isoformat(), "00:00:00.000100")
1772 self.assertEqual(t.isoformat(), str(t))
1773
1774 t = self.theclass(microsecond=1000)
1775 self.assertEqual(t.isoformat(), "00:00:00.001000")
1776 self.assertEqual(t.isoformat(), str(t))
1777
1778 t = self.theclass(microsecond=10000)
1779 self.assertEqual(t.isoformat(), "00:00:00.010000")
1780 self.assertEqual(t.isoformat(), str(t))
1781
1782 t = self.theclass(microsecond=100000)
1783 self.assertEqual(t.isoformat(), "00:00:00.100000")
1784 self.assertEqual(t.isoformat(), str(t))
1785
Thomas Wouterscf297e42007-02-23 15:07:44 +00001786 def test_1653736(self):
1787 # verify it doesn't accept extra keyword arguments
1788 t = self.theclass(second=1)
1789 self.assertRaises(TypeError, t.isoformat, foo=3)
1790
Tim Peters2a799bf2002-12-16 20:18:38 +00001791 def test_strftime(self):
1792 t = self.theclass(1, 2, 3, 4)
1793 self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
1794 # A naive object replaces %z and %Z with empty strings.
1795 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1796
1797 def test_str(self):
1798 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1799 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1800 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1801 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1802 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1803
1804 def test_repr(self):
1805 name = 'datetime.' + self.theclass.__name__
1806 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1807 "%s(1, 2, 3, 4)" % name)
1808 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1809 "%s(10, 2, 3, 4000)" % name)
1810 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1811 "%s(0, 2, 3, 400000)" % name)
1812 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1813 "%s(12, 2, 3)" % name)
1814 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1815 "%s(23, 15)" % name)
1816
1817 def test_resolution_info(self):
1818 self.assert_(isinstance(self.theclass.min, self.theclass))
1819 self.assert_(isinstance(self.theclass.max, self.theclass))
1820 self.assert_(isinstance(self.theclass.resolution, timedelta))
1821 self.assert_(self.theclass.max > self.theclass.min)
1822
1823 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001824 args = 20, 59, 16, 64**2
1825 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001826 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001827 green = pickler.dumps(orig, proto)
1828 derived = unpickler.loads(green)
1829 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001830
Tim Peters604c0132004-06-07 23:04:33 +00001831 def test_pickling_subclass_time(self):
1832 args = 20, 59, 16, 64**2
1833 orig = SubclassTime(*args)
1834 for pickler, unpickler, proto in pickle_choices:
1835 green = pickler.dumps(orig, proto)
1836 derived = unpickler.loads(green)
1837 self.assertEqual(orig, derived)
1838
Tim Peters2a799bf2002-12-16 20:18:38 +00001839 def test_bool(self):
1840 cls = self.theclass
1841 self.failUnless(cls(1))
1842 self.failUnless(cls(0, 1))
1843 self.failUnless(cls(0, 0, 1))
1844 self.failUnless(cls(0, 0, 0, 1))
1845 self.failUnless(not cls(0))
1846 self.failUnless(not cls())
1847
Tim Peters12bf3392002-12-24 05:41:27 +00001848 def test_replace(self):
1849 cls = self.theclass
1850 args = [1, 2, 3, 4]
1851 base = cls(*args)
1852 self.assertEqual(base, base.replace())
1853
1854 i = 0
1855 for name, newval in (("hour", 5),
1856 ("minute", 6),
1857 ("second", 7),
1858 ("microsecond", 8)):
1859 newargs = args[:]
1860 newargs[i] = newval
1861 expected = cls(*newargs)
1862 got = base.replace(**{name: newval})
1863 self.assertEqual(expected, got)
1864 i += 1
1865
1866 # Out of bounds.
1867 base = cls(1)
1868 self.assertRaises(ValueError, base.replace, hour=24)
1869 self.assertRaises(ValueError, base.replace, minute=-1)
1870 self.assertRaises(ValueError, base.replace, second=100)
1871 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1872
Tim Petersa98924a2003-05-17 05:55:19 +00001873 def test_subclass_time(self):
1874
1875 class C(self.theclass):
1876 theAnswer = 42
1877
1878 def __new__(cls, *args, **kws):
1879 temp = kws.copy()
1880 extra = temp.pop('extra')
1881 result = self.theclass.__new__(cls, *args, **temp)
1882 result.extra = extra
1883 return result
1884
1885 def newmeth(self, start):
1886 return start + self.hour + self.second
1887
1888 args = 4, 5, 6
1889
1890 dt1 = self.theclass(*args)
1891 dt2 = C(*args, **{'extra': 7})
1892
1893 self.assertEqual(dt2.__class__, C)
1894 self.assertEqual(dt2.theAnswer, 42)
1895 self.assertEqual(dt2.extra, 7)
1896 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1897 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1898
Armin Rigof4afb212005-11-07 07:15:48 +00001899 def test_backdoor_resistance(self):
1900 # see TestDate.test_backdoor_resistance().
1901 base = '2:59.0'
1902 for hour_byte in ' ', '9', chr(24), '\xff':
1903 self.assertRaises(TypeError, self.theclass,
1904 hour_byte + base[1:])
1905
Tim Peters855fe882002-12-22 03:43:39 +00001906# A mixin for classes with a tzinfo= argument. Subclasses must define
1907# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001908# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001909class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001910
Tim Petersbad8ff02002-12-30 20:52:32 +00001911 def test_argument_passing(self):
1912 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001913 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001914 class introspective(tzinfo):
1915 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001916 def utcoffset(self, dt):
1917 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001918 dst = utcoffset
1919
1920 obj = cls(1, 2, 3, tzinfo=introspective())
1921
Tim Peters0bf60bd2003-01-08 20:40:01 +00001922 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001923 self.assertEqual(obj.tzname(), expected)
1924
Tim Peters0bf60bd2003-01-08 20:40:01 +00001925 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001926 self.assertEqual(obj.utcoffset(), expected)
1927 self.assertEqual(obj.dst(), expected)
1928
Tim Peters855fe882002-12-22 03:43:39 +00001929 def test_bad_tzinfo_classes(self):
1930 cls = self.theclass
1931 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001932
Tim Peters855fe882002-12-22 03:43:39 +00001933 class NiceTry(object):
1934 def __init__(self): pass
1935 def utcoffset(self, dt): pass
1936 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1937
1938 class BetterTry(tzinfo):
1939 def __init__(self): pass
1940 def utcoffset(self, dt): pass
1941 b = BetterTry()
1942 t = cls(1, 1, 1, tzinfo=b)
1943 self.failUnless(t.tzinfo is b)
1944
1945 def test_utc_offset_out_of_bounds(self):
1946 class Edgy(tzinfo):
1947 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00001948 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00001949 def utcoffset(self, dt):
1950 return self.offset
1951
1952 cls = self.theclass
1953 for offset, legit in ((-1440, False),
1954 (-1439, True),
1955 (1439, True),
1956 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00001957 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00001958 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001959 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00001960 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00001961 else:
1962 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00001963 if legit:
1964 aofs = abs(offset)
1965 h, m = divmod(aofs, 60)
1966 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001967 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00001968 t = t.timetz()
1969 self.assertEqual(str(t), "01:02:03" + tag)
1970 else:
1971 self.assertRaises(ValueError, str, t)
1972
1973 def test_tzinfo_classes(self):
1974 cls = self.theclass
1975 class C1(tzinfo):
1976 def utcoffset(self, dt): return None
1977 def dst(self, dt): return None
1978 def tzname(self, dt): return None
1979 for t in (cls(1, 1, 1),
1980 cls(1, 1, 1, tzinfo=None),
1981 cls(1, 1, 1, tzinfo=C1())):
1982 self.failUnless(t.utcoffset() is None)
1983 self.failUnless(t.dst() is None)
1984 self.failUnless(t.tzname() is None)
1985
Tim Peters855fe882002-12-22 03:43:39 +00001986 class C3(tzinfo):
1987 def utcoffset(self, dt): return timedelta(minutes=-1439)
1988 def dst(self, dt): return timedelta(minutes=1439)
1989 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001990 t = cls(1, 1, 1, tzinfo=C3())
1991 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
1992 self.assertEqual(t.dst(), timedelta(minutes=1439))
1993 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00001994
1995 # Wrong types.
1996 class C4(tzinfo):
1997 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00001998 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00001999 def tzname(self, dt): return 0
2000 t = cls(1, 1, 1, tzinfo=C4())
2001 self.assertRaises(TypeError, t.utcoffset)
2002 self.assertRaises(TypeError, t.dst)
2003 self.assertRaises(TypeError, t.tzname)
2004
2005 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002006 class C6(tzinfo):
2007 def utcoffset(self, dt): return timedelta(hours=-24)
2008 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002009 t = cls(1, 1, 1, tzinfo=C6())
2010 self.assertRaises(ValueError, t.utcoffset)
2011 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002012
2013 # Not a whole number of minutes.
2014 class C7(tzinfo):
2015 def utcoffset(self, dt): return timedelta(seconds=61)
2016 def dst(self, dt): return timedelta(microseconds=-81)
2017 t = cls(1, 1, 1, tzinfo=C7())
2018 self.assertRaises(ValueError, t.utcoffset)
2019 self.assertRaises(ValueError, t.dst)
2020
Tim Peters4c0db782002-12-26 05:01:19 +00002021 def test_aware_compare(self):
2022 cls = self.theclass
2023
Tim Peters60c76e42002-12-27 00:41:11 +00002024 # Ensure that utcoffset() gets ignored if the comparands have
2025 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002026 class OperandDependentOffset(tzinfo):
2027 def utcoffset(self, t):
2028 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002029 # d0 and d1 equal after adjustment
2030 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002031 else:
Tim Peters397301e2003-01-02 21:28:08 +00002032 # d2 off in the weeds
2033 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002034
2035 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2036 d0 = base.replace(minute=3)
2037 d1 = base.replace(minute=9)
2038 d2 = base.replace(minute=11)
2039 for x in d0, d1, d2:
2040 for y in d0, d1, d2:
2041 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002042 expected = cmp(x.minute, y.minute)
2043 self.assertEqual(got, expected)
2044
2045 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002046 # Note that a time can't actually have an operand-depedent offset,
2047 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2048 # so skip this test for time.
2049 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002050 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2051 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2052 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2053 for x in d0, d1, d2:
2054 for y in d0, d1, d2:
2055 got = cmp(x, y)
2056 if (x is d0 or x is d1) and (y is d0 or y is d1):
2057 expected = 0
2058 elif x is y is d2:
2059 expected = 0
2060 elif x is d2:
2061 expected = -1
2062 else:
2063 assert y is d2
2064 expected = 1
2065 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002066
Tim Peters855fe882002-12-22 03:43:39 +00002067
Tim Peters0bf60bd2003-01-08 20:40:01 +00002068# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002069class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002070 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002071
2072 def test_empty(self):
2073 t = self.theclass()
2074 self.assertEqual(t.hour, 0)
2075 self.assertEqual(t.minute, 0)
2076 self.assertEqual(t.second, 0)
2077 self.assertEqual(t.microsecond, 0)
2078 self.failUnless(t.tzinfo is None)
2079
Tim Peters2a799bf2002-12-16 20:18:38 +00002080 def test_zones(self):
2081 est = FixedOffset(-300, "EST", 1)
2082 utc = FixedOffset(0, "UTC", -2)
2083 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002084 t1 = time( 7, 47, tzinfo=est)
2085 t2 = time(12, 47, tzinfo=utc)
2086 t3 = time(13, 47, tzinfo=met)
2087 t4 = time(microsecond=40)
2088 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002089
2090 self.assertEqual(t1.tzinfo, est)
2091 self.assertEqual(t2.tzinfo, utc)
2092 self.assertEqual(t3.tzinfo, met)
2093 self.failUnless(t4.tzinfo is None)
2094 self.assertEqual(t5.tzinfo, utc)
2095
Tim Peters855fe882002-12-22 03:43:39 +00002096 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2097 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2098 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002099 self.failUnless(t4.utcoffset() is None)
2100 self.assertRaises(TypeError, t1.utcoffset, "no args")
2101
2102 self.assertEqual(t1.tzname(), "EST")
2103 self.assertEqual(t2.tzname(), "UTC")
2104 self.assertEqual(t3.tzname(), "MET")
2105 self.failUnless(t4.tzname() is None)
2106 self.assertRaises(TypeError, t1.tzname, "no args")
2107
Tim Peters855fe882002-12-22 03:43:39 +00002108 self.assertEqual(t1.dst(), timedelta(minutes=1))
2109 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2110 self.assertEqual(t3.dst(), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +00002111 self.failUnless(t4.dst() is None)
2112 self.assertRaises(TypeError, t1.dst, "no args")
2113
2114 self.assertEqual(hash(t1), hash(t2))
2115 self.assertEqual(hash(t1), hash(t3))
2116 self.assertEqual(hash(t2), hash(t3))
2117
2118 self.assertEqual(t1, t2)
2119 self.assertEqual(t1, t3)
2120 self.assertEqual(t2, t3)
2121 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2122 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2123 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2124
2125 self.assertEqual(str(t1), "07:47:00-05:00")
2126 self.assertEqual(str(t2), "12:47:00+00:00")
2127 self.assertEqual(str(t3), "13:47:00+01:00")
2128 self.assertEqual(str(t4), "00:00:00.000040")
2129 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2130
2131 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2132 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2133 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2134 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2135 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2136
Tim Peters0bf60bd2003-01-08 20:40:01 +00002137 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002138 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2139 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2140 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2141 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2142 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2143
2144 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2145 "07:47:00 %Z=EST %z=-0500")
2146 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2147 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2148
2149 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002150 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002151 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2152 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2153
Tim Petersb92bb712002-12-21 17:44:07 +00002154 # Check that an invalid tzname result raises an exception.
2155 class Badtzname(tzinfo):
2156 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002157 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002158 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2159 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002160
2161 def test_hash_edge_cases(self):
2162 # Offsets that overflow a basic time.
2163 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2164 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2165 self.assertEqual(hash(t1), hash(t2))
2166
2167 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2168 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2169 self.assertEqual(hash(t1), hash(t2))
2170
Tim Peters2a799bf2002-12-16 20:18:38 +00002171 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002172 # Try one without a tzinfo.
2173 args = 20, 59, 16, 64**2
2174 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002175 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002176 green = pickler.dumps(orig, proto)
2177 derived = unpickler.loads(green)
2178 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002179
2180 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002181 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002182 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002183 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002184 green = pickler.dumps(orig, proto)
2185 derived = unpickler.loads(green)
2186 self.assertEqual(orig, derived)
2187 self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
2188 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2189 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002190
2191 def test_more_bool(self):
2192 # Test cases with non-None tzinfo.
2193 cls = self.theclass
2194
2195 t = cls(0, tzinfo=FixedOffset(-300, ""))
2196 self.failUnless(t)
2197
2198 t = cls(5, tzinfo=FixedOffset(-300, ""))
2199 self.failUnless(t)
2200
2201 t = cls(5, tzinfo=FixedOffset(300, ""))
2202 self.failUnless(not t)
2203
2204 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2205 self.failUnless(not t)
2206
2207 # Mostly ensuring this doesn't overflow internally.
2208 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2209 self.failUnless(t)
2210
2211 # But this should yield a value error -- the utcoffset is bogus.
2212 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2213 self.assertRaises(ValueError, lambda: bool(t))
2214
2215 # Likewise.
2216 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2217 self.assertRaises(ValueError, lambda: bool(t))
2218
Tim Peters12bf3392002-12-24 05:41:27 +00002219 def test_replace(self):
2220 cls = self.theclass
2221 z100 = FixedOffset(100, "+100")
2222 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2223 args = [1, 2, 3, 4, z100]
2224 base = cls(*args)
2225 self.assertEqual(base, base.replace())
2226
2227 i = 0
2228 for name, newval in (("hour", 5),
2229 ("minute", 6),
2230 ("second", 7),
2231 ("microsecond", 8),
2232 ("tzinfo", zm200)):
2233 newargs = args[:]
2234 newargs[i] = newval
2235 expected = cls(*newargs)
2236 got = base.replace(**{name: newval})
2237 self.assertEqual(expected, got)
2238 i += 1
2239
2240 # Ensure we can get rid of a tzinfo.
2241 self.assertEqual(base.tzname(), "+100")
2242 base2 = base.replace(tzinfo=None)
2243 self.failUnless(base2.tzinfo is None)
2244 self.failUnless(base2.tzname() is None)
2245
2246 # Ensure we can add one.
2247 base3 = base2.replace(tzinfo=z100)
2248 self.assertEqual(base, base3)
2249 self.failUnless(base.tzinfo is base3.tzinfo)
2250
2251 # Out of bounds.
2252 base = cls(1)
2253 self.assertRaises(ValueError, base.replace, hour=24)
2254 self.assertRaises(ValueError, base.replace, minute=-1)
2255 self.assertRaises(ValueError, base.replace, second=100)
2256 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2257
Tim Peters60c76e42002-12-27 00:41:11 +00002258 def test_mixed_compare(self):
2259 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002260 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002261 self.assertEqual(t1, t2)
2262 t2 = t2.replace(tzinfo=None)
2263 self.assertEqual(t1, t2)
2264 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2265 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002266 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2267 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002268
Tim Peters0bf60bd2003-01-08 20:40:01 +00002269 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002270 class Varies(tzinfo):
2271 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002272 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002273 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002274 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002275 return self.offset
2276
2277 v = Varies()
2278 t1 = t2.replace(tzinfo=v)
2279 t2 = t2.replace(tzinfo=v)
2280 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2281 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2282 self.assertEqual(t1, t2)
2283
2284 # But if they're not identical, it isn't ignored.
2285 t2 = t2.replace(tzinfo=Varies())
2286 self.failUnless(t1 < t2) # t1's offset counter still going up
2287
Tim Petersa98924a2003-05-17 05:55:19 +00002288 def test_subclass_timetz(self):
2289
2290 class C(self.theclass):
2291 theAnswer = 42
2292
2293 def __new__(cls, *args, **kws):
2294 temp = kws.copy()
2295 extra = temp.pop('extra')
2296 result = self.theclass.__new__(cls, *args, **temp)
2297 result.extra = extra
2298 return result
2299
2300 def newmeth(self, start):
2301 return start + self.hour + self.second
2302
2303 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2304
2305 dt1 = self.theclass(*args)
2306 dt2 = C(*args, **{'extra': 7})
2307
2308 self.assertEqual(dt2.__class__, C)
2309 self.assertEqual(dt2.theAnswer, 42)
2310 self.assertEqual(dt2.extra, 7)
2311 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2312 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2313
Tim Peters4c0db782002-12-26 05:01:19 +00002314
Tim Peters0bf60bd2003-01-08 20:40:01 +00002315# Testing datetime objects with a non-None tzinfo.
2316
Guido van Rossumd8faa362007-04-27 19:54:29 +00002317class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002318 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002319
2320 def test_trivial(self):
2321 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2322 self.assertEqual(dt.year, 1)
2323 self.assertEqual(dt.month, 2)
2324 self.assertEqual(dt.day, 3)
2325 self.assertEqual(dt.hour, 4)
2326 self.assertEqual(dt.minute, 5)
2327 self.assertEqual(dt.second, 6)
2328 self.assertEqual(dt.microsecond, 7)
2329 self.assertEqual(dt.tzinfo, None)
2330
2331 def test_even_more_compare(self):
2332 # The test_compare() and test_more_compare() inherited from TestDate
2333 # and TestDateTime covered non-tzinfo cases.
2334
2335 # Smallest possible after UTC adjustment.
2336 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2337 # Largest possible after UTC adjustment.
2338 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2339 tzinfo=FixedOffset(-1439, ""))
2340
2341 # Make sure those compare correctly, and w/o overflow.
2342 self.failUnless(t1 < t2)
2343 self.failUnless(t1 != t2)
2344 self.failUnless(t2 > t1)
2345
2346 self.failUnless(t1 == t1)
2347 self.failUnless(t2 == t2)
2348
2349 # Equal afer adjustment.
2350 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2351 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2352 self.assertEqual(t1, t2)
2353
2354 # Change t1 not to subtract a minute, and t1 should be larger.
2355 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2356 self.failUnless(t1 > t2)
2357
2358 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2359 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2360 self.failUnless(t1 < t2)
2361
2362 # Back to the original t1, but make seconds resolve it.
2363 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2364 second=1)
2365 self.failUnless(t1 > t2)
2366
2367 # Likewise, but make microseconds resolve it.
2368 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2369 microsecond=1)
2370 self.failUnless(t1 > t2)
2371
2372 # Make t2 naive and it should fail.
2373 t2 = self.theclass.min
2374 self.assertRaises(TypeError, lambda: t1 == t2)
2375 self.assertEqual(t2, t2)
2376
2377 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2378 class Naive(tzinfo):
2379 def utcoffset(self, dt): return None
2380 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2381 self.assertRaises(TypeError, lambda: t1 == t2)
2382 self.assertEqual(t2, t2)
2383
2384 # OTOH, it's OK to compare two of these mixing the two ways of being
2385 # naive.
2386 t1 = self.theclass(5, 6, 7)
2387 self.assertEqual(t1, t2)
2388
2389 # Try a bogus uctoffset.
2390 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002391 def utcoffset(self, dt):
2392 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002393 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2394 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002395 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002396
Tim Peters2a799bf2002-12-16 20:18:38 +00002397 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002398 # Try one without a tzinfo.
2399 args = 6, 7, 23, 20, 59, 1, 64**2
2400 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002401 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002402 green = pickler.dumps(orig, proto)
2403 derived = unpickler.loads(green)
2404 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002405
2406 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002407 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002408 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002409 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002410 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002411 green = pickler.dumps(orig, proto)
2412 derived = unpickler.loads(green)
2413 self.assertEqual(orig, derived)
2414 self.failUnless(isinstance(derived.tzinfo,
2415 PicklableFixedOffset))
2416 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2417 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002418
2419 def test_extreme_hashes(self):
2420 # If an attempt is made to hash these via subtracting the offset
2421 # then hashing a datetime object, OverflowError results. The
2422 # Python implementation used to blow up here.
2423 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2424 hash(t)
2425 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2426 tzinfo=FixedOffset(-1439, ""))
2427 hash(t)
2428
2429 # OTOH, an OOB offset should blow up.
2430 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2431 self.assertRaises(ValueError, hash, t)
2432
2433 def test_zones(self):
2434 est = FixedOffset(-300, "EST")
2435 utc = FixedOffset(0, "UTC")
2436 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002437 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2438 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2439 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002440 self.assertEqual(t1.tzinfo, est)
2441 self.assertEqual(t2.tzinfo, utc)
2442 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002443 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2444 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2445 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002446 self.assertEqual(t1.tzname(), "EST")
2447 self.assertEqual(t2.tzname(), "UTC")
2448 self.assertEqual(t3.tzname(), "MET")
2449 self.assertEqual(hash(t1), hash(t2))
2450 self.assertEqual(hash(t1), hash(t3))
2451 self.assertEqual(hash(t2), hash(t3))
2452 self.assertEqual(t1, t2)
2453 self.assertEqual(t1, t3)
2454 self.assertEqual(t2, t3)
2455 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2456 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2457 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002458 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002459 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2460 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2461 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2462
2463 def test_combine(self):
2464 met = FixedOffset(60, "MET")
2465 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002466 tz = time(18, 45, 3, 1234, tzinfo=met)
2467 dt = datetime.combine(d, tz)
2468 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002469 tzinfo=met))
2470
2471 def test_extract(self):
2472 met = FixedOffset(60, "MET")
2473 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2474 self.assertEqual(dt.date(), date(2002, 3, 4))
2475 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002476 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002477
2478 def test_tz_aware_arithmetic(self):
2479 import random
2480
2481 now = self.theclass.now()
2482 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002483 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002484 nowaware = self.theclass.combine(now.date(), timeaware)
2485 self.failUnless(nowaware.tzinfo is tz55)
2486 self.assertEqual(nowaware.timetz(), timeaware)
2487
2488 # Can't mix aware and non-aware.
2489 self.assertRaises(TypeError, lambda: now - nowaware)
2490 self.assertRaises(TypeError, lambda: nowaware - now)
2491
Tim Peters0bf60bd2003-01-08 20:40:01 +00002492 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002493 self.assertRaises(TypeError, lambda: now + nowaware)
2494 self.assertRaises(TypeError, lambda: nowaware + now)
2495 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2496
2497 # Subtracting should yield 0.
2498 self.assertEqual(now - now, timedelta(0))
2499 self.assertEqual(nowaware - nowaware, timedelta(0))
2500
2501 # Adding a delta should preserve tzinfo.
2502 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2503 nowawareplus = nowaware + delta
2504 self.failUnless(nowaware.tzinfo is tz55)
2505 nowawareplus2 = delta + nowaware
2506 self.failUnless(nowawareplus2.tzinfo is tz55)
2507 self.assertEqual(nowawareplus, nowawareplus2)
2508
2509 # that - delta should be what we started with, and that - what we
2510 # started with should be delta.
2511 diff = nowawareplus - delta
2512 self.failUnless(diff.tzinfo is tz55)
2513 self.assertEqual(nowaware, diff)
2514 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2515 self.assertEqual(nowawareplus - nowaware, delta)
2516
2517 # Make up a random timezone.
2518 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002519 # Attach it to nowawareplus.
2520 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002521 self.failUnless(nowawareplus.tzinfo is tzr)
2522 # Make sure the difference takes the timezone adjustments into account.
2523 got = nowaware - nowawareplus
2524 # Expected: (nowaware base - nowaware offset) -
2525 # (nowawareplus base - nowawareplus offset) =
2526 # (nowaware base - nowawareplus base) +
2527 # (nowawareplus offset - nowaware offset) =
2528 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002529 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002530 self.assertEqual(got, expected)
2531
2532 # Try max possible difference.
2533 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2534 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2535 tzinfo=FixedOffset(-1439, "max"))
2536 maxdiff = max - min
2537 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2538 timedelta(minutes=2*1439))
2539
2540 def test_tzinfo_now(self):
2541 meth = self.theclass.now
2542 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2543 base = meth()
2544 # Try with and without naming the keyword.
2545 off42 = FixedOffset(42, "42")
2546 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002547 again = meth(tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002548 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002549 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002550 # Bad argument with and w/o naming the keyword.
2551 self.assertRaises(TypeError, meth, 16)
2552 self.assertRaises(TypeError, meth, tzinfo=16)
2553 # Bad keyword name.
2554 self.assertRaises(TypeError, meth, tinfo=off42)
2555 # Too many args.
2556 self.assertRaises(TypeError, meth, off42, off42)
2557
Tim Peters10cadce2003-01-23 19:58:02 +00002558 # We don't know which time zone we're in, and don't have a tzinfo
2559 # class to represent it, so seeing whether a tz argument actually
2560 # does a conversion is tricky.
2561 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2562 utc = FixedOffset(0, "utc", 0)
2563 for dummy in range(3):
2564 now = datetime.now(weirdtz)
2565 self.failUnless(now.tzinfo is weirdtz)
2566 utcnow = datetime.utcnow().replace(tzinfo=utc)
2567 now2 = utcnow.astimezone(weirdtz)
2568 if abs(now - now2) < timedelta(seconds=30):
2569 break
2570 # Else the code is broken, or more than 30 seconds passed between
2571 # calls; assuming the latter, just try again.
2572 else:
2573 # Three strikes and we're out.
2574 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2575
Tim Peters2a799bf2002-12-16 20:18:38 +00002576 def test_tzinfo_fromtimestamp(self):
2577 import time
2578 meth = self.theclass.fromtimestamp
2579 ts = time.time()
2580 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2581 base = meth(ts)
2582 # Try with and without naming the keyword.
2583 off42 = FixedOffset(42, "42")
2584 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002585 again = meth(ts, tz=off42)
Tim Peters2a799bf2002-12-16 20:18:38 +00002586 self.failUnless(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002587 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002588 # Bad argument with and w/o naming the keyword.
2589 self.assertRaises(TypeError, meth, ts, 16)
2590 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2591 # Bad keyword name.
2592 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2593 # Too many args.
2594 self.assertRaises(TypeError, meth, ts, off42, off42)
2595 # Too few args.
2596 self.assertRaises(TypeError, meth)
2597
Tim Peters2a44a8d2003-01-23 20:53:10 +00002598 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002599 timestamp = 1000000000
2600 utcdatetime = datetime.utcfromtimestamp(timestamp)
2601 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2602 # But on some flavor of Mac, it's nowhere near that. So we can't have
2603 # any idea here what time that actually is, we can only test that
2604 # relative changes match.
2605 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2606 tz = FixedOffset(utcoffset, "tz", 0)
2607 expected = utcdatetime + utcoffset
2608 got = datetime.fromtimestamp(timestamp, tz)
2609 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002610
Tim Peters2a799bf2002-12-16 20:18:38 +00002611 def test_tzinfo_utcnow(self):
2612 meth = self.theclass.utcnow
2613 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2614 base = meth()
2615 # Try with and without naming the keyword; for whatever reason,
2616 # utcnow() doesn't accept a tzinfo argument.
2617 off42 = FixedOffset(42, "42")
2618 self.assertRaises(TypeError, meth, off42)
2619 self.assertRaises(TypeError, meth, tzinfo=off42)
2620
2621 def test_tzinfo_utcfromtimestamp(self):
2622 import time
2623 meth = self.theclass.utcfromtimestamp
2624 ts = time.time()
2625 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2626 base = meth(ts)
2627 # Try with and without naming the keyword; for whatever reason,
2628 # utcfromtimestamp() doesn't accept a tzinfo argument.
2629 off42 = FixedOffset(42, "42")
2630 self.assertRaises(TypeError, meth, ts, off42)
2631 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2632
2633 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002634 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002635 # DST flag.
2636 class DST(tzinfo):
2637 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002638 if isinstance(dstvalue, int):
2639 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002640 self.dstvalue = dstvalue
2641 def dst(self, dt):
2642 return self.dstvalue
2643
2644 cls = self.theclass
2645 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2646 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2647 t = d.timetuple()
2648 self.assertEqual(1, t.tm_year)
2649 self.assertEqual(1, t.tm_mon)
2650 self.assertEqual(1, t.tm_mday)
2651 self.assertEqual(10, t.tm_hour)
2652 self.assertEqual(20, t.tm_min)
2653 self.assertEqual(30, t.tm_sec)
2654 self.assertEqual(0, t.tm_wday)
2655 self.assertEqual(1, t.tm_yday)
2656 self.assertEqual(flag, t.tm_isdst)
2657
2658 # dst() returns wrong type.
2659 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2660
2661 # dst() at the edge.
2662 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2663 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2664
2665 # dst() out of range.
2666 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2667 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2668
2669 def test_utctimetuple(self):
2670 class DST(tzinfo):
2671 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002672 if isinstance(dstvalue, int):
2673 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002674 self.dstvalue = dstvalue
2675 def dst(self, dt):
2676 return self.dstvalue
2677
2678 cls = self.theclass
2679 # This can't work: DST didn't implement utcoffset.
2680 self.assertRaises(NotImplementedError,
2681 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2682
2683 class UOFS(DST):
2684 def __init__(self, uofs, dofs=None):
2685 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002686 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002687 def utcoffset(self, dt):
2688 return self.uofs
2689
2690 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2691 # in effect for a UTC time.
2692 for dstvalue in -33, 33, 0, None:
2693 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2694 t = d.utctimetuple()
2695 self.assertEqual(d.year, t.tm_year)
2696 self.assertEqual(d.month, t.tm_mon)
2697 self.assertEqual(d.day, t.tm_mday)
2698 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2699 self.assertEqual(13, t.tm_min)
2700 self.assertEqual(d.second, t.tm_sec)
2701 self.assertEqual(d.weekday(), t.tm_wday)
2702 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2703 t.tm_yday)
2704 self.assertEqual(0, t.tm_isdst)
2705
2706 # At the edges, UTC adjustment can normalize into years out-of-range
2707 # for a datetime object. Ensure that a correct timetuple is
2708 # created anyway.
2709 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2710 # That goes back 1 minute less than a full day.
2711 t = tiny.utctimetuple()
2712 self.assertEqual(t.tm_year, MINYEAR-1)
2713 self.assertEqual(t.tm_mon, 12)
2714 self.assertEqual(t.tm_mday, 31)
2715 self.assertEqual(t.tm_hour, 0)
2716 self.assertEqual(t.tm_min, 1)
2717 self.assertEqual(t.tm_sec, 37)
2718 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2719 self.assertEqual(t.tm_isdst, 0)
2720
2721 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2722 # That goes forward 1 minute less than a full day.
2723 t = huge.utctimetuple()
2724 self.assertEqual(t.tm_year, MAXYEAR+1)
2725 self.assertEqual(t.tm_mon, 1)
2726 self.assertEqual(t.tm_mday, 1)
2727 self.assertEqual(t.tm_hour, 23)
2728 self.assertEqual(t.tm_min, 58)
2729 self.assertEqual(t.tm_sec, 37)
2730 self.assertEqual(t.tm_yday, 1)
2731 self.assertEqual(t.tm_isdst, 0)
2732
2733 def test_tzinfo_isoformat(self):
2734 zero = FixedOffset(0, "+00:00")
2735 plus = FixedOffset(220, "+03:40")
2736 minus = FixedOffset(-231, "-03:51")
2737 unknown = FixedOffset(None, "")
2738
2739 cls = self.theclass
2740 datestr = '0001-02-03'
2741 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002742 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002743 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2744 timestr = '04:05:59' + (us and '.987001' or '')
2745 ofsstr = ofs is not None and d.tzname() or ''
2746 tailstr = timestr + ofsstr
2747 iso = d.isoformat()
2748 self.assertEqual(iso, datestr + 'T' + tailstr)
2749 self.assertEqual(iso, d.isoformat('T'))
2750 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002751 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002752 self.assertEqual(str(d), datestr + ' ' + tailstr)
2753
Tim Peters12bf3392002-12-24 05:41:27 +00002754 def test_replace(self):
2755 cls = self.theclass
2756 z100 = FixedOffset(100, "+100")
2757 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2758 args = [1, 2, 3, 4, 5, 6, 7, z100]
2759 base = cls(*args)
2760 self.assertEqual(base, base.replace())
2761
2762 i = 0
2763 for name, newval in (("year", 2),
2764 ("month", 3),
2765 ("day", 4),
2766 ("hour", 5),
2767 ("minute", 6),
2768 ("second", 7),
2769 ("microsecond", 8),
2770 ("tzinfo", zm200)):
2771 newargs = args[:]
2772 newargs[i] = newval
2773 expected = cls(*newargs)
2774 got = base.replace(**{name: newval})
2775 self.assertEqual(expected, got)
2776 i += 1
2777
2778 # Ensure we can get rid of a tzinfo.
2779 self.assertEqual(base.tzname(), "+100")
2780 base2 = base.replace(tzinfo=None)
2781 self.failUnless(base2.tzinfo is None)
2782 self.failUnless(base2.tzname() is None)
2783
2784 # Ensure we can add one.
2785 base3 = base2.replace(tzinfo=z100)
2786 self.assertEqual(base, base3)
2787 self.failUnless(base.tzinfo is base3.tzinfo)
2788
2789 # Out of bounds.
2790 base = cls(2000, 2, 29)
2791 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002792
Tim Peters80475bb2002-12-25 07:40:55 +00002793 def test_more_astimezone(self):
2794 # The inherited test_astimezone covered some trivial and error cases.
2795 fnone = FixedOffset(None, "None")
2796 f44m = FixedOffset(44, "44")
2797 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2798
Tim Peters10cadce2003-01-23 19:58:02 +00002799 dt = self.theclass.now(tz=f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002800 self.failUnless(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002801 # Replacing with degenerate tzinfo raises an exception.
2802 self.assertRaises(ValueError, dt.astimezone, fnone)
2803 # Ditto with None tz.
2804 self.assertRaises(TypeError, dt.astimezone, None)
2805 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002806 x = dt.astimezone(dt.tzinfo)
2807 self.failUnless(x.tzinfo is f44m)
2808 self.assertEqual(x.date(), dt.date())
2809 self.assertEqual(x.time(), dt.time())
2810
2811 # Replacing with different tzinfo does adjust.
2812 got = dt.astimezone(fm5h)
2813 self.failUnless(got.tzinfo is fm5h)
2814 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2815 expected = dt - dt.utcoffset() # in effect, convert to UTC
2816 expected += fm5h.utcoffset(dt) # and from there to local time
2817 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2818 self.assertEqual(got.date(), expected.date())
2819 self.assertEqual(got.time(), expected.time())
2820 self.assertEqual(got.timetz(), expected.timetz())
2821 self.failUnless(got.tzinfo is expected.tzinfo)
2822 self.assertEqual(got, expected)
2823
Tim Peters4c0db782002-12-26 05:01:19 +00002824 def test_aware_subtract(self):
2825 cls = self.theclass
2826
Tim Peters60c76e42002-12-27 00:41:11 +00002827 # Ensure that utcoffset() is ignored when the operands have the
2828 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002829 class OperandDependentOffset(tzinfo):
2830 def utcoffset(self, t):
2831 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002832 # d0 and d1 equal after adjustment
2833 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002834 else:
Tim Peters397301e2003-01-02 21:28:08 +00002835 # d2 off in the weeds
2836 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002837
2838 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2839 d0 = base.replace(minute=3)
2840 d1 = base.replace(minute=9)
2841 d2 = base.replace(minute=11)
2842 for x in d0, d1, d2:
2843 for y in d0, d1, d2:
2844 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002845 expected = timedelta(minutes=x.minute - y.minute)
2846 self.assertEqual(got, expected)
2847
2848 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2849 # ignored.
2850 base = cls(8, 9, 10, 11, 12, 13, 14)
2851 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2852 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2853 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2854 for x in d0, d1, d2:
2855 for y in d0, d1, d2:
2856 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002857 if (x is d0 or x is d1) and (y is d0 or y is d1):
2858 expected = timedelta(0)
2859 elif x is y is d2:
2860 expected = timedelta(0)
2861 elif x is d2:
2862 expected = timedelta(minutes=(11-59)-0)
2863 else:
2864 assert y is d2
2865 expected = timedelta(minutes=0-(11-59))
2866 self.assertEqual(got, expected)
2867
Tim Peters60c76e42002-12-27 00:41:11 +00002868 def test_mixed_compare(self):
2869 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002870 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002871 self.assertEqual(t1, t2)
2872 t2 = t2.replace(tzinfo=None)
2873 self.assertEqual(t1, t2)
2874 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2875 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002876 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2877 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002878
Tim Peters0bf60bd2003-01-08 20:40:01 +00002879 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002880 class Varies(tzinfo):
2881 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002882 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002883 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002884 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002885 return self.offset
2886
2887 v = Varies()
2888 t1 = t2.replace(tzinfo=v)
2889 t2 = t2.replace(tzinfo=v)
2890 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2891 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2892 self.assertEqual(t1, t2)
2893
2894 # But if they're not identical, it isn't ignored.
2895 t2 = t2.replace(tzinfo=Varies())
2896 self.failUnless(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002897
Tim Petersa98924a2003-05-17 05:55:19 +00002898 def test_subclass_datetimetz(self):
2899
2900 class C(self.theclass):
2901 theAnswer = 42
2902
2903 def __new__(cls, *args, **kws):
2904 temp = kws.copy()
2905 extra = temp.pop('extra')
2906 result = self.theclass.__new__(cls, *args, **temp)
2907 result.extra = extra
2908 return result
2909
2910 def newmeth(self, start):
2911 return start + self.hour + self.year
2912
2913 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2914
2915 dt1 = self.theclass(*args)
2916 dt2 = C(*args, **{'extra': 7})
2917
2918 self.assertEqual(dt2.__class__, C)
2919 self.assertEqual(dt2.theAnswer, 42)
2920 self.assertEqual(dt2.extra, 7)
2921 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2922 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2923
Tim Peters621818b2002-12-29 23:44:49 +00002924# Pain to set up DST-aware tzinfo classes.
2925
2926def first_sunday_on_or_after(dt):
2927 days_to_go = 6 - dt.weekday()
2928 if days_to_go:
2929 dt += timedelta(days_to_go)
2930 return dt
2931
2932ZERO = timedelta(0)
2933HOUR = timedelta(hours=1)
2934DAY = timedelta(days=1)
2935# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2936DSTSTART = datetime(1, 4, 1, 2)
2937# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002938# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2939# being standard time on that day, there is no spelling in local time of
2940# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2941DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002942
2943class USTimeZone(tzinfo):
2944
2945 def __init__(self, hours, reprname, stdname, dstname):
2946 self.stdoffset = timedelta(hours=hours)
2947 self.reprname = reprname
2948 self.stdname = stdname
2949 self.dstname = dstname
2950
2951 def __repr__(self):
2952 return self.reprname
2953
2954 def tzname(self, dt):
2955 if self.dst(dt):
2956 return self.dstname
2957 else:
2958 return self.stdname
2959
2960 def utcoffset(self, dt):
2961 return self.stdoffset + self.dst(dt)
2962
2963 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00002964 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00002965 # An exception instead may be sensible here, in one or more of
2966 # the cases.
2967 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00002968 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00002969
2970 # Find first Sunday in April.
2971 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
2972 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
2973
2974 # Find last Sunday in October.
2975 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
2976 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
2977
Tim Peters621818b2002-12-29 23:44:49 +00002978 # Can't compare naive to aware objects, so strip the timezone from
2979 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00002980 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00002981 return HOUR
2982 else:
2983 return ZERO
2984
Tim Peters521fc152002-12-31 17:36:56 +00002985Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
2986Central = USTimeZone(-6, "Central", "CST", "CDT")
2987Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
2988Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00002989utc_real = FixedOffset(0, "UTC", 0)
2990# For better test coverage, we want another flavor of UTC that's west of
2991# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00002992utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00002993
2994class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00002995 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002996 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00002997 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00002998
Tim Peters0bf60bd2003-01-08 20:40:01 +00002999 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003000
Tim Peters521fc152002-12-31 17:36:56 +00003001 # Check a time that's inside DST.
3002 def checkinside(self, dt, tz, utc, dston, dstoff):
3003 self.assertEqual(dt.dst(), HOUR)
3004
3005 # Conversion to our own timezone is always an identity.
3006 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003007
3008 asutc = dt.astimezone(utc)
3009 there_and_back = asutc.astimezone(tz)
3010
3011 # Conversion to UTC and back isn't always an identity here,
3012 # because there are redundant spellings (in local time) of
3013 # UTC time when DST begins: the clock jumps from 1:59:59
3014 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3015 # make sense then. The classes above treat 2:MM:SS as
3016 # daylight time then (it's "after 2am"), really an alias
3017 # for 1:MM:SS standard time. The latter form is what
3018 # conversion back from UTC produces.
3019 if dt.date() == dston.date() and dt.hour == 2:
3020 # We're in the redundant hour, and coming back from
3021 # UTC gives the 1:MM:SS standard-time spelling.
3022 self.assertEqual(there_and_back + HOUR, dt)
3023 # Although during was considered to be in daylight
3024 # time, there_and_back is not.
3025 self.assertEqual(there_and_back.dst(), ZERO)
3026 # They're the same times in UTC.
3027 self.assertEqual(there_and_back.astimezone(utc),
3028 dt.astimezone(utc))
3029 else:
3030 # We're not in the redundant hour.
3031 self.assertEqual(dt, there_and_back)
3032
Tim Peters327098a2003-01-20 22:54:38 +00003033 # Because we have a redundant spelling when DST begins, there is
3034 # (unforunately) an hour when DST ends that can't be spelled at all in
3035 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3036 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3037 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3038 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3039 # expressed in local time. Nevertheless, we want conversion back
3040 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003041 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003042 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003043 if dt.date() == dstoff.date() and dt.hour == 0:
3044 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003045 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003046 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3047 nexthour_utc += HOUR
3048 nexthour_tz = nexthour_utc.astimezone(tz)
3049 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003050 else:
Tim Peters327098a2003-01-20 22:54:38 +00003051 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003052
3053 # Check a time that's outside DST.
3054 def checkoutside(self, dt, tz, utc):
3055 self.assertEqual(dt.dst(), ZERO)
3056
3057 # Conversion to our own timezone is always an identity.
3058 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003059
3060 # Converting to UTC and back is an identity too.
3061 asutc = dt.astimezone(utc)
3062 there_and_back = asutc.astimezone(tz)
3063 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003064
Tim Peters1024bf82002-12-30 17:09:40 +00003065 def convert_between_tz_and_utc(self, tz, utc):
3066 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003067 # Because 1:MM on the day DST ends is taken as being standard time,
3068 # there is no spelling in tz for the last hour of daylight time.
3069 # For purposes of the test, the last hour of DST is 0:MM, which is
3070 # taken as being daylight time (and 1:MM is taken as being standard
3071 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003072 dstoff = self.dstoff.replace(tzinfo=tz)
3073 for delta in (timedelta(weeks=13),
3074 DAY,
3075 HOUR,
3076 timedelta(minutes=1),
3077 timedelta(microseconds=1)):
3078
Tim Peters521fc152002-12-31 17:36:56 +00003079 self.checkinside(dston, tz, utc, dston, dstoff)
3080 for during in dston + delta, dstoff - delta:
3081 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003082
Tim Peters521fc152002-12-31 17:36:56 +00003083 self.checkoutside(dstoff, tz, utc)
3084 for outside in dston - delta, dstoff + delta:
3085 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003086
Tim Peters621818b2002-12-29 23:44:49 +00003087 def test_easy(self):
3088 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003089 self.convert_between_tz_and_utc(Eastern, utc_real)
3090 self.convert_between_tz_and_utc(Pacific, utc_real)
3091 self.convert_between_tz_and_utc(Eastern, utc_fake)
3092 self.convert_between_tz_and_utc(Pacific, utc_fake)
3093 # The next is really dancing near the edge. It works because
3094 # Pacific and Eastern are far enough apart that their "problem
3095 # hours" don't overlap.
3096 self.convert_between_tz_and_utc(Eastern, Pacific)
3097 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003098 # OTOH, these fail! Don't enable them. The difficulty is that
3099 # the edge case tests assume that every hour is representable in
3100 # the "utc" class. This is always true for a fixed-offset tzinfo
3101 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3102 # For these adjacent DST-aware time zones, the range of time offsets
3103 # tested ends up creating hours in the one that aren't representable
3104 # in the other. For the same reason, we would see failures in the
3105 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3106 # offset deltas in convert_between_tz_and_utc().
3107 #
3108 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3109 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003110
Tim Petersf3615152003-01-01 21:51:37 +00003111 def test_tricky(self):
3112 # 22:00 on day before daylight starts.
3113 fourback = self.dston - timedelta(hours=4)
3114 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003115 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003116 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3117 # 2", we should get the 3 spelling.
3118 # If we plug 22:00 the day before into Eastern, it "looks like std
3119 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3120 # to 22:00 lands on 2:00, which makes no sense in local time (the
3121 # local clock jumps from 1 to 3). The point here is to make sure we
3122 # get the 3 spelling.
3123 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003124 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003125 self.assertEqual(expected, got)
3126
3127 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3128 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003129 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003130 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3131 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3132 # spelling.
3133 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003134 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003135 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003136
Tim Petersadf64202003-01-04 06:03:15 +00003137 # Now on the day DST ends, we want "repeat an hour" behavior.
3138 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3139 # EST 23:MM 0:MM 1:MM 2:MM
3140 # EDT 0:MM 1:MM 2:MM 3:MM
3141 # wall 0:MM 1:MM 1:MM 2:MM against these
3142 for utc in utc_real, utc_fake:
3143 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003144 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003145 # Convert that to UTC.
3146 first_std_hour -= tz.utcoffset(None)
3147 # Adjust for possibly fake UTC.
3148 asutc = first_std_hour + utc.utcoffset(None)
3149 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3150 # tz=Eastern.
3151 asutcbase = asutc.replace(tzinfo=utc)
3152 for tzhour in (0, 1, 1, 2):
3153 expectedbase = self.dstoff.replace(hour=tzhour)
3154 for minute in 0, 30, 59:
3155 expected = expectedbase.replace(minute=minute)
3156 asutc = asutcbase.replace(minute=minute)
3157 astz = asutc.astimezone(tz)
3158 self.assertEqual(astz.replace(tzinfo=None), expected)
3159 asutcbase += HOUR
3160
3161
Tim Peters710fb152003-01-02 19:35:54 +00003162 def test_bogus_dst(self):
3163 class ok(tzinfo):
3164 def utcoffset(self, dt): return HOUR
3165 def dst(self, dt): return HOUR
3166
3167 now = self.theclass.now().replace(tzinfo=utc_real)
3168 # Doesn't blow up.
3169 now.astimezone(ok())
3170
3171 # Does blow up.
3172 class notok(ok):
3173 def dst(self, dt): return None
3174 self.assertRaises(ValueError, now.astimezone, notok())
3175
Tim Peters52dcce22003-01-23 16:36:11 +00003176 def test_fromutc(self):
3177 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3178 now = datetime.utcnow().replace(tzinfo=utc_real)
3179 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3180 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3181 enow = Eastern.fromutc(now) # doesn't blow up
3182 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3183 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3184 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3185
3186 # Always converts UTC to standard time.
3187 class FauxUSTimeZone(USTimeZone):
3188 def fromutc(self, dt):
3189 return dt + self.stdoffset
3190 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3191
3192 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3193 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3194 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3195
3196 # Check around DST start.
3197 start = self.dston.replace(hour=4, tzinfo=Eastern)
3198 fstart = start.replace(tzinfo=FEastern)
3199 for wall in 23, 0, 1, 3, 4, 5:
3200 expected = start.replace(hour=wall)
3201 if wall == 23:
3202 expected -= timedelta(days=1)
3203 got = Eastern.fromutc(start)
3204 self.assertEqual(expected, got)
3205
3206 expected = fstart + FEastern.stdoffset
3207 got = FEastern.fromutc(fstart)
3208 self.assertEqual(expected, got)
3209
3210 # Ensure astimezone() calls fromutc() too.
3211 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3212 self.assertEqual(expected, got)
3213
3214 start += HOUR
3215 fstart += HOUR
3216
3217 # Check around DST end.
3218 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3219 fstart = start.replace(tzinfo=FEastern)
3220 for wall in 0, 1, 1, 2, 3, 4:
3221 expected = start.replace(hour=wall)
3222 got = Eastern.fromutc(start)
3223 self.assertEqual(expected, got)
3224
3225 expected = fstart + FEastern.stdoffset
3226 got = FEastern.fromutc(fstart)
3227 self.assertEqual(expected, got)
3228
3229 # Ensure astimezone() calls fromutc() too.
3230 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3231 self.assertEqual(expected, got)
3232
3233 start += HOUR
3234 fstart += HOUR
3235
Tim Peters710fb152003-01-02 19:35:54 +00003236
Tim Peters528ca532004-09-16 01:30:50 +00003237#############################################################################
3238# oddballs
3239
3240class Oddballs(unittest.TestCase):
3241
3242 def test_bug_1028306(self):
3243 # Trying to compare a date to a datetime should act like a mixed-
3244 # type comparison, despite that datetime is a subclass of date.
3245 as_date = date.today()
3246 as_datetime = datetime.combine(as_date, time())
3247 self.assert_(as_date != as_datetime)
3248 self.assert_(as_datetime != as_date)
3249 self.assert_(not as_date == as_datetime)
3250 self.assert_(not as_datetime == as_date)
3251 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3252 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3253 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3254 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3255 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3256 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3257 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3258 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3259
3260 # Neverthelss, comparison should work with the base-class (date)
3261 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003262 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003263 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003264 as_different = as_datetime.replace(day= different_day)
3265 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003266
3267 # And date should compare with other subclasses of date. If a
3268 # subclass wants to stop this, it's up to the subclass to do so.
3269 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3270 self.assertEqual(as_date, date_sc)
3271 self.assertEqual(date_sc, as_date)
3272
3273 # Ditto for datetimes.
3274 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3275 as_date.day, 0, 0, 0)
3276 self.assertEqual(as_datetime, datetime_sc)
3277 self.assertEqual(datetime_sc, as_datetime)
3278
Tim Peters2a799bf2002-12-16 20:18:38 +00003279def test_main():
Guido van Rossumd8faa362007-04-27 19:54:29 +00003280 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003281
3282if __name__ == "__main__":
3283 test_main()