blob: c5d268ffff32b74e1fd53b51b801ae729b7f5a3e [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 Rossumf1200f82007-03-07 15:16:29 +00006import os
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
8import cPickle
Tim Peters2a799bf2002-12-16 20:18:38 +00009import unittest
10
11from test import test_support
12
13from datetime import MINYEAR, MAXYEAR
14from datetime import timedelta
15from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000016from datetime import time
17from datetime import date, datetime
18
Tim Peters35ad6412003-02-05 04:08:07 +000019pickle_choices = [(pickler, unpickler, proto)
20 for pickler in pickle, cPickle
21 for unpickler in pickle, cPickle
22 for proto in range(3)]
23assert len(pickle_choices) == 2*2*3
Guido van Rossum177e41a2003-01-30 22:06:23 +000024
Tim Peters68124bb2003-02-08 03:46:31 +000025# An arbitrary collection of objects of non-datetime types, for testing
26# mixed-type comparisons.
27OTHERSTUFF = (10, 10L, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000028
Tim Peters2a799bf2002-12-16 20:18:38 +000029
30#############################################################################
31# module tests
32
33class TestModule(unittest.TestCase):
34
35 def test_constants(self):
36 import datetime
37 self.assertEqual(datetime.MINYEAR, 1)
38 self.assertEqual(datetime.MAXYEAR, 9999)
39
40#############################################################################
41# tzinfo tests
42
43class FixedOffset(tzinfo):
44 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000045 if isinstance(offset, int):
46 offset = timedelta(minutes=offset)
47 if isinstance(dstoffset, int):
48 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000049 self.__offset = offset
50 self.__name = name
51 self.__dstoffset = dstoffset
52 def __repr__(self):
53 return self.__name.lower()
54 def utcoffset(self, dt):
55 return self.__offset
56 def tzname(self, dt):
57 return self.__name
58 def dst(self, dt):
59 return self.__dstoffset
60
Tim Petersfb8472c2002-12-21 05:04:42 +000061class PicklableFixedOffset(FixedOffset):
62 def __init__(self, offset=None, name=None, dstoffset=None):
63 FixedOffset.__init__(self, offset, name, dstoffset)
64
Tim Peters2a799bf2002-12-16 20:18:38 +000065class TestTZInfo(unittest.TestCase):
66
67 def test_non_abstractness(self):
68 # In order to allow subclasses to get pickled, the C implementation
69 # wasn't able to get away with having __init__ raise
70 # NotImplementedError.
71 useless = tzinfo()
72 dt = datetime.max
73 self.assertRaises(NotImplementedError, useless.tzname, dt)
74 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
75 self.assertRaises(NotImplementedError, useless.dst, dt)
76
77 def test_subclass_must_override(self):
78 class NotEnough(tzinfo):
79 def __init__(self, offset, name):
80 self.__offset = offset
81 self.__name = name
Benjamin Peterson5c8da862009-06-30 22:57:08 +000082 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000083 ne = NotEnough(3, "NotByALongShot")
Benjamin Peterson5c8da862009-06-30 22:57:08 +000084 self.assertTrue(isinstance(ne, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000085
86 dt = datetime.now()
87 self.assertRaises(NotImplementedError, ne.tzname, dt)
88 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
89 self.assertRaises(NotImplementedError, ne.dst, dt)
90
91 def test_normal(self):
92 fo = FixedOffset(3, "Three")
Benjamin Peterson5c8da862009-06-30 22:57:08 +000093 self.assertTrue(isinstance(fo, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000094 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +000095 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +000096 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +000097 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +000098
99 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000100 # There's no point to pickling tzinfo objects on their own (they
101 # carry no data), but they need to be picklable anyway else
102 # concrete subclasses can't be pickled.
103 orig = tzinfo.__new__(tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000104 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000105 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000106 green = pickler.dumps(orig, proto)
107 derived = unpickler.loads(green)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000108 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000109
110 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000111 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000112 offset = timedelta(minutes=-300)
113 orig = PicklableFixedOffset(offset, 'cookie')
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000114 self.assertTrue(isinstance(orig, tzinfo))
115 self.assertTrue(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000116 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000117 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000118 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000119 green = pickler.dumps(orig, proto)
120 derived = unpickler.loads(green)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000121 self.assertTrue(isinstance(derived, tzinfo))
122 self.assertTrue(type(derived) is PicklableFixedOffset)
Tim Petersf2715e02003-02-19 02:35:07 +0000123 self.assertEqual(derived.utcoffset(None), offset)
124 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000125
126#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000127# Base clase for testing a particular aspect of timedelta, time, date and
128# datetime comparisons.
129
Collin Winterc2898c52007-04-25 17:29:52 +0000130class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000131 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
132
133 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
134 # legit constructor.
135
136 def test_harmless_mixed_comparison(self):
137 me = self.theclass(1, 1, 1)
138
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000139 self.assertFalse(me == ())
140 self.assertTrue(me != ())
141 self.assertFalse(() == me)
142 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000143
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000144 self.assertTrue(me in [1, 20L, [], me])
145 self.assertFalse(me not in [1, 20L, [], me])
Tim Peters07534a62003-02-07 22:50:28 +0000146
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000147 self.assertTrue([] in [me, 1, 20L, []])
148 self.assertFalse([] not in [me, 1, 20L, []])
Tim Peters07534a62003-02-07 22:50:28 +0000149
150 def test_harmful_mixed_comparison(self):
151 me = self.theclass(1, 1, 1)
152
153 self.assertRaises(TypeError, lambda: me < ())
154 self.assertRaises(TypeError, lambda: me <= ())
155 self.assertRaises(TypeError, lambda: me > ())
156 self.assertRaises(TypeError, lambda: me >= ())
157
158 self.assertRaises(TypeError, lambda: () < me)
159 self.assertRaises(TypeError, lambda: () <= me)
160 self.assertRaises(TypeError, lambda: () > me)
161 self.assertRaises(TypeError, lambda: () >= me)
162
163 self.assertRaises(TypeError, cmp, (), me)
164 self.assertRaises(TypeError, cmp, me, ())
165
166#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000167# timedelta tests
168
Collin Winterc2898c52007-04-25 17:29:52 +0000169class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000170
171 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000172
173 def test_constructor(self):
174 eq = self.assertEqual
175 td = timedelta
176
177 # Check keyword args to constructor
178 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
179 milliseconds=0, microseconds=0))
180 eq(td(1), td(days=1))
181 eq(td(0, 1), td(seconds=1))
182 eq(td(0, 0, 1), td(microseconds=1))
183 eq(td(weeks=1), td(days=7))
184 eq(td(days=1), td(hours=24))
185 eq(td(hours=1), td(minutes=60))
186 eq(td(minutes=1), td(seconds=60))
187 eq(td(seconds=1), td(milliseconds=1000))
188 eq(td(milliseconds=1), td(microseconds=1000))
189
190 # Check float args to constructor
191 eq(td(weeks=1.0/7), td(days=1))
192 eq(td(days=1.0/24), td(hours=1))
193 eq(td(hours=1.0/60), td(minutes=1))
194 eq(td(minutes=1.0/60), td(seconds=1))
195 eq(td(seconds=0.001), td(milliseconds=1))
196 eq(td(milliseconds=0.001), td(microseconds=1))
197
198 def test_computations(self):
199 eq = self.assertEqual
200 td = timedelta
201
202 a = td(7) # One week
203 b = td(0, 60) # One minute
204 c = td(0, 0, 1000) # One millisecond
205 eq(a+b+c, td(7, 60, 1000))
206 eq(a-b, td(6, 24*3600 - 60))
207 eq(-a, td(-7))
208 eq(+a, td(7))
209 eq(-b, td(-1, 24*3600 - 60))
210 eq(-c, td(-1, 24*3600 - 1, 999000))
211 eq(abs(a), a)
212 eq(abs(-a), a)
213 eq(td(6, 24*3600), a)
214 eq(td(0, 0, 60*1000000), b)
215 eq(a*10, td(70))
216 eq(a*10, 10*a)
217 eq(a*10L, 10*a)
218 eq(b*10, td(0, 600))
219 eq(10*b, td(0, 600))
220 eq(b*10L, td(0, 600))
221 eq(c*10, td(0, 0, 10000))
222 eq(10*c, td(0, 0, 10000))
223 eq(c*10L, td(0, 0, 10000))
224 eq(a*-1, -a)
225 eq(b*-2, -b-b)
226 eq(c*-2, -c+-c)
227 eq(b*(60*24), (b*60)*24)
228 eq(b*(60*24), (60*b)*24)
229 eq(c*1000, td(0, 1))
230 eq(1000*c, td(0, 1))
231 eq(a//7, td(1))
232 eq(b//10, td(0, 6))
233 eq(c//1000, td(0, 0, 1))
234 eq(a//10, td(0, 7*24*360))
235 eq(a//3600000, td(0, 0, 7*24*1000))
236
237 def test_disallowed_computations(self):
238 a = timedelta(42)
239
240 # Add/sub ints, longs, floats should be illegal
241 for i in 1, 1L, 1.0:
242 self.assertRaises(TypeError, lambda: a+i)
243 self.assertRaises(TypeError, lambda: a-i)
244 self.assertRaises(TypeError, lambda: i+a)
245 self.assertRaises(TypeError, lambda: i-a)
246
247 # Mul/div by float isn't supported.
248 x = 2.3
249 self.assertRaises(TypeError, lambda: a*x)
250 self.assertRaises(TypeError, lambda: x*a)
251 self.assertRaises(TypeError, lambda: a/x)
252 self.assertRaises(TypeError, lambda: x/a)
253 self.assertRaises(TypeError, lambda: a // x)
254 self.assertRaises(TypeError, lambda: x // a)
255
Andrew M. Kuchling081bb452008-10-03 16:42:52 +0000256 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000257 # Division by zero doesn't make sense.
258 for zero in 0, 0L:
259 self.assertRaises(TypeError, lambda: zero // a)
260 self.assertRaises(ZeroDivisionError, lambda: a // zero)
261
262 def test_basic_attributes(self):
263 days, seconds, us = 1, 7, 31
264 td = timedelta(days, seconds, us)
265 self.assertEqual(td.days, days)
266 self.assertEqual(td.seconds, seconds)
267 self.assertEqual(td.microseconds, us)
268
Antoine Pitroubcfaf802009-11-25 22:59:36 +0000269 def test_total_seconds(self):
270 td = timedelta(days=365)
271 self.assertEqual(td.total_seconds(), 31536000.0)
272 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
273 td = timedelta(seconds=total_seconds)
274 self.assertEqual(td.total_seconds(), total_seconds)
275
Tim Peters2a799bf2002-12-16 20:18:38 +0000276 def test_carries(self):
277 t1 = timedelta(days=100,
278 weeks=-7,
279 hours=-24*(100-49),
280 minutes=-3,
281 seconds=12,
282 microseconds=(3*60 - 12) * 1e6 + 1)
283 t2 = timedelta(microseconds=1)
284 self.assertEqual(t1, t2)
285
286 def test_hash_equality(self):
287 t1 = timedelta(days=100,
288 weeks=-7,
289 hours=-24*(100-49),
290 minutes=-3,
291 seconds=12,
292 microseconds=(3*60 - 12) * 1000000)
293 t2 = timedelta()
294 self.assertEqual(hash(t1), hash(t2))
295
296 t1 += timedelta(weeks=7)
297 t2 += timedelta(days=7*7)
298 self.assertEqual(t1, t2)
299 self.assertEqual(hash(t1), hash(t2))
300
301 d = {t1: 1}
302 d[t2] = 2
303 self.assertEqual(len(d), 1)
304 self.assertEqual(d[t1], 2)
305
306 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000307 args = 12, 34, 56
308 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000309 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000310 green = pickler.dumps(orig, proto)
311 derived = unpickler.loads(green)
312 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000313
314 def test_compare(self):
315 t1 = timedelta(2, 3, 4)
316 t2 = timedelta(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000317 self.assertTrue(t1 == t2)
318 self.assertTrue(t1 <= t2)
319 self.assertTrue(t1 >= t2)
320 self.assertTrue(not t1 != t2)
321 self.assertTrue(not t1 < t2)
322 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000323 self.assertEqual(cmp(t1, t2), 0)
324 self.assertEqual(cmp(t2, t1), 0)
325
326 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
327 t2 = timedelta(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000328 self.assertTrue(t1 < t2)
329 self.assertTrue(t2 > t1)
330 self.assertTrue(t1 <= t2)
331 self.assertTrue(t2 >= t1)
332 self.assertTrue(t1 != t2)
333 self.assertTrue(t2 != t1)
334 self.assertTrue(not t1 == t2)
335 self.assertTrue(not t2 == t1)
336 self.assertTrue(not t1 > t2)
337 self.assertTrue(not t2 < t1)
338 self.assertTrue(not t1 >= t2)
339 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000340 self.assertEqual(cmp(t1, t2), -1)
341 self.assertEqual(cmp(t2, t1), 1)
342
Tim Peters68124bb2003-02-08 03:46:31 +0000343 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000344 self.assertEqual(t1 == badarg, False)
345 self.assertEqual(t1 != badarg, True)
346 self.assertEqual(badarg == t1, False)
347 self.assertEqual(badarg != t1, True)
348
Tim Peters2a799bf2002-12-16 20:18:38 +0000349 self.assertRaises(TypeError, lambda: t1 <= badarg)
350 self.assertRaises(TypeError, lambda: t1 < badarg)
351 self.assertRaises(TypeError, lambda: t1 > badarg)
352 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000353 self.assertRaises(TypeError, lambda: badarg <= t1)
354 self.assertRaises(TypeError, lambda: badarg < t1)
355 self.assertRaises(TypeError, lambda: badarg > t1)
356 self.assertRaises(TypeError, lambda: badarg >= t1)
357
358 def test_str(self):
359 td = timedelta
360 eq = self.assertEqual
361
362 eq(str(td(1)), "1 day, 0:00:00")
363 eq(str(td(-1)), "-1 day, 0:00:00")
364 eq(str(td(2)), "2 days, 0:00:00")
365 eq(str(td(-2)), "-2 days, 0:00:00")
366
367 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
368 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
369 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
370 "-210 days, 23:12:34")
371
372 eq(str(td(milliseconds=1)), "0:00:00.001000")
373 eq(str(td(microseconds=3)), "0:00:00.000003")
374
375 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
376 microseconds=999999)),
377 "999999999 days, 23:59:59.999999")
378
379 def test_roundtrip(self):
380 for td in (timedelta(days=999999999, hours=23, minutes=59,
381 seconds=59, microseconds=999999),
382 timedelta(days=-999999999),
383 timedelta(days=1, seconds=2, microseconds=3)):
384
385 # Verify td -> string -> td identity.
386 s = repr(td)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000387 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000388 s = s[9:]
389 td2 = eval(s)
390 self.assertEqual(td, td2)
391
392 # Verify identity via reconstructing from pieces.
393 td2 = timedelta(td.days, td.seconds, td.microseconds)
394 self.assertEqual(td, td2)
395
396 def test_resolution_info(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000397 self.assertTrue(isinstance(timedelta.min, timedelta))
398 self.assertTrue(isinstance(timedelta.max, timedelta))
399 self.assertTrue(isinstance(timedelta.resolution, timedelta))
400 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000401 self.assertEqual(timedelta.min, timedelta(-999999999))
402 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
403 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
404
405 def test_overflow(self):
406 tiny = timedelta.resolution
407
408 td = timedelta.min + tiny
409 td -= tiny # no problem
410 self.assertRaises(OverflowError, td.__sub__, tiny)
411 self.assertRaises(OverflowError, td.__add__, -tiny)
412
413 td = timedelta.max - tiny
414 td += tiny # no problem
415 self.assertRaises(OverflowError, td.__add__, tiny)
416 self.assertRaises(OverflowError, td.__sub__, -tiny)
417
418 self.assertRaises(OverflowError, lambda: -timedelta.max)
419
420 def test_microsecond_rounding(self):
421 td = timedelta
422 eq = self.assertEqual
423
424 # Single-field rounding.
425 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
426 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
427 eq(td(milliseconds=0.6/1000), td(microseconds=1))
428 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
429
430 # Rounding due to contributions from more than one field.
431 us_per_hour = 3600e6
432 us_per_day = us_per_hour * 24
433 eq(td(days=.4/us_per_day), td(0))
434 eq(td(hours=.2/us_per_hour), td(0))
435 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
436
437 eq(td(days=-.4/us_per_day), td(0))
438 eq(td(hours=-.2/us_per_hour), td(0))
439 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
440
441 def test_massive_normalization(self):
442 td = timedelta(microseconds=-1)
443 self.assertEqual((td.days, td.seconds, td.microseconds),
444 (-1, 24*3600-1, 999999))
445
446 def test_bool(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000447 self.assertTrue(timedelta(1))
448 self.assertTrue(timedelta(0, 1))
449 self.assertTrue(timedelta(0, 0, 1))
450 self.assertTrue(timedelta(microseconds=1))
451 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000452
Tim Petersb0c854d2003-05-17 15:57:00 +0000453 def test_subclass_timedelta(self):
454
455 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000456 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000457 def from_td(td):
458 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000459
460 def as_hours(self):
461 sum = (self.days * 24 +
462 self.seconds / 3600.0 +
463 self.microseconds / 3600e6)
464 return round(sum)
465
466 t1 = T(days=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000467 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000468 self.assertEqual(t1.as_hours(), 24)
469
470 t2 = T(days=-1, seconds=-3600)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000471 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000472 self.assertEqual(t2.as_hours(), -25)
473
474 t3 = t1 + t2
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000475 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000476 t4 = T.from_td(t3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000477 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000478 self.assertEqual(t3.days, t4.days)
479 self.assertEqual(t3.seconds, t4.seconds)
480 self.assertEqual(t3.microseconds, t4.microseconds)
481 self.assertEqual(str(t3), str(t4))
482 self.assertEqual(t4.as_hours(), -1)
483
Tim Peters2a799bf2002-12-16 20:18:38 +0000484#############################################################################
485# date tests
486
487class TestDateOnly(unittest.TestCase):
488 # Tests here won't pass if also run on datetime objects, so don't
489 # subclass this to test datetimes too.
490
491 def test_delta_non_days_ignored(self):
492 dt = date(2000, 1, 2)
493 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
494 microseconds=5)
495 days = timedelta(delta.days)
496 self.assertEqual(days, timedelta(1))
497
498 dt2 = dt + delta
499 self.assertEqual(dt2, dt + days)
500
501 dt2 = delta + dt
502 self.assertEqual(dt2, dt + days)
503
504 dt2 = dt - delta
505 self.assertEqual(dt2, dt - days)
506
507 delta = -delta
508 days = timedelta(delta.days)
509 self.assertEqual(days, timedelta(-2))
510
511 dt2 = dt + delta
512 self.assertEqual(dt2, dt + days)
513
514 dt2 = delta + dt
515 self.assertEqual(dt2, dt + days)
516
517 dt2 = dt - delta
518 self.assertEqual(dt2, dt - days)
519
Tim Peters604c0132004-06-07 23:04:33 +0000520class SubclassDate(date):
521 sub_var = 1
522
Collin Winterc2898c52007-04-25 17:29:52 +0000523class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000524 # Tests here should pass for both dates and datetimes, except for a
525 # few tests that TestDateTime overrides.
526
527 theclass = date
528
529 def test_basic_attributes(self):
530 dt = self.theclass(2002, 3, 1)
531 self.assertEqual(dt.year, 2002)
532 self.assertEqual(dt.month, 3)
533 self.assertEqual(dt.day, 1)
534
535 def test_roundtrip(self):
536 for dt in (self.theclass(1, 2, 3),
537 self.theclass.today()):
538 # Verify dt -> string -> date identity.
539 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000540 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000541 s = s[9:]
542 dt2 = eval(s)
543 self.assertEqual(dt, dt2)
544
545 # Verify identity via reconstructing from pieces.
546 dt2 = self.theclass(dt.year, dt.month, dt.day)
547 self.assertEqual(dt, dt2)
548
549 def test_ordinal_conversions(self):
550 # Check some fixed values.
551 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
552 (1, 12, 31, 365),
553 (2, 1, 1, 366),
554 # first example from "Calendrical Calculations"
555 (1945, 11, 12, 710347)]:
556 d = self.theclass(y, m, d)
557 self.assertEqual(n, d.toordinal())
558 fromord = self.theclass.fromordinal(n)
559 self.assertEqual(d, fromord)
560 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000561 # if we're checking something fancier than a date, verify
562 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000563 self.assertEqual(fromord.hour, 0)
564 self.assertEqual(fromord.minute, 0)
565 self.assertEqual(fromord.second, 0)
566 self.assertEqual(fromord.microsecond, 0)
567
Tim Peters0bf60bd2003-01-08 20:40:01 +0000568 # Check first and last days of year spottily across the whole
569 # range of years supported.
570 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000571 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
572 d = self.theclass(year, 1, 1)
573 n = d.toordinal()
574 d2 = self.theclass.fromordinal(n)
575 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000576 # Verify that moving back a day gets to the end of year-1.
577 if year > 1:
578 d = self.theclass.fromordinal(n-1)
579 d2 = self.theclass(year-1, 12, 31)
580 self.assertEqual(d, d2)
581 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000582
583 # Test every day in a leap-year and a non-leap year.
584 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
585 for year, isleap in (2000, True), (2002, False):
586 n = self.theclass(year, 1, 1).toordinal()
587 for month, maxday in zip(range(1, 13), dim):
588 if month == 2 and isleap:
589 maxday += 1
590 for day in range(1, maxday+1):
591 d = self.theclass(year, month, day)
592 self.assertEqual(d.toordinal(), n)
593 self.assertEqual(d, self.theclass.fromordinal(n))
594 n += 1
595
596 def test_extreme_ordinals(self):
597 a = self.theclass.min
598 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
599 aord = a.toordinal()
600 b = a.fromordinal(aord)
601 self.assertEqual(a, b)
602
603 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
604
605 b = a + timedelta(days=1)
606 self.assertEqual(b.toordinal(), aord + 1)
607 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
608
609 a = self.theclass.max
610 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
611 aord = a.toordinal()
612 b = a.fromordinal(aord)
613 self.assertEqual(a, b)
614
615 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
616
617 b = a - timedelta(days=1)
618 self.assertEqual(b.toordinal(), aord - 1)
619 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
620
621 def test_bad_constructor_arguments(self):
622 # bad years
623 self.theclass(MINYEAR, 1, 1) # no exception
624 self.theclass(MAXYEAR, 1, 1) # no exception
625 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
626 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
627 # bad months
628 self.theclass(2000, 1, 1) # no exception
629 self.theclass(2000, 12, 1) # no exception
630 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
631 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
632 # bad days
633 self.theclass(2000, 2, 29) # no exception
634 self.theclass(2004, 2, 29) # no exception
635 self.theclass(2400, 2, 29) # no exception
636 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
637 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
638 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
639 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
640 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
641 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
642
643 def test_hash_equality(self):
644 d = self.theclass(2000, 12, 31)
645 # same thing
646 e = self.theclass(2000, 12, 31)
647 self.assertEqual(d, e)
648 self.assertEqual(hash(d), hash(e))
649
650 dic = {d: 1}
651 dic[e] = 2
652 self.assertEqual(len(dic), 1)
653 self.assertEqual(dic[d], 2)
654 self.assertEqual(dic[e], 2)
655
656 d = self.theclass(2001, 1, 1)
657 # same thing
658 e = self.theclass(2001, 1, 1)
659 self.assertEqual(d, e)
660 self.assertEqual(hash(d), hash(e))
661
662 dic = {d: 1}
663 dic[e] = 2
664 self.assertEqual(len(dic), 1)
665 self.assertEqual(dic[d], 2)
666 self.assertEqual(dic[e], 2)
667
668 def test_computations(self):
669 a = self.theclass(2002, 1, 31)
670 b = self.theclass(1956, 1, 31)
671
672 diff = a-b
673 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
674 self.assertEqual(diff.seconds, 0)
675 self.assertEqual(diff.microseconds, 0)
676
677 day = timedelta(1)
678 week = timedelta(7)
679 a = self.theclass(2002, 3, 2)
680 self.assertEqual(a + day, self.theclass(2002, 3, 3))
681 self.assertEqual(day + a, self.theclass(2002, 3, 3))
682 self.assertEqual(a - day, self.theclass(2002, 3, 1))
683 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
684 self.assertEqual(a + week, self.theclass(2002, 3, 9))
685 self.assertEqual(a - week, self.theclass(2002, 2, 23))
686 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
687 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
688 self.assertEqual((a + week) - a, week)
689 self.assertEqual((a + day) - a, day)
690 self.assertEqual((a - week) - a, -week)
691 self.assertEqual((a - day) - a, -day)
692 self.assertEqual(a - (a + week), -week)
693 self.assertEqual(a - (a + day), -day)
694 self.assertEqual(a - (a - week), week)
695 self.assertEqual(a - (a - day), day)
696
697 # Add/sub ints, longs, floats should be illegal
698 for i in 1, 1L, 1.0:
699 self.assertRaises(TypeError, lambda: a+i)
700 self.assertRaises(TypeError, lambda: a-i)
701 self.assertRaises(TypeError, lambda: i+a)
702 self.assertRaises(TypeError, lambda: i-a)
703
704 # delta - date is senseless.
705 self.assertRaises(TypeError, lambda: day - a)
706 # mixing date and (delta or date) via * or // is senseless
707 self.assertRaises(TypeError, lambda: day * a)
708 self.assertRaises(TypeError, lambda: a * day)
709 self.assertRaises(TypeError, lambda: day // a)
710 self.assertRaises(TypeError, lambda: a // day)
711 self.assertRaises(TypeError, lambda: a * a)
712 self.assertRaises(TypeError, lambda: a // a)
713 # date + date is senseless
714 self.assertRaises(TypeError, lambda: a + a)
715
716 def test_overflow(self):
717 tiny = self.theclass.resolution
718
719 dt = self.theclass.min + tiny
720 dt -= tiny # no problem
721 self.assertRaises(OverflowError, dt.__sub__, tiny)
722 self.assertRaises(OverflowError, dt.__add__, -tiny)
723
724 dt = self.theclass.max - tiny
725 dt += tiny # no problem
726 self.assertRaises(OverflowError, dt.__add__, tiny)
727 self.assertRaises(OverflowError, dt.__sub__, -tiny)
728
729 def test_fromtimestamp(self):
730 import time
731
732 # Try an arbitrary fixed value.
733 year, month, day = 1999, 9, 19
734 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
735 d = self.theclass.fromtimestamp(ts)
736 self.assertEqual(d.year, year)
737 self.assertEqual(d.month, month)
738 self.assertEqual(d.day, day)
739
Tim Peters1b6f7a92004-06-20 02:50:16 +0000740 def test_insane_fromtimestamp(self):
741 # It's possible that some platform maps time_t to double,
742 # and that this test will fail there. This test should
743 # exempt such platforms (provided they return reasonable
744 # results!).
745 for insane in -1e200, 1e200:
746 self.assertRaises(ValueError, self.theclass.fromtimestamp,
747 insane)
748
Tim Peters2a799bf2002-12-16 20:18:38 +0000749 def test_today(self):
750 import time
751
752 # We claim that today() is like fromtimestamp(time.time()), so
753 # prove it.
754 for dummy in range(3):
755 today = self.theclass.today()
756 ts = time.time()
757 todayagain = self.theclass.fromtimestamp(ts)
758 if today == todayagain:
759 break
760 # There are several legit reasons that could fail:
761 # 1. It recently became midnight, between the today() and the
762 # time() calls.
763 # 2. The platform time() has such fine resolution that we'll
764 # never get the same value twice.
765 # 3. The platform time() has poor resolution, and we just
766 # happened to call today() right before a resolution quantum
767 # boundary.
768 # 4. The system clock got fiddled between calls.
769 # In any case, wait a little while and try again.
770 time.sleep(0.1)
771
772 # It worked or it didn't. If it didn't, assume it's reason #2, and
773 # let the test pass if they're within half a second of each other.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000774 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000775 abs(todayagain - today) < timedelta(seconds=0.5))
776
777 def test_weekday(self):
778 for i in range(7):
779 # March 4, 2002 is a Monday
780 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
781 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
782 # January 2, 1956 is a Monday
783 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
784 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
785
786 def test_isocalendar(self):
787 # Check examples from
788 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
789 for i in range(7):
790 d = self.theclass(2003, 12, 22+i)
791 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
792 d = self.theclass(2003, 12, 29) + timedelta(i)
793 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
794 d = self.theclass(2004, 1, 5+i)
795 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
796 d = self.theclass(2009, 12, 21+i)
797 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
798 d = self.theclass(2009, 12, 28) + timedelta(i)
799 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
800 d = self.theclass(2010, 1, 4+i)
801 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
802
803 def test_iso_long_years(self):
804 # Calculate long ISO years and compare to table from
805 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
806 ISO_LONG_YEARS_TABLE = """
807 4 32 60 88
808 9 37 65 93
809 15 43 71 99
810 20 48 76
811 26 54 82
812
813 105 133 161 189
814 111 139 167 195
815 116 144 172
816 122 150 178
817 128 156 184
818
819 201 229 257 285
820 207 235 263 291
821 212 240 268 296
822 218 246 274
823 224 252 280
824
825 303 331 359 387
826 308 336 364 392
827 314 342 370 398
828 320 348 376
829 325 353 381
830 """
831 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
832 iso_long_years.sort()
833 L = []
834 for i in range(400):
835 d = self.theclass(2000+i, 12, 31)
836 d1 = self.theclass(1600+i, 12, 31)
837 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
838 if d.isocalendar()[1] == 53:
839 L.append(i)
840 self.assertEqual(L, iso_long_years)
841
842 def test_isoformat(self):
843 t = self.theclass(2, 3, 2)
844 self.assertEqual(t.isoformat(), "0002-03-02")
845
846 def test_ctime(self):
847 t = self.theclass(2002, 3, 2)
848 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
849
850 def test_strftime(self):
851 t = self.theclass(2005, 3, 2)
852 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000853 self.assertEqual(t.strftime(""), "") # SF bug #761337
Georg Brandl4ddfcd32006-09-30 11:17:34 +0000854 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000855
856 self.assertRaises(TypeError, t.strftime) # needs an arg
857 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
858 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
859
Gregory P. Smith137d8242008-06-02 04:05:52 +0000860 # test that unicode input is allowed (issue 2782)
861 self.assertEqual(t.strftime(u"%m"), "03")
862
Tim Peters2a799bf2002-12-16 20:18:38 +0000863 # A naive object replaces %z and %Z w/ empty strings.
864 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
865
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000866 #make sure that invalid format specifiers are handled correctly
Kristján Valur Jónsson8adc0b52009-01-15 09:09:13 +0000867 #self.assertRaises(ValueError, t.strftime, "%e")
868 #self.assertRaises(ValueError, t.strftime, "%")
869 #self.assertRaises(ValueError, t.strftime, "%#")
870
871 #oh well, some systems just ignore those invalid ones.
872 #at least, excercise them to make sure that no crashes
873 #are generated
874 for f in ["%e", "%", "%#"]:
875 try:
876 t.strftime(f)
877 except ValueError:
878 pass
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000879
880 #check that this standard extension works
881 t.strftime("%f")
882
Gregory P. Smith137d8242008-06-02 04:05:52 +0000883
Eric Smitha9f7d622008-02-17 19:46:49 +0000884 def test_format(self):
885 dt = self.theclass(2007, 9, 10)
886 self.assertEqual(dt.__format__(''), str(dt))
887
888 # check that a derived class's __str__() gets called
889 class A(self.theclass):
890 def __str__(self):
891 return 'A'
892 a = A(2007, 9, 10)
893 self.assertEqual(a.__format__(''), 'A')
894
895 # check that a derived class's strftime gets called
896 class B(self.theclass):
897 def strftime(self, format_spec):
898 return 'B'
899 b = B(2007, 9, 10)
900 self.assertEqual(b.__format__(''), str(dt))
901
902 for fmt in ["m:%m d:%d y:%y",
903 "m:%m d:%d y:%y H:%H M:%M S:%S",
904 "%z %Z",
905 ]:
906 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
907 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
908 self.assertEqual(b.__format__(fmt), 'B')
909
Tim Peters2a799bf2002-12-16 20:18:38 +0000910 def test_resolution_info(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000911 self.assertTrue(isinstance(self.theclass.min, self.theclass))
912 self.assertTrue(isinstance(self.theclass.max, self.theclass))
913 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
914 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000915
916 def test_extreme_timedelta(self):
917 big = self.theclass.max - self.theclass.min
918 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
919 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
920 # n == 315537897599999999 ~= 2**58.13
921 justasbig = timedelta(0, 0, n)
922 self.assertEqual(big, justasbig)
923 self.assertEqual(self.theclass.min + big, self.theclass.max)
924 self.assertEqual(self.theclass.max - big, self.theclass.min)
925
926 def test_timetuple(self):
927 for i in range(7):
928 # January 2, 1956 is a Monday (0)
929 d = self.theclass(1956, 1, 2+i)
930 t = d.timetuple()
931 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
932 # February 1, 1956 is a Wednesday (2)
933 d = self.theclass(1956, 2, 1+i)
934 t = d.timetuple()
935 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
936 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
937 # of the year.
938 d = self.theclass(1956, 3, 1+i)
939 t = d.timetuple()
940 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
941 self.assertEqual(t.tm_year, 1956)
942 self.assertEqual(t.tm_mon, 3)
943 self.assertEqual(t.tm_mday, 1+i)
944 self.assertEqual(t.tm_hour, 0)
945 self.assertEqual(t.tm_min, 0)
946 self.assertEqual(t.tm_sec, 0)
947 self.assertEqual(t.tm_wday, (3+i)%7)
948 self.assertEqual(t.tm_yday, 61+i)
949 self.assertEqual(t.tm_isdst, -1)
950
951 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000952 args = 6, 7, 23
953 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000954 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000955 green = pickler.dumps(orig, proto)
956 derived = unpickler.loads(green)
957 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000958
959 def test_compare(self):
960 t1 = self.theclass(2, 3, 4)
961 t2 = self.theclass(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000962 self.assertTrue(t1 == t2)
963 self.assertTrue(t1 <= t2)
964 self.assertTrue(t1 >= t2)
965 self.assertTrue(not t1 != t2)
966 self.assertTrue(not t1 < t2)
967 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000968 self.assertEqual(cmp(t1, t2), 0)
969 self.assertEqual(cmp(t2, t1), 0)
970
971 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
972 t2 = self.theclass(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000973 self.assertTrue(t1 < t2)
974 self.assertTrue(t2 > t1)
975 self.assertTrue(t1 <= t2)
976 self.assertTrue(t2 >= t1)
977 self.assertTrue(t1 != t2)
978 self.assertTrue(t2 != t1)
979 self.assertTrue(not t1 == t2)
980 self.assertTrue(not t2 == t1)
981 self.assertTrue(not t1 > t2)
982 self.assertTrue(not t2 < t1)
983 self.assertTrue(not t1 >= t2)
984 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000985 self.assertEqual(cmp(t1, t2), -1)
986 self.assertEqual(cmp(t2, t1), 1)
987
Tim Peters68124bb2003-02-08 03:46:31 +0000988 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000989 self.assertEqual(t1 == badarg, False)
990 self.assertEqual(t1 != badarg, True)
991 self.assertEqual(badarg == t1, False)
992 self.assertEqual(badarg != t1, True)
993
Tim Peters2a799bf2002-12-16 20:18:38 +0000994 self.assertRaises(TypeError, lambda: t1 < badarg)
995 self.assertRaises(TypeError, lambda: t1 > badarg)
996 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000997 self.assertRaises(TypeError, lambda: badarg <= t1)
998 self.assertRaises(TypeError, lambda: badarg < t1)
999 self.assertRaises(TypeError, lambda: badarg > t1)
1000 self.assertRaises(TypeError, lambda: badarg >= t1)
1001
Tim Peters8d81a012003-01-24 22:36:34 +00001002 def test_mixed_compare(self):
1003 our = self.theclass(2000, 4, 5)
1004 self.assertRaises(TypeError, cmp, our, 1)
1005 self.assertRaises(TypeError, cmp, 1, our)
1006
1007 class AnotherDateTimeClass(object):
1008 def __cmp__(self, other):
1009 # Return "equal" so calling this can't be confused with
1010 # compare-by-address (which never says "equal" for distinct
1011 # objects).
1012 return 0
Nick Coghlan48361f52008-08-11 15:45:58 +00001013 __hash__ = None # Silence Py3k warning
Tim Peters8d81a012003-01-24 22:36:34 +00001014
1015 # This still errors, because date and datetime comparison raise
1016 # TypeError instead of NotImplemented when they don't know what to
1017 # do, in order to stop comparison from falling back to the default
1018 # compare-by-address.
1019 their = AnotherDateTimeClass()
1020 self.assertRaises(TypeError, cmp, our, their)
1021 # Oops: The next stab raises TypeError in the C implementation,
1022 # but not in the Python implementation of datetime. The difference
1023 # is due to that the Python implementation defines __cmp__ but
1024 # the C implementation defines tp_richcompare. This is more pain
1025 # to fix than it's worth, so commenting out the test.
1026 # self.assertEqual(cmp(their, our), 0)
1027
1028 # But date and datetime comparison return NotImplemented instead if the
1029 # other object has a timetuple attr. This gives the other object a
1030 # chance to do the comparison.
1031 class Comparable(AnotherDateTimeClass):
1032 def timetuple(self):
1033 return ()
1034
1035 their = Comparable()
1036 self.assertEqual(cmp(our, their), 0)
1037 self.assertEqual(cmp(their, our), 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001038 self.assertTrue(our == their)
1039 self.assertTrue(their == our)
Tim Peters8d81a012003-01-24 22:36:34 +00001040
Tim Peters2a799bf2002-12-16 20:18:38 +00001041 def test_bool(self):
1042 # All dates are considered true.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001043 self.assertTrue(self.theclass.min)
1044 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001045
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001046 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001047 # For nasty technical reasons, we can't handle years before 1900.
1048 cls = self.theclass
1049 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1050 for y in 1, 49, 51, 99, 100, 1000, 1899:
1051 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001052
1053 def test_replace(self):
1054 cls = self.theclass
1055 args = [1, 2, 3]
1056 base = cls(*args)
1057 self.assertEqual(base, base.replace())
1058
1059 i = 0
1060 for name, newval in (("year", 2),
1061 ("month", 3),
1062 ("day", 4)):
1063 newargs = args[:]
1064 newargs[i] = newval
1065 expected = cls(*newargs)
1066 got = base.replace(**{name: newval})
1067 self.assertEqual(expected, got)
1068 i += 1
1069
1070 # Out of bounds.
1071 base = cls(2000, 2, 29)
1072 self.assertRaises(ValueError, base.replace, year=2001)
1073
Tim Petersa98924a2003-05-17 05:55:19 +00001074 def test_subclass_date(self):
1075
1076 class C(self.theclass):
1077 theAnswer = 42
1078
1079 def __new__(cls, *args, **kws):
1080 temp = kws.copy()
1081 extra = temp.pop('extra')
1082 result = self.theclass.__new__(cls, *args, **temp)
1083 result.extra = extra
1084 return result
1085
1086 def newmeth(self, start):
1087 return start + self.year + self.month
1088
1089 args = 2003, 4, 14
1090
1091 dt1 = self.theclass(*args)
1092 dt2 = C(*args, **{'extra': 7})
1093
1094 self.assertEqual(dt2.__class__, C)
1095 self.assertEqual(dt2.theAnswer, 42)
1096 self.assertEqual(dt2.extra, 7)
1097 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1098 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1099
Tim Peters604c0132004-06-07 23:04:33 +00001100 def test_pickling_subclass_date(self):
1101
1102 args = 6, 7, 23
1103 orig = SubclassDate(*args)
1104 for pickler, unpickler, proto in pickle_choices:
1105 green = pickler.dumps(orig, proto)
1106 derived = unpickler.loads(green)
1107 self.assertEqual(orig, derived)
1108
Tim Peters3f606292004-03-21 23:38:41 +00001109 def test_backdoor_resistance(self):
1110 # For fast unpickling, the constructor accepts a pickle string.
1111 # This is a low-overhead backdoor. A user can (by intent or
1112 # mistake) pass a string directly, which (if it's the right length)
1113 # will get treated like a pickle, and bypass the normal sanity
1114 # checks in the constructor. This can create insane objects.
1115 # The constructor doesn't want to burn the time to validate all
1116 # fields, but does check the month field. This stops, e.g.,
1117 # datetime.datetime('1995-03-25') from yielding an insane object.
1118 base = '1995-03-25'
1119 if not issubclass(self.theclass, datetime):
1120 base = base[:4]
1121 for month_byte in '9', chr(0), chr(13), '\xff':
1122 self.assertRaises(TypeError, self.theclass,
1123 base[:2] + month_byte + base[3:])
1124 for ord_byte in range(1, 13):
1125 # This shouldn't blow up because of the month byte alone. If
1126 # the implementation changes to do more-careful checking, it may
1127 # blow up because other fields are insane.
1128 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001129
Tim Peters2a799bf2002-12-16 20:18:38 +00001130#############################################################################
1131# datetime tests
1132
Tim Peters604c0132004-06-07 23:04:33 +00001133class SubclassDatetime(datetime):
1134 sub_var = 1
1135
Tim Peters2a799bf2002-12-16 20:18:38 +00001136class TestDateTime(TestDate):
1137
1138 theclass = datetime
1139
1140 def test_basic_attributes(self):
1141 dt = self.theclass(2002, 3, 1, 12, 0)
1142 self.assertEqual(dt.year, 2002)
1143 self.assertEqual(dt.month, 3)
1144 self.assertEqual(dt.day, 1)
1145 self.assertEqual(dt.hour, 12)
1146 self.assertEqual(dt.minute, 0)
1147 self.assertEqual(dt.second, 0)
1148 self.assertEqual(dt.microsecond, 0)
1149
1150 def test_basic_attributes_nonzero(self):
1151 # Make sure all attributes are non-zero so bugs in
1152 # bit-shifting access show up.
1153 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1154 self.assertEqual(dt.year, 2002)
1155 self.assertEqual(dt.month, 3)
1156 self.assertEqual(dt.day, 1)
1157 self.assertEqual(dt.hour, 12)
1158 self.assertEqual(dt.minute, 59)
1159 self.assertEqual(dt.second, 59)
1160 self.assertEqual(dt.microsecond, 8000)
1161
1162 def test_roundtrip(self):
1163 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1164 self.theclass.now()):
1165 # Verify dt -> string -> datetime identity.
1166 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001167 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001168 s = s[9:]
1169 dt2 = eval(s)
1170 self.assertEqual(dt, dt2)
1171
1172 # Verify identity via reconstructing from pieces.
1173 dt2 = self.theclass(dt.year, dt.month, dt.day,
1174 dt.hour, dt.minute, dt.second,
1175 dt.microsecond)
1176 self.assertEqual(dt, dt2)
1177
1178 def test_isoformat(self):
1179 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1180 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1181 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1182 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arc8645a5c2009-12-29 22:03:38 +00001183 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001184 # str is ISO format with the separator forced to a blank.
1185 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1186
1187 t = self.theclass(2, 3, 2)
1188 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1189 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1190 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1191 # str is ISO format with the separator forced to a blank.
1192 self.assertEqual(str(t), "0002-03-02 00:00:00")
1193
Eric Smitha9f7d622008-02-17 19:46:49 +00001194 def test_format(self):
1195 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1196 self.assertEqual(dt.__format__(''), str(dt))
1197
1198 # check that a derived class's __str__() gets called
1199 class A(self.theclass):
1200 def __str__(self):
1201 return 'A'
1202 a = A(2007, 9, 10, 4, 5, 1, 123)
1203 self.assertEqual(a.__format__(''), 'A')
1204
1205 # check that a derived class's strftime gets called
1206 class B(self.theclass):
1207 def strftime(self, format_spec):
1208 return 'B'
1209 b = B(2007, 9, 10, 4, 5, 1, 123)
1210 self.assertEqual(b.__format__(''), str(dt))
1211
1212 for fmt in ["m:%m d:%d y:%y",
1213 "m:%m d:%d y:%y H:%H M:%M S:%S",
1214 "%z %Z",
1215 ]:
1216 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1217 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1218 self.assertEqual(b.__format__(fmt), 'B')
1219
Tim Peters2a799bf2002-12-16 20:18:38 +00001220 def test_more_ctime(self):
1221 # Test fields that TestDate doesn't touch.
1222 import time
1223
1224 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1225 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1226 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1227 # out. The difference is that t.ctime() produces " 2" for the day,
1228 # but platform ctime() produces "02" for the day. According to
1229 # C99, t.ctime() is correct here.
1230 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1231
1232 # So test a case where that difference doesn't matter.
1233 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1234 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1235
1236 def test_tz_independent_comparing(self):
1237 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1238 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1239 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1240 self.assertEqual(dt1, dt3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001241 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001242
1243 # Make sure comparison doesn't forget microseconds, and isn't done
1244 # via comparing a float timestamp (an IEEE double doesn't have enough
1245 # precision to span microsecond resolution across years 1 thru 9999,
1246 # so comparing via timestamp necessarily calls some distinct values
1247 # equal).
1248 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1249 us = timedelta(microseconds=1)
1250 dt2 = dt1 + us
1251 self.assertEqual(dt2 - dt1, us)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001252 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001253
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001254 def test_strftime_with_bad_tzname_replace(self):
1255 # verify ok if tzinfo.tzname().replace() returns a non-string
1256 class MyTzInfo(FixedOffset):
1257 def tzname(self, dt):
1258 class MyStr(str):
1259 def replace(self, *args):
1260 return None
1261 return MyStr('name')
1262 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1263 self.assertRaises(TypeError, t.strftime, '%Z')
1264
Tim Peters2a799bf2002-12-16 20:18:38 +00001265 def test_bad_constructor_arguments(self):
1266 # bad years
1267 self.theclass(MINYEAR, 1, 1) # no exception
1268 self.theclass(MAXYEAR, 1, 1) # no exception
1269 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1270 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1271 # bad months
1272 self.theclass(2000, 1, 1) # no exception
1273 self.theclass(2000, 12, 1) # no exception
1274 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1275 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1276 # bad days
1277 self.theclass(2000, 2, 29) # no exception
1278 self.theclass(2004, 2, 29) # no exception
1279 self.theclass(2400, 2, 29) # no exception
1280 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1281 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1282 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1283 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1284 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1285 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1286 # bad hours
1287 self.theclass(2000, 1, 31, 0) # no exception
1288 self.theclass(2000, 1, 31, 23) # no exception
1289 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1290 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1291 # bad minutes
1292 self.theclass(2000, 1, 31, 23, 0) # no exception
1293 self.theclass(2000, 1, 31, 23, 59) # no exception
1294 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1295 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1296 # bad seconds
1297 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1298 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1299 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1300 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1301 # bad microseconds
1302 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1303 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1304 self.assertRaises(ValueError, self.theclass,
1305 2000, 1, 31, 23, 59, 59, -1)
1306 self.assertRaises(ValueError, self.theclass,
1307 2000, 1, 31, 23, 59, 59,
1308 1000000)
1309
1310 def test_hash_equality(self):
1311 d = self.theclass(2000, 12, 31, 23, 30, 17)
1312 e = self.theclass(2000, 12, 31, 23, 30, 17)
1313 self.assertEqual(d, e)
1314 self.assertEqual(hash(d), hash(e))
1315
1316 dic = {d: 1}
1317 dic[e] = 2
1318 self.assertEqual(len(dic), 1)
1319 self.assertEqual(dic[d], 2)
1320 self.assertEqual(dic[e], 2)
1321
1322 d = self.theclass(2001, 1, 1, 0, 5, 17)
1323 e = self.theclass(2001, 1, 1, 0, 5, 17)
1324 self.assertEqual(d, e)
1325 self.assertEqual(hash(d), hash(e))
1326
1327 dic = {d: 1}
1328 dic[e] = 2
1329 self.assertEqual(len(dic), 1)
1330 self.assertEqual(dic[d], 2)
1331 self.assertEqual(dic[e], 2)
1332
1333 def test_computations(self):
1334 a = self.theclass(2002, 1, 31)
1335 b = self.theclass(1956, 1, 31)
1336 diff = a-b
1337 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1338 self.assertEqual(diff.seconds, 0)
1339 self.assertEqual(diff.microseconds, 0)
1340 a = self.theclass(2002, 3, 2, 17, 6)
1341 millisec = timedelta(0, 0, 1000)
1342 hour = timedelta(0, 3600)
1343 day = timedelta(1)
1344 week = timedelta(7)
1345 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1346 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1347 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1348 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1349 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1350 self.assertEqual(a - hour, a + -hour)
1351 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1352 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1353 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1354 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1355 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1356 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1357 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1358 self.assertEqual((a + week) - a, week)
1359 self.assertEqual((a + day) - a, day)
1360 self.assertEqual((a + hour) - a, hour)
1361 self.assertEqual((a + millisec) - a, millisec)
1362 self.assertEqual((a - week) - a, -week)
1363 self.assertEqual((a - day) - a, -day)
1364 self.assertEqual((a - hour) - a, -hour)
1365 self.assertEqual((a - millisec) - a, -millisec)
1366 self.assertEqual(a - (a + week), -week)
1367 self.assertEqual(a - (a + day), -day)
1368 self.assertEqual(a - (a + hour), -hour)
1369 self.assertEqual(a - (a + millisec), -millisec)
1370 self.assertEqual(a - (a - week), week)
1371 self.assertEqual(a - (a - day), day)
1372 self.assertEqual(a - (a - hour), hour)
1373 self.assertEqual(a - (a - millisec), millisec)
1374 self.assertEqual(a + (week + day + hour + millisec),
1375 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1376 self.assertEqual(a + (week + day + hour + millisec),
1377 (((a + week) + day) + hour) + millisec)
1378 self.assertEqual(a - (week + day + hour + millisec),
1379 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1380 self.assertEqual(a - (week + day + hour + millisec),
1381 (((a - week) - day) - hour) - millisec)
1382 # Add/sub ints, longs, floats should be illegal
1383 for i in 1, 1L, 1.0:
1384 self.assertRaises(TypeError, lambda: a+i)
1385 self.assertRaises(TypeError, lambda: a-i)
1386 self.assertRaises(TypeError, lambda: i+a)
1387 self.assertRaises(TypeError, lambda: i-a)
1388
1389 # delta - datetime is senseless.
1390 self.assertRaises(TypeError, lambda: day - a)
1391 # mixing datetime and (delta or datetime) via * or // is senseless
1392 self.assertRaises(TypeError, lambda: day * a)
1393 self.assertRaises(TypeError, lambda: a * day)
1394 self.assertRaises(TypeError, lambda: day // a)
1395 self.assertRaises(TypeError, lambda: a // day)
1396 self.assertRaises(TypeError, lambda: a * a)
1397 self.assertRaises(TypeError, lambda: a // a)
1398 # datetime + datetime is senseless
1399 self.assertRaises(TypeError, lambda: a + a)
1400
1401 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001402 args = 6, 7, 23, 20, 59, 1, 64**2
1403 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001404 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001405 green = pickler.dumps(orig, proto)
1406 derived = unpickler.loads(green)
1407 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001408
Guido van Rossum275666f2003-02-07 21:49:01 +00001409 def test_more_pickling(self):
1410 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1411 s = pickle.dumps(a)
1412 b = pickle.loads(s)
1413 self.assertEqual(b.year, 2003)
1414 self.assertEqual(b.month, 2)
1415 self.assertEqual(b.day, 7)
1416
Tim Peters604c0132004-06-07 23:04:33 +00001417 def test_pickling_subclass_datetime(self):
1418 args = 6, 7, 23, 20, 59, 1, 64**2
1419 orig = SubclassDatetime(*args)
1420 for pickler, unpickler, proto in pickle_choices:
1421 green = pickler.dumps(orig, proto)
1422 derived = unpickler.loads(green)
1423 self.assertEqual(orig, derived)
1424
Tim Peters2a799bf2002-12-16 20:18:38 +00001425 def test_more_compare(self):
1426 # The test_compare() inherited from TestDate covers the error cases.
1427 # We just want to test lexicographic ordering on the members datetime
1428 # has that date lacks.
1429 args = [2000, 11, 29, 20, 58, 16, 999998]
1430 t1 = self.theclass(*args)
1431 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001432 self.assertTrue(t1 == t2)
1433 self.assertTrue(t1 <= t2)
1434 self.assertTrue(t1 >= t2)
1435 self.assertTrue(not t1 != t2)
1436 self.assertTrue(not t1 < t2)
1437 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001438 self.assertEqual(cmp(t1, t2), 0)
1439 self.assertEqual(cmp(t2, t1), 0)
1440
1441 for i in range(len(args)):
1442 newargs = args[:]
1443 newargs[i] = args[i] + 1
1444 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001445 self.assertTrue(t1 < t2)
1446 self.assertTrue(t2 > t1)
1447 self.assertTrue(t1 <= t2)
1448 self.assertTrue(t2 >= t1)
1449 self.assertTrue(t1 != t2)
1450 self.assertTrue(t2 != t1)
1451 self.assertTrue(not t1 == t2)
1452 self.assertTrue(not t2 == t1)
1453 self.assertTrue(not t1 > t2)
1454 self.assertTrue(not t2 < t1)
1455 self.assertTrue(not t1 >= t2)
1456 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001457 self.assertEqual(cmp(t1, t2), -1)
1458 self.assertEqual(cmp(t2, t1), 1)
1459
1460
1461 # A helper for timestamp constructor tests.
1462 def verify_field_equality(self, expected, got):
1463 self.assertEqual(expected.tm_year, got.year)
1464 self.assertEqual(expected.tm_mon, got.month)
1465 self.assertEqual(expected.tm_mday, got.day)
1466 self.assertEqual(expected.tm_hour, got.hour)
1467 self.assertEqual(expected.tm_min, got.minute)
1468 self.assertEqual(expected.tm_sec, got.second)
1469
1470 def test_fromtimestamp(self):
1471 import time
1472
1473 ts = time.time()
1474 expected = time.localtime(ts)
1475 got = self.theclass.fromtimestamp(ts)
1476 self.verify_field_equality(expected, got)
1477
1478 def test_utcfromtimestamp(self):
1479 import time
1480
1481 ts = time.time()
1482 expected = time.gmtime(ts)
1483 got = self.theclass.utcfromtimestamp(ts)
1484 self.verify_field_equality(expected, got)
1485
Georg Brandl6d78a582006-04-28 19:09:24 +00001486 def test_microsecond_rounding(self):
1487 # Test whether fromtimestamp "rounds up" floats that are less
1488 # than one microsecond smaller than an integer.
1489 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1490 self.theclass.fromtimestamp(1))
1491
Tim Peters1b6f7a92004-06-20 02:50:16 +00001492 def test_insane_fromtimestamp(self):
1493 # It's possible that some platform maps time_t to double,
1494 # and that this test will fail there. This test should
1495 # exempt such platforms (provided they return reasonable
1496 # results!).
1497 for insane in -1e200, 1e200:
1498 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1499 insane)
1500
1501 def test_insane_utcfromtimestamp(self):
1502 # It's possible that some platform maps time_t to double,
1503 # and that this test will fail there. This test should
1504 # exempt such platforms (provided they return reasonable
1505 # results!).
1506 for insane in -1e200, 1e200:
1507 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1508 insane)
1509
Guido van Rossum2054ee92007-03-06 15:50:01 +00001510 def test_negative_float_fromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001511 # Windows doesn't accept negative timestamps
1512 if os.name == "nt":
1513 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001514 # The result is tz-dependent; at least test that this doesn't
1515 # fail (like it did before bug 1646728 was fixed).
1516 self.theclass.fromtimestamp(-1.05)
1517
1518 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001519 # Windows doesn't accept negative timestamps
1520 if os.name == "nt":
1521 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001522 d = self.theclass.utcfromtimestamp(-1.05)
1523 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1524
Tim Peters2a799bf2002-12-16 20:18:38 +00001525 def test_utcnow(self):
1526 import time
1527
1528 # Call it a success if utcnow() and utcfromtimestamp() are within
1529 # a second of each other.
1530 tolerance = timedelta(seconds=1)
1531 for dummy in range(3):
1532 from_now = self.theclass.utcnow()
1533 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1534 if abs(from_timestamp - from_now) <= tolerance:
1535 break
1536 # Else try again a few times.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001537 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001538
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001539 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001540 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001541
Skip Montanarofc070d22008-03-15 16:04:45 +00001542 string = '2004-12-01 13:02:47.197'
1543 format = '%Y-%m-%d %H:%M:%S.%f'
1544 result, frac = _strptime._strptime(string, format)
1545 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001546 got = self.theclass.strptime(string, format)
1547 self.assertEqual(expected, got)
1548
Tim Peters2a799bf2002-12-16 20:18:38 +00001549 def test_more_timetuple(self):
1550 # This tests fields beyond those tested by the TestDate.test_timetuple.
1551 t = self.theclass(2004, 12, 31, 6, 22, 33)
1552 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1553 self.assertEqual(t.timetuple(),
1554 (t.year, t.month, t.day,
1555 t.hour, t.minute, t.second,
1556 t.weekday(),
1557 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1558 -1))
1559 tt = t.timetuple()
1560 self.assertEqual(tt.tm_year, t.year)
1561 self.assertEqual(tt.tm_mon, t.month)
1562 self.assertEqual(tt.tm_mday, t.day)
1563 self.assertEqual(tt.tm_hour, t.hour)
1564 self.assertEqual(tt.tm_min, t.minute)
1565 self.assertEqual(tt.tm_sec, t.second)
1566 self.assertEqual(tt.tm_wday, t.weekday())
1567 self.assertEqual(tt.tm_yday, t.toordinal() -
1568 date(t.year, 1, 1).toordinal() + 1)
1569 self.assertEqual(tt.tm_isdst, -1)
1570
1571 def test_more_strftime(self):
1572 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001573 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1574 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1575 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001576
1577 def test_extract(self):
1578 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1579 self.assertEqual(dt.date(), date(2002, 3, 4))
1580 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1581
1582 def test_combine(self):
1583 d = date(2002, 3, 4)
1584 t = time(18, 45, 3, 1234)
1585 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1586 combine = self.theclass.combine
1587 dt = combine(d, t)
1588 self.assertEqual(dt, expected)
1589
1590 dt = combine(time=t, date=d)
1591 self.assertEqual(dt, expected)
1592
1593 self.assertEqual(d, dt.date())
1594 self.assertEqual(t, dt.time())
1595 self.assertEqual(dt, combine(dt.date(), dt.time()))
1596
1597 self.assertRaises(TypeError, combine) # need an arg
1598 self.assertRaises(TypeError, combine, d) # need two args
1599 self.assertRaises(TypeError, combine, t, d) # args reversed
1600 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1601 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1602
Tim Peters12bf3392002-12-24 05:41:27 +00001603 def test_replace(self):
1604 cls = self.theclass
1605 args = [1, 2, 3, 4, 5, 6, 7]
1606 base = cls(*args)
1607 self.assertEqual(base, base.replace())
1608
1609 i = 0
1610 for name, newval in (("year", 2),
1611 ("month", 3),
1612 ("day", 4),
1613 ("hour", 5),
1614 ("minute", 6),
1615 ("second", 7),
1616 ("microsecond", 8)):
1617 newargs = args[:]
1618 newargs[i] = newval
1619 expected = cls(*newargs)
1620 got = base.replace(**{name: newval})
1621 self.assertEqual(expected, got)
1622 i += 1
1623
1624 # Out of bounds.
1625 base = cls(2000, 2, 29)
1626 self.assertRaises(ValueError, base.replace, year=2001)
1627
Tim Peters80475bb2002-12-25 07:40:55 +00001628 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001629 # Pretty boring! The TZ test is more interesting here. astimezone()
1630 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001631 dt = self.theclass.now()
1632 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001633 self.assertRaises(TypeError, dt.astimezone) # not enough args
1634 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1635 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001636 self.assertRaises(ValueError, dt.astimezone, f) # naive
1637 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001638
Tim Peters52dcce22003-01-23 16:36:11 +00001639 class Bogus(tzinfo):
1640 def utcoffset(self, dt): return None
1641 def dst(self, dt): return timedelta(0)
1642 bog = Bogus()
1643 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1644
1645 class AlsoBogus(tzinfo):
1646 def utcoffset(self, dt): return timedelta(0)
1647 def dst(self, dt): return None
1648 alsobog = AlsoBogus()
1649 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001650
Tim Petersa98924a2003-05-17 05:55:19 +00001651 def test_subclass_datetime(self):
1652
1653 class C(self.theclass):
1654 theAnswer = 42
1655
1656 def __new__(cls, *args, **kws):
1657 temp = kws.copy()
1658 extra = temp.pop('extra')
1659 result = self.theclass.__new__(cls, *args, **temp)
1660 result.extra = extra
1661 return result
1662
1663 def newmeth(self, start):
1664 return start + self.year + self.month + self.second
1665
1666 args = 2003, 4, 14, 12, 13, 41
1667
1668 dt1 = self.theclass(*args)
1669 dt2 = C(*args, **{'extra': 7})
1670
1671 self.assertEqual(dt2.__class__, C)
1672 self.assertEqual(dt2.theAnswer, 42)
1673 self.assertEqual(dt2.extra, 7)
1674 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1675 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1676 dt1.second - 7)
1677
Tim Peters604c0132004-06-07 23:04:33 +00001678class SubclassTime(time):
1679 sub_var = 1
1680
Collin Winterc2898c52007-04-25 17:29:52 +00001681class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001682
1683 theclass = time
1684
1685 def test_basic_attributes(self):
1686 t = self.theclass(12, 0)
1687 self.assertEqual(t.hour, 12)
1688 self.assertEqual(t.minute, 0)
1689 self.assertEqual(t.second, 0)
1690 self.assertEqual(t.microsecond, 0)
1691
1692 def test_basic_attributes_nonzero(self):
1693 # Make sure all attributes are non-zero so bugs in
1694 # bit-shifting access show up.
1695 t = self.theclass(12, 59, 59, 8000)
1696 self.assertEqual(t.hour, 12)
1697 self.assertEqual(t.minute, 59)
1698 self.assertEqual(t.second, 59)
1699 self.assertEqual(t.microsecond, 8000)
1700
1701 def test_roundtrip(self):
1702 t = self.theclass(1, 2, 3, 4)
1703
1704 # Verify t -> string -> time identity.
1705 s = repr(t)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001706 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001707 s = s[9:]
1708 t2 = eval(s)
1709 self.assertEqual(t, t2)
1710
1711 # Verify identity via reconstructing from pieces.
1712 t2 = self.theclass(t.hour, t.minute, t.second,
1713 t.microsecond)
1714 self.assertEqual(t, t2)
1715
1716 def test_comparing(self):
1717 args = [1, 2, 3, 4]
1718 t1 = self.theclass(*args)
1719 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001720 self.assertTrue(t1 == t2)
1721 self.assertTrue(t1 <= t2)
1722 self.assertTrue(t1 >= t2)
1723 self.assertTrue(not t1 != t2)
1724 self.assertTrue(not t1 < t2)
1725 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001726 self.assertEqual(cmp(t1, t2), 0)
1727 self.assertEqual(cmp(t2, t1), 0)
1728
1729 for i in range(len(args)):
1730 newargs = args[:]
1731 newargs[i] = args[i] + 1
1732 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001733 self.assertTrue(t1 < t2)
1734 self.assertTrue(t2 > t1)
1735 self.assertTrue(t1 <= t2)
1736 self.assertTrue(t2 >= t1)
1737 self.assertTrue(t1 != t2)
1738 self.assertTrue(t2 != t1)
1739 self.assertTrue(not t1 == t2)
1740 self.assertTrue(not t2 == t1)
1741 self.assertTrue(not t1 > t2)
1742 self.assertTrue(not t2 < t1)
1743 self.assertTrue(not t1 >= t2)
1744 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001745 self.assertEqual(cmp(t1, t2), -1)
1746 self.assertEqual(cmp(t2, t1), 1)
1747
Tim Peters68124bb2003-02-08 03:46:31 +00001748 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001749 self.assertEqual(t1 == badarg, False)
1750 self.assertEqual(t1 != badarg, True)
1751 self.assertEqual(badarg == t1, False)
1752 self.assertEqual(badarg != t1, True)
1753
Tim Peters2a799bf2002-12-16 20:18:38 +00001754 self.assertRaises(TypeError, lambda: t1 <= badarg)
1755 self.assertRaises(TypeError, lambda: t1 < badarg)
1756 self.assertRaises(TypeError, lambda: t1 > badarg)
1757 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001758 self.assertRaises(TypeError, lambda: badarg <= t1)
1759 self.assertRaises(TypeError, lambda: badarg < t1)
1760 self.assertRaises(TypeError, lambda: badarg > t1)
1761 self.assertRaises(TypeError, lambda: badarg >= t1)
1762
1763 def test_bad_constructor_arguments(self):
1764 # bad hours
1765 self.theclass(0, 0) # no exception
1766 self.theclass(23, 0) # no exception
1767 self.assertRaises(ValueError, self.theclass, -1, 0)
1768 self.assertRaises(ValueError, self.theclass, 24, 0)
1769 # bad minutes
1770 self.theclass(23, 0) # no exception
1771 self.theclass(23, 59) # no exception
1772 self.assertRaises(ValueError, self.theclass, 23, -1)
1773 self.assertRaises(ValueError, self.theclass, 23, 60)
1774 # bad seconds
1775 self.theclass(23, 59, 0) # no exception
1776 self.theclass(23, 59, 59) # no exception
1777 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1778 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1779 # bad microseconds
1780 self.theclass(23, 59, 59, 0) # no exception
1781 self.theclass(23, 59, 59, 999999) # no exception
1782 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1783 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1784
1785 def test_hash_equality(self):
1786 d = self.theclass(23, 30, 17)
1787 e = self.theclass(23, 30, 17)
1788 self.assertEqual(d, e)
1789 self.assertEqual(hash(d), hash(e))
1790
1791 dic = {d: 1}
1792 dic[e] = 2
1793 self.assertEqual(len(dic), 1)
1794 self.assertEqual(dic[d], 2)
1795 self.assertEqual(dic[e], 2)
1796
1797 d = self.theclass(0, 5, 17)
1798 e = self.theclass(0, 5, 17)
1799 self.assertEqual(d, e)
1800 self.assertEqual(hash(d), hash(e))
1801
1802 dic = {d: 1}
1803 dic[e] = 2
1804 self.assertEqual(len(dic), 1)
1805 self.assertEqual(dic[d], 2)
1806 self.assertEqual(dic[e], 2)
1807
1808 def test_isoformat(self):
1809 t = self.theclass(4, 5, 1, 123)
1810 self.assertEqual(t.isoformat(), "04:05:01.000123")
1811 self.assertEqual(t.isoformat(), str(t))
1812
1813 t = self.theclass()
1814 self.assertEqual(t.isoformat(), "00:00:00")
1815 self.assertEqual(t.isoformat(), str(t))
1816
1817 t = self.theclass(microsecond=1)
1818 self.assertEqual(t.isoformat(), "00:00:00.000001")
1819 self.assertEqual(t.isoformat(), str(t))
1820
1821 t = self.theclass(microsecond=10)
1822 self.assertEqual(t.isoformat(), "00:00:00.000010")
1823 self.assertEqual(t.isoformat(), str(t))
1824
1825 t = self.theclass(microsecond=100)
1826 self.assertEqual(t.isoformat(), "00:00:00.000100")
1827 self.assertEqual(t.isoformat(), str(t))
1828
1829 t = self.theclass(microsecond=1000)
1830 self.assertEqual(t.isoformat(), "00:00:00.001000")
1831 self.assertEqual(t.isoformat(), str(t))
1832
1833 t = self.theclass(microsecond=10000)
1834 self.assertEqual(t.isoformat(), "00:00:00.010000")
1835 self.assertEqual(t.isoformat(), str(t))
1836
1837 t = self.theclass(microsecond=100000)
1838 self.assertEqual(t.isoformat(), "00:00:00.100000")
1839 self.assertEqual(t.isoformat(), str(t))
1840
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001841 def test_1653736(self):
1842 # verify it doesn't accept extra keyword arguments
1843 t = self.theclass(second=1)
1844 self.assertRaises(TypeError, t.isoformat, foo=3)
1845
Tim Peters2a799bf2002-12-16 20:18:38 +00001846 def test_strftime(self):
1847 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001848 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001849 # A naive object replaces %z and %Z with empty strings.
1850 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1851
Eric Smitha9f7d622008-02-17 19:46:49 +00001852 def test_format(self):
1853 t = self.theclass(1, 2, 3, 4)
1854 self.assertEqual(t.__format__(''), str(t))
1855
1856 # check that a derived class's __str__() gets called
1857 class A(self.theclass):
1858 def __str__(self):
1859 return 'A'
1860 a = A(1, 2, 3, 4)
1861 self.assertEqual(a.__format__(''), 'A')
1862
1863 # check that a derived class's strftime gets called
1864 class B(self.theclass):
1865 def strftime(self, format_spec):
1866 return 'B'
1867 b = B(1, 2, 3, 4)
1868 self.assertEqual(b.__format__(''), str(t))
1869
1870 for fmt in ['%H %M %S',
1871 ]:
1872 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1873 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1874 self.assertEqual(b.__format__(fmt), 'B')
1875
Tim Peters2a799bf2002-12-16 20:18:38 +00001876 def test_str(self):
1877 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1878 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1879 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1880 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1881 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1882
1883 def test_repr(self):
1884 name = 'datetime.' + self.theclass.__name__
1885 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1886 "%s(1, 2, 3, 4)" % name)
1887 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1888 "%s(10, 2, 3, 4000)" % name)
1889 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1890 "%s(0, 2, 3, 400000)" % name)
1891 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1892 "%s(12, 2, 3)" % name)
1893 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1894 "%s(23, 15)" % name)
1895
1896 def test_resolution_info(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001897 self.assertTrue(isinstance(self.theclass.min, self.theclass))
1898 self.assertTrue(isinstance(self.theclass.max, self.theclass))
1899 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
1900 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001901
1902 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001903 args = 20, 59, 16, 64**2
1904 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001905 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001906 green = pickler.dumps(orig, proto)
1907 derived = unpickler.loads(green)
1908 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001909
Tim Peters604c0132004-06-07 23:04:33 +00001910 def test_pickling_subclass_time(self):
1911 args = 20, 59, 16, 64**2
1912 orig = SubclassTime(*args)
1913 for pickler, unpickler, proto in pickle_choices:
1914 green = pickler.dumps(orig, proto)
1915 derived = unpickler.loads(green)
1916 self.assertEqual(orig, derived)
1917
Tim Peters2a799bf2002-12-16 20:18:38 +00001918 def test_bool(self):
1919 cls = self.theclass
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001920 self.assertTrue(cls(1))
1921 self.assertTrue(cls(0, 1))
1922 self.assertTrue(cls(0, 0, 1))
1923 self.assertTrue(cls(0, 0, 0, 1))
1924 self.assertTrue(not cls(0))
1925 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001926
Tim Peters12bf3392002-12-24 05:41:27 +00001927 def test_replace(self):
1928 cls = self.theclass
1929 args = [1, 2, 3, 4]
1930 base = cls(*args)
1931 self.assertEqual(base, base.replace())
1932
1933 i = 0
1934 for name, newval in (("hour", 5),
1935 ("minute", 6),
1936 ("second", 7),
1937 ("microsecond", 8)):
1938 newargs = args[:]
1939 newargs[i] = newval
1940 expected = cls(*newargs)
1941 got = base.replace(**{name: newval})
1942 self.assertEqual(expected, got)
1943 i += 1
1944
1945 # Out of bounds.
1946 base = cls(1)
1947 self.assertRaises(ValueError, base.replace, hour=24)
1948 self.assertRaises(ValueError, base.replace, minute=-1)
1949 self.assertRaises(ValueError, base.replace, second=100)
1950 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1951
Tim Petersa98924a2003-05-17 05:55:19 +00001952 def test_subclass_time(self):
1953
1954 class C(self.theclass):
1955 theAnswer = 42
1956
1957 def __new__(cls, *args, **kws):
1958 temp = kws.copy()
1959 extra = temp.pop('extra')
1960 result = self.theclass.__new__(cls, *args, **temp)
1961 result.extra = extra
1962 return result
1963
1964 def newmeth(self, start):
1965 return start + self.hour + self.second
1966
1967 args = 4, 5, 6
1968
1969 dt1 = self.theclass(*args)
1970 dt2 = C(*args, **{'extra': 7})
1971
1972 self.assertEqual(dt2.__class__, C)
1973 self.assertEqual(dt2.theAnswer, 42)
1974 self.assertEqual(dt2.extra, 7)
1975 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1976 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1977
Armin Rigof4afb212005-11-07 07:15:48 +00001978 def test_backdoor_resistance(self):
1979 # see TestDate.test_backdoor_resistance().
1980 base = '2:59.0'
1981 for hour_byte in ' ', '9', chr(24), '\xff':
1982 self.assertRaises(TypeError, self.theclass,
1983 hour_byte + base[1:])
1984
Tim Peters855fe882002-12-22 03:43:39 +00001985# A mixin for classes with a tzinfo= argument. Subclasses must define
1986# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001987# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001988class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001989
Tim Petersbad8ff02002-12-30 20:52:32 +00001990 def test_argument_passing(self):
1991 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001992 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001993 class introspective(tzinfo):
1994 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001995 def utcoffset(self, dt):
1996 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001997 dst = utcoffset
1998
1999 obj = cls(1, 2, 3, tzinfo=introspective())
2000
Tim Peters0bf60bd2003-01-08 20:40:01 +00002001 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002002 self.assertEqual(obj.tzname(), expected)
2003
Tim Peters0bf60bd2003-01-08 20:40:01 +00002004 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002005 self.assertEqual(obj.utcoffset(), expected)
2006 self.assertEqual(obj.dst(), expected)
2007
Tim Peters855fe882002-12-22 03:43:39 +00002008 def test_bad_tzinfo_classes(self):
2009 cls = self.theclass
2010 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002011
Tim Peters855fe882002-12-22 03:43:39 +00002012 class NiceTry(object):
2013 def __init__(self): pass
2014 def utcoffset(self, dt): pass
2015 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2016
2017 class BetterTry(tzinfo):
2018 def __init__(self): pass
2019 def utcoffset(self, dt): pass
2020 b = BetterTry()
2021 t = cls(1, 1, 1, tzinfo=b)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002022 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002023
2024 def test_utc_offset_out_of_bounds(self):
2025 class Edgy(tzinfo):
2026 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002027 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002028 def utcoffset(self, dt):
2029 return self.offset
2030
2031 cls = self.theclass
2032 for offset, legit in ((-1440, False),
2033 (-1439, True),
2034 (1439, True),
2035 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002036 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002037 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002038 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002039 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002040 else:
2041 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002042 if legit:
2043 aofs = abs(offset)
2044 h, m = divmod(aofs, 60)
2045 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002046 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002047 t = t.timetz()
2048 self.assertEqual(str(t), "01:02:03" + tag)
2049 else:
2050 self.assertRaises(ValueError, str, t)
2051
2052 def test_tzinfo_classes(self):
2053 cls = self.theclass
2054 class C1(tzinfo):
2055 def utcoffset(self, dt): return None
2056 def dst(self, dt): return None
2057 def tzname(self, dt): return None
2058 for t in (cls(1, 1, 1),
2059 cls(1, 1, 1, tzinfo=None),
2060 cls(1, 1, 1, tzinfo=C1())):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002061 self.assertTrue(t.utcoffset() is None)
2062 self.assertTrue(t.dst() is None)
2063 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002064
Tim Peters855fe882002-12-22 03:43:39 +00002065 class C3(tzinfo):
2066 def utcoffset(self, dt): return timedelta(minutes=-1439)
2067 def dst(self, dt): return timedelta(minutes=1439)
2068 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002069 t = cls(1, 1, 1, tzinfo=C3())
2070 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2071 self.assertEqual(t.dst(), timedelta(minutes=1439))
2072 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002073
2074 # Wrong types.
2075 class C4(tzinfo):
2076 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002077 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002078 def tzname(self, dt): return 0
2079 t = cls(1, 1, 1, tzinfo=C4())
2080 self.assertRaises(TypeError, t.utcoffset)
2081 self.assertRaises(TypeError, t.dst)
2082 self.assertRaises(TypeError, t.tzname)
2083
2084 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002085 class C6(tzinfo):
2086 def utcoffset(self, dt): return timedelta(hours=-24)
2087 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002088 t = cls(1, 1, 1, tzinfo=C6())
2089 self.assertRaises(ValueError, t.utcoffset)
2090 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002091
2092 # Not a whole number of minutes.
2093 class C7(tzinfo):
2094 def utcoffset(self, dt): return timedelta(seconds=61)
2095 def dst(self, dt): return timedelta(microseconds=-81)
2096 t = cls(1, 1, 1, tzinfo=C7())
2097 self.assertRaises(ValueError, t.utcoffset)
2098 self.assertRaises(ValueError, t.dst)
2099
Tim Peters4c0db782002-12-26 05:01:19 +00002100 def test_aware_compare(self):
2101 cls = self.theclass
2102
Tim Peters60c76e42002-12-27 00:41:11 +00002103 # Ensure that utcoffset() gets ignored if the comparands have
2104 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002105 class OperandDependentOffset(tzinfo):
2106 def utcoffset(self, t):
2107 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002108 # d0 and d1 equal after adjustment
2109 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002110 else:
Tim Peters397301e2003-01-02 21:28:08 +00002111 # d2 off in the weeds
2112 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002113
2114 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2115 d0 = base.replace(minute=3)
2116 d1 = base.replace(minute=9)
2117 d2 = base.replace(minute=11)
2118 for x in d0, d1, d2:
2119 for y in d0, d1, d2:
2120 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002121 expected = cmp(x.minute, y.minute)
2122 self.assertEqual(got, expected)
2123
2124 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002125 # Note that a time can't actually have an operand-depedent offset,
2126 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2127 # so skip this test for time.
2128 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002129 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2130 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2131 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2132 for x in d0, d1, d2:
2133 for y in d0, d1, d2:
2134 got = cmp(x, y)
2135 if (x is d0 or x is d1) and (y is d0 or y is d1):
2136 expected = 0
2137 elif x is y is d2:
2138 expected = 0
2139 elif x is d2:
2140 expected = -1
2141 else:
2142 assert y is d2
2143 expected = 1
2144 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002145
Tim Peters855fe882002-12-22 03:43:39 +00002146
Tim Peters0bf60bd2003-01-08 20:40:01 +00002147# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002148class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002149 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002150
2151 def test_empty(self):
2152 t = self.theclass()
2153 self.assertEqual(t.hour, 0)
2154 self.assertEqual(t.minute, 0)
2155 self.assertEqual(t.second, 0)
2156 self.assertEqual(t.microsecond, 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002157 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002158
Tim Peters2a799bf2002-12-16 20:18:38 +00002159 def test_zones(self):
2160 est = FixedOffset(-300, "EST", 1)
2161 utc = FixedOffset(0, "UTC", -2)
2162 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002163 t1 = time( 7, 47, tzinfo=est)
2164 t2 = time(12, 47, tzinfo=utc)
2165 t3 = time(13, 47, tzinfo=met)
2166 t4 = time(microsecond=40)
2167 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002168
2169 self.assertEqual(t1.tzinfo, est)
2170 self.assertEqual(t2.tzinfo, utc)
2171 self.assertEqual(t3.tzinfo, met)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002172 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002173 self.assertEqual(t5.tzinfo, utc)
2174
Tim Peters855fe882002-12-22 03:43:39 +00002175 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2176 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2177 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002178 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002179 self.assertRaises(TypeError, t1.utcoffset, "no args")
2180
2181 self.assertEqual(t1.tzname(), "EST")
2182 self.assertEqual(t2.tzname(), "UTC")
2183 self.assertEqual(t3.tzname(), "MET")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002184 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002185 self.assertRaises(TypeError, t1.tzname, "no args")
2186
Tim Peters855fe882002-12-22 03:43:39 +00002187 self.assertEqual(t1.dst(), timedelta(minutes=1))
2188 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2189 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002190 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002191 self.assertRaises(TypeError, t1.dst, "no args")
2192
2193 self.assertEqual(hash(t1), hash(t2))
2194 self.assertEqual(hash(t1), hash(t3))
2195 self.assertEqual(hash(t2), hash(t3))
2196
2197 self.assertEqual(t1, t2)
2198 self.assertEqual(t1, t3)
2199 self.assertEqual(t2, t3)
2200 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2201 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2202 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2203
2204 self.assertEqual(str(t1), "07:47:00-05:00")
2205 self.assertEqual(str(t2), "12:47:00+00:00")
2206 self.assertEqual(str(t3), "13:47:00+01:00")
2207 self.assertEqual(str(t4), "00:00:00.000040")
2208 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2209
2210 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2211 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2212 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2213 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2214 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2215
Tim Peters0bf60bd2003-01-08 20:40:01 +00002216 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002217 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2218 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2219 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2220 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2221 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2222
2223 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2224 "07:47:00 %Z=EST %z=-0500")
2225 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2226 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2227
2228 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002229 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002230 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2231 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2232
Tim Petersb92bb712002-12-21 17:44:07 +00002233 # Check that an invalid tzname result raises an exception.
2234 class Badtzname(tzinfo):
2235 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002236 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002237 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2238 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002239
2240 def test_hash_edge_cases(self):
2241 # Offsets that overflow a basic time.
2242 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2243 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2244 self.assertEqual(hash(t1), hash(t2))
2245
2246 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2247 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2248 self.assertEqual(hash(t1), hash(t2))
2249
Tim Peters2a799bf2002-12-16 20:18:38 +00002250 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002251 # Try one without a tzinfo.
2252 args = 20, 59, 16, 64**2
2253 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002254 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002255 green = pickler.dumps(orig, proto)
2256 derived = unpickler.loads(green)
2257 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002258
2259 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002260 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002261 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002262 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002263 green = pickler.dumps(orig, proto)
2264 derived = unpickler.loads(green)
2265 self.assertEqual(orig, derived)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002266 self.assertTrue(isinstance(derived.tzinfo, PicklableFixedOffset))
Tim Peters96940c92003-01-31 21:55:33 +00002267 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2268 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002269
2270 def test_more_bool(self):
2271 # Test cases with non-None tzinfo.
2272 cls = self.theclass
2273
2274 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002275 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002276
2277 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002278 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002279
2280 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002281 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002282
2283 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002284 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002285
2286 # Mostly ensuring this doesn't overflow internally.
2287 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002288 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002289
2290 # But this should yield a value error -- the utcoffset is bogus.
2291 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2292 self.assertRaises(ValueError, lambda: bool(t))
2293
2294 # Likewise.
2295 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2296 self.assertRaises(ValueError, lambda: bool(t))
2297
Tim Peters12bf3392002-12-24 05:41:27 +00002298 def test_replace(self):
2299 cls = self.theclass
2300 z100 = FixedOffset(100, "+100")
2301 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2302 args = [1, 2, 3, 4, z100]
2303 base = cls(*args)
2304 self.assertEqual(base, base.replace())
2305
2306 i = 0
2307 for name, newval in (("hour", 5),
2308 ("minute", 6),
2309 ("second", 7),
2310 ("microsecond", 8),
2311 ("tzinfo", zm200)):
2312 newargs = args[:]
2313 newargs[i] = newval
2314 expected = cls(*newargs)
2315 got = base.replace(**{name: newval})
2316 self.assertEqual(expected, got)
2317 i += 1
2318
2319 # Ensure we can get rid of a tzinfo.
2320 self.assertEqual(base.tzname(), "+100")
2321 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002322 self.assertTrue(base2.tzinfo is None)
2323 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002324
2325 # Ensure we can add one.
2326 base3 = base2.replace(tzinfo=z100)
2327 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002328 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002329
2330 # Out of bounds.
2331 base = cls(1)
2332 self.assertRaises(ValueError, base.replace, hour=24)
2333 self.assertRaises(ValueError, base.replace, minute=-1)
2334 self.assertRaises(ValueError, base.replace, second=100)
2335 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2336
Tim Peters60c76e42002-12-27 00:41:11 +00002337 def test_mixed_compare(self):
2338 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002339 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002340 self.assertEqual(t1, t2)
2341 t2 = t2.replace(tzinfo=None)
2342 self.assertEqual(t1, t2)
2343 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2344 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002345 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2346 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002347
Tim Peters0bf60bd2003-01-08 20:40:01 +00002348 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002349 class Varies(tzinfo):
2350 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002351 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002352 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002353 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002354 return self.offset
2355
2356 v = Varies()
2357 t1 = t2.replace(tzinfo=v)
2358 t2 = t2.replace(tzinfo=v)
2359 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2360 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2361 self.assertEqual(t1, t2)
2362
2363 # But if they're not identical, it isn't ignored.
2364 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002365 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002366
Tim Petersa98924a2003-05-17 05:55:19 +00002367 def test_subclass_timetz(self):
2368
2369 class C(self.theclass):
2370 theAnswer = 42
2371
2372 def __new__(cls, *args, **kws):
2373 temp = kws.copy()
2374 extra = temp.pop('extra')
2375 result = self.theclass.__new__(cls, *args, **temp)
2376 result.extra = extra
2377 return result
2378
2379 def newmeth(self, start):
2380 return start + self.hour + self.second
2381
2382 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2383
2384 dt1 = self.theclass(*args)
2385 dt2 = C(*args, **{'extra': 7})
2386
2387 self.assertEqual(dt2.__class__, C)
2388 self.assertEqual(dt2.theAnswer, 42)
2389 self.assertEqual(dt2.extra, 7)
2390 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2391 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2392
Tim Peters4c0db782002-12-26 05:01:19 +00002393
Tim Peters0bf60bd2003-01-08 20:40:01 +00002394# Testing datetime objects with a non-None tzinfo.
2395
Collin Winterc2898c52007-04-25 17:29:52 +00002396class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002397 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002398
2399 def test_trivial(self):
2400 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2401 self.assertEqual(dt.year, 1)
2402 self.assertEqual(dt.month, 2)
2403 self.assertEqual(dt.day, 3)
2404 self.assertEqual(dt.hour, 4)
2405 self.assertEqual(dt.minute, 5)
2406 self.assertEqual(dt.second, 6)
2407 self.assertEqual(dt.microsecond, 7)
2408 self.assertEqual(dt.tzinfo, None)
2409
2410 def test_even_more_compare(self):
2411 # The test_compare() and test_more_compare() inherited from TestDate
2412 # and TestDateTime covered non-tzinfo cases.
2413
2414 # Smallest possible after UTC adjustment.
2415 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2416 # Largest possible after UTC adjustment.
2417 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2418 tzinfo=FixedOffset(-1439, ""))
2419
2420 # Make sure those compare correctly, and w/o overflow.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002421 self.assertTrue(t1 < t2)
2422 self.assertTrue(t1 != t2)
2423 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002424
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002425 self.assertTrue(t1 == t1)
2426 self.assertTrue(t2 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002427
2428 # Equal afer adjustment.
2429 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2430 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2431 self.assertEqual(t1, t2)
2432
2433 # Change t1 not to subtract a minute, and t1 should be larger.
2434 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002435 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002436
2437 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2438 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002439 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002440
2441 # Back to the original t1, but make seconds resolve it.
2442 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2443 second=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002444 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002445
2446 # Likewise, but make microseconds resolve it.
2447 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2448 microsecond=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002449 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002450
2451 # Make t2 naive and it should fail.
2452 t2 = self.theclass.min
2453 self.assertRaises(TypeError, lambda: t1 == t2)
2454 self.assertEqual(t2, t2)
2455
2456 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2457 class Naive(tzinfo):
2458 def utcoffset(self, dt): return None
2459 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2460 self.assertRaises(TypeError, lambda: t1 == t2)
2461 self.assertEqual(t2, t2)
2462
2463 # OTOH, it's OK to compare two of these mixing the two ways of being
2464 # naive.
2465 t1 = self.theclass(5, 6, 7)
2466 self.assertEqual(t1, t2)
2467
2468 # Try a bogus uctoffset.
2469 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002470 def utcoffset(self, dt):
2471 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002472 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2473 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002474 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002475
Tim Peters2a799bf2002-12-16 20:18:38 +00002476 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002477 # Try one without a tzinfo.
2478 args = 6, 7, 23, 20, 59, 1, 64**2
2479 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002480 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002481 green = pickler.dumps(orig, proto)
2482 derived = unpickler.loads(green)
2483 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002484
2485 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002486 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002487 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002488 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002489 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002490 green = pickler.dumps(orig, proto)
2491 derived = unpickler.loads(green)
2492 self.assertEqual(orig, derived)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002493 self.assertTrue(isinstance(derived.tzinfo,
Tim Peters96940c92003-01-31 21:55:33 +00002494 PicklableFixedOffset))
2495 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2496 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002497
2498 def test_extreme_hashes(self):
2499 # If an attempt is made to hash these via subtracting the offset
2500 # then hashing a datetime object, OverflowError results. The
2501 # Python implementation used to blow up here.
2502 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2503 hash(t)
2504 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2505 tzinfo=FixedOffset(-1439, ""))
2506 hash(t)
2507
2508 # OTOH, an OOB offset should blow up.
2509 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2510 self.assertRaises(ValueError, hash, t)
2511
2512 def test_zones(self):
2513 est = FixedOffset(-300, "EST")
2514 utc = FixedOffset(0, "UTC")
2515 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002516 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2517 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2518 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002519 self.assertEqual(t1.tzinfo, est)
2520 self.assertEqual(t2.tzinfo, utc)
2521 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002522 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2523 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2524 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002525 self.assertEqual(t1.tzname(), "EST")
2526 self.assertEqual(t2.tzname(), "UTC")
2527 self.assertEqual(t3.tzname(), "MET")
2528 self.assertEqual(hash(t1), hash(t2))
2529 self.assertEqual(hash(t1), hash(t3))
2530 self.assertEqual(hash(t2), hash(t3))
2531 self.assertEqual(t1, t2)
2532 self.assertEqual(t1, t3)
2533 self.assertEqual(t2, t3)
2534 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2535 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2536 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002537 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002538 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2539 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2540 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2541
2542 def test_combine(self):
2543 met = FixedOffset(60, "MET")
2544 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002545 tz = time(18, 45, 3, 1234, tzinfo=met)
2546 dt = datetime.combine(d, tz)
2547 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002548 tzinfo=met))
2549
2550 def test_extract(self):
2551 met = FixedOffset(60, "MET")
2552 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2553 self.assertEqual(dt.date(), date(2002, 3, 4))
2554 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002555 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002556
2557 def test_tz_aware_arithmetic(self):
2558 import random
2559
2560 now = self.theclass.now()
2561 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002562 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002563 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002564 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002565 self.assertEqual(nowaware.timetz(), timeaware)
2566
2567 # Can't mix aware and non-aware.
2568 self.assertRaises(TypeError, lambda: now - nowaware)
2569 self.assertRaises(TypeError, lambda: nowaware - now)
2570
Tim Peters0bf60bd2003-01-08 20:40:01 +00002571 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002572 self.assertRaises(TypeError, lambda: now + nowaware)
2573 self.assertRaises(TypeError, lambda: nowaware + now)
2574 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2575
2576 # Subtracting should yield 0.
2577 self.assertEqual(now - now, timedelta(0))
2578 self.assertEqual(nowaware - nowaware, timedelta(0))
2579
2580 # Adding a delta should preserve tzinfo.
2581 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2582 nowawareplus = nowaware + delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002583 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002584 nowawareplus2 = delta + nowaware
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002585 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002586 self.assertEqual(nowawareplus, nowawareplus2)
2587
2588 # that - delta should be what we started with, and that - what we
2589 # started with should be delta.
2590 diff = nowawareplus - delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002591 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002592 self.assertEqual(nowaware, diff)
2593 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2594 self.assertEqual(nowawareplus - nowaware, delta)
2595
2596 # Make up a random timezone.
2597 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002598 # Attach it to nowawareplus.
2599 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002600 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002601 # Make sure the difference takes the timezone adjustments into account.
2602 got = nowaware - nowawareplus
2603 # Expected: (nowaware base - nowaware offset) -
2604 # (nowawareplus base - nowawareplus offset) =
2605 # (nowaware base - nowawareplus base) +
2606 # (nowawareplus offset - nowaware offset) =
2607 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002608 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002609 self.assertEqual(got, expected)
2610
2611 # Try max possible difference.
2612 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2613 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2614 tzinfo=FixedOffset(-1439, "max"))
2615 maxdiff = max - min
2616 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2617 timedelta(minutes=2*1439))
2618
2619 def test_tzinfo_now(self):
2620 meth = self.theclass.now
2621 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2622 base = meth()
2623 # Try with and without naming the keyword.
2624 off42 = FixedOffset(42, "42")
2625 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002626 again = meth(tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002627 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002628 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002629 # Bad argument with and w/o naming the keyword.
2630 self.assertRaises(TypeError, meth, 16)
2631 self.assertRaises(TypeError, meth, tzinfo=16)
2632 # Bad keyword name.
2633 self.assertRaises(TypeError, meth, tinfo=off42)
2634 # Too many args.
2635 self.assertRaises(TypeError, meth, off42, off42)
2636
Tim Peters10cadce2003-01-23 19:58:02 +00002637 # We don't know which time zone we're in, and don't have a tzinfo
2638 # class to represent it, so seeing whether a tz argument actually
2639 # does a conversion is tricky.
2640 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2641 utc = FixedOffset(0, "utc", 0)
2642 for dummy in range(3):
2643 now = datetime.now(weirdtz)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002644 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002645 utcnow = datetime.utcnow().replace(tzinfo=utc)
2646 now2 = utcnow.astimezone(weirdtz)
2647 if abs(now - now2) < timedelta(seconds=30):
2648 break
2649 # Else the code is broken, or more than 30 seconds passed between
2650 # calls; assuming the latter, just try again.
2651 else:
2652 # Three strikes and we're out.
2653 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2654
Tim Peters2a799bf2002-12-16 20:18:38 +00002655 def test_tzinfo_fromtimestamp(self):
2656 import time
2657 meth = self.theclass.fromtimestamp
2658 ts = time.time()
2659 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2660 base = meth(ts)
2661 # Try with and without naming the keyword.
2662 off42 = FixedOffset(42, "42")
2663 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002664 again = meth(ts, tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002665 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002666 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002667 # Bad argument with and w/o naming the keyword.
2668 self.assertRaises(TypeError, meth, ts, 16)
2669 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2670 # Bad keyword name.
2671 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2672 # Too many args.
2673 self.assertRaises(TypeError, meth, ts, off42, off42)
2674 # Too few args.
2675 self.assertRaises(TypeError, meth)
2676
Tim Peters2a44a8d2003-01-23 20:53:10 +00002677 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002678 timestamp = 1000000000
2679 utcdatetime = datetime.utcfromtimestamp(timestamp)
2680 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2681 # But on some flavor of Mac, it's nowhere near that. So we can't have
2682 # any idea here what time that actually is, we can only test that
2683 # relative changes match.
2684 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2685 tz = FixedOffset(utcoffset, "tz", 0)
2686 expected = utcdatetime + utcoffset
2687 got = datetime.fromtimestamp(timestamp, tz)
2688 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002689
Tim Peters2a799bf2002-12-16 20:18:38 +00002690 def test_tzinfo_utcnow(self):
2691 meth = self.theclass.utcnow
2692 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2693 base = meth()
2694 # Try with and without naming the keyword; for whatever reason,
2695 # utcnow() doesn't accept a tzinfo argument.
2696 off42 = FixedOffset(42, "42")
2697 self.assertRaises(TypeError, meth, off42)
2698 self.assertRaises(TypeError, meth, tzinfo=off42)
2699
2700 def test_tzinfo_utcfromtimestamp(self):
2701 import time
2702 meth = self.theclass.utcfromtimestamp
2703 ts = time.time()
2704 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2705 base = meth(ts)
2706 # Try with and without naming the keyword; for whatever reason,
2707 # utcfromtimestamp() doesn't accept a tzinfo argument.
2708 off42 = FixedOffset(42, "42")
2709 self.assertRaises(TypeError, meth, ts, off42)
2710 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2711
2712 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002713 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002714 # DST flag.
2715 class DST(tzinfo):
2716 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002717 if isinstance(dstvalue, int):
2718 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002719 self.dstvalue = dstvalue
2720 def dst(self, dt):
2721 return self.dstvalue
2722
2723 cls = self.theclass
2724 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2725 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2726 t = d.timetuple()
2727 self.assertEqual(1, t.tm_year)
2728 self.assertEqual(1, t.tm_mon)
2729 self.assertEqual(1, t.tm_mday)
2730 self.assertEqual(10, t.tm_hour)
2731 self.assertEqual(20, t.tm_min)
2732 self.assertEqual(30, t.tm_sec)
2733 self.assertEqual(0, t.tm_wday)
2734 self.assertEqual(1, t.tm_yday)
2735 self.assertEqual(flag, t.tm_isdst)
2736
2737 # dst() returns wrong type.
2738 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2739
2740 # dst() at the edge.
2741 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2742 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2743
2744 # dst() out of range.
2745 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2746 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2747
2748 def test_utctimetuple(self):
2749 class DST(tzinfo):
2750 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002751 if isinstance(dstvalue, int):
2752 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002753 self.dstvalue = dstvalue
2754 def dst(self, dt):
2755 return self.dstvalue
2756
2757 cls = self.theclass
2758 # This can't work: DST didn't implement utcoffset.
2759 self.assertRaises(NotImplementedError,
2760 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2761
2762 class UOFS(DST):
2763 def __init__(self, uofs, dofs=None):
2764 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002765 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002766 def utcoffset(self, dt):
2767 return self.uofs
2768
2769 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2770 # in effect for a UTC time.
2771 for dstvalue in -33, 33, 0, None:
2772 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2773 t = d.utctimetuple()
2774 self.assertEqual(d.year, t.tm_year)
2775 self.assertEqual(d.month, t.tm_mon)
2776 self.assertEqual(d.day, t.tm_mday)
2777 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2778 self.assertEqual(13, t.tm_min)
2779 self.assertEqual(d.second, t.tm_sec)
2780 self.assertEqual(d.weekday(), t.tm_wday)
2781 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2782 t.tm_yday)
2783 self.assertEqual(0, t.tm_isdst)
2784
2785 # At the edges, UTC adjustment can normalize into years out-of-range
2786 # for a datetime object. Ensure that a correct timetuple is
2787 # created anyway.
2788 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2789 # That goes back 1 minute less than a full day.
2790 t = tiny.utctimetuple()
2791 self.assertEqual(t.tm_year, MINYEAR-1)
2792 self.assertEqual(t.tm_mon, 12)
2793 self.assertEqual(t.tm_mday, 31)
2794 self.assertEqual(t.tm_hour, 0)
2795 self.assertEqual(t.tm_min, 1)
2796 self.assertEqual(t.tm_sec, 37)
2797 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2798 self.assertEqual(t.tm_isdst, 0)
2799
2800 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2801 # That goes forward 1 minute less than a full day.
2802 t = huge.utctimetuple()
2803 self.assertEqual(t.tm_year, MAXYEAR+1)
2804 self.assertEqual(t.tm_mon, 1)
2805 self.assertEqual(t.tm_mday, 1)
2806 self.assertEqual(t.tm_hour, 23)
2807 self.assertEqual(t.tm_min, 58)
2808 self.assertEqual(t.tm_sec, 37)
2809 self.assertEqual(t.tm_yday, 1)
2810 self.assertEqual(t.tm_isdst, 0)
2811
2812 def test_tzinfo_isoformat(self):
2813 zero = FixedOffset(0, "+00:00")
2814 plus = FixedOffset(220, "+03:40")
2815 minus = FixedOffset(-231, "-03:51")
2816 unknown = FixedOffset(None, "")
2817
2818 cls = self.theclass
2819 datestr = '0001-02-03'
2820 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002821 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002822 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2823 timestr = '04:05:59' + (us and '.987001' or '')
2824 ofsstr = ofs is not None and d.tzname() or ''
2825 tailstr = timestr + ofsstr
2826 iso = d.isoformat()
2827 self.assertEqual(iso, datestr + 'T' + tailstr)
2828 self.assertEqual(iso, d.isoformat('T'))
2829 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2830 self.assertEqual(str(d), datestr + ' ' + tailstr)
2831
Tim Peters12bf3392002-12-24 05:41:27 +00002832 def test_replace(self):
2833 cls = self.theclass
2834 z100 = FixedOffset(100, "+100")
2835 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2836 args = [1, 2, 3, 4, 5, 6, 7, z100]
2837 base = cls(*args)
2838 self.assertEqual(base, base.replace())
2839
2840 i = 0
2841 for name, newval in (("year", 2),
2842 ("month", 3),
2843 ("day", 4),
2844 ("hour", 5),
2845 ("minute", 6),
2846 ("second", 7),
2847 ("microsecond", 8),
2848 ("tzinfo", zm200)):
2849 newargs = args[:]
2850 newargs[i] = newval
2851 expected = cls(*newargs)
2852 got = base.replace(**{name: newval})
2853 self.assertEqual(expected, got)
2854 i += 1
2855
2856 # Ensure we can get rid of a tzinfo.
2857 self.assertEqual(base.tzname(), "+100")
2858 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002859 self.assertTrue(base2.tzinfo is None)
2860 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002861
2862 # Ensure we can add one.
2863 base3 = base2.replace(tzinfo=z100)
2864 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002865 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002866
2867 # Out of bounds.
2868 base = cls(2000, 2, 29)
2869 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002870
Tim Peters80475bb2002-12-25 07:40:55 +00002871 def test_more_astimezone(self):
2872 # The inherited test_astimezone covered some trivial and error cases.
2873 fnone = FixedOffset(None, "None")
2874 f44m = FixedOffset(44, "44")
2875 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2876
Tim Peters10cadce2003-01-23 19:58:02 +00002877 dt = self.theclass.now(tz=f44m)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002878 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002879 # Replacing with degenerate tzinfo raises an exception.
2880 self.assertRaises(ValueError, dt.astimezone, fnone)
2881 # Ditto with None tz.
2882 self.assertRaises(TypeError, dt.astimezone, None)
2883 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002884 x = dt.astimezone(dt.tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002885 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002886 self.assertEqual(x.date(), dt.date())
2887 self.assertEqual(x.time(), dt.time())
2888
2889 # Replacing with different tzinfo does adjust.
2890 got = dt.astimezone(fm5h)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002891 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002892 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2893 expected = dt - dt.utcoffset() # in effect, convert to UTC
2894 expected += fm5h.utcoffset(dt) # and from there to local time
2895 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2896 self.assertEqual(got.date(), expected.date())
2897 self.assertEqual(got.time(), expected.time())
2898 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002899 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002900 self.assertEqual(got, expected)
2901
Tim Peters4c0db782002-12-26 05:01:19 +00002902 def test_aware_subtract(self):
2903 cls = self.theclass
2904
Tim Peters60c76e42002-12-27 00:41:11 +00002905 # Ensure that utcoffset() is ignored when the operands have the
2906 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002907 class OperandDependentOffset(tzinfo):
2908 def utcoffset(self, t):
2909 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002910 # d0 and d1 equal after adjustment
2911 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002912 else:
Tim Peters397301e2003-01-02 21:28:08 +00002913 # d2 off in the weeds
2914 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002915
2916 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2917 d0 = base.replace(minute=3)
2918 d1 = base.replace(minute=9)
2919 d2 = base.replace(minute=11)
2920 for x in d0, d1, d2:
2921 for y in d0, d1, d2:
2922 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002923 expected = timedelta(minutes=x.minute - y.minute)
2924 self.assertEqual(got, expected)
2925
2926 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2927 # ignored.
2928 base = cls(8, 9, 10, 11, 12, 13, 14)
2929 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2930 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2931 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2932 for x in d0, d1, d2:
2933 for y in d0, d1, d2:
2934 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002935 if (x is d0 or x is d1) and (y is d0 or y is d1):
2936 expected = timedelta(0)
2937 elif x is y is d2:
2938 expected = timedelta(0)
2939 elif x is d2:
2940 expected = timedelta(minutes=(11-59)-0)
2941 else:
2942 assert y is d2
2943 expected = timedelta(minutes=0-(11-59))
2944 self.assertEqual(got, expected)
2945
Tim Peters60c76e42002-12-27 00:41:11 +00002946 def test_mixed_compare(self):
2947 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002948 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002949 self.assertEqual(t1, t2)
2950 t2 = t2.replace(tzinfo=None)
2951 self.assertEqual(t1, t2)
2952 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2953 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002954 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2955 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002956
Tim Peters0bf60bd2003-01-08 20:40:01 +00002957 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002958 class Varies(tzinfo):
2959 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002960 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002961 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002962 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002963 return self.offset
2964
2965 v = Varies()
2966 t1 = t2.replace(tzinfo=v)
2967 t2 = t2.replace(tzinfo=v)
2968 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2969 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2970 self.assertEqual(t1, t2)
2971
2972 # But if they're not identical, it isn't ignored.
2973 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002974 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002975
Tim Petersa98924a2003-05-17 05:55:19 +00002976 def test_subclass_datetimetz(self):
2977
2978 class C(self.theclass):
2979 theAnswer = 42
2980
2981 def __new__(cls, *args, **kws):
2982 temp = kws.copy()
2983 extra = temp.pop('extra')
2984 result = self.theclass.__new__(cls, *args, **temp)
2985 result.extra = extra
2986 return result
2987
2988 def newmeth(self, start):
2989 return start + self.hour + self.year
2990
2991 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2992
2993 dt1 = self.theclass(*args)
2994 dt2 = C(*args, **{'extra': 7})
2995
2996 self.assertEqual(dt2.__class__, C)
2997 self.assertEqual(dt2.theAnswer, 42)
2998 self.assertEqual(dt2.extra, 7)
2999 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3000 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3001
Tim Peters621818b2002-12-29 23:44:49 +00003002# Pain to set up DST-aware tzinfo classes.
3003
3004def first_sunday_on_or_after(dt):
3005 days_to_go = 6 - dt.weekday()
3006 if days_to_go:
3007 dt += timedelta(days_to_go)
3008 return dt
3009
3010ZERO = timedelta(0)
3011HOUR = timedelta(hours=1)
3012DAY = timedelta(days=1)
3013# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3014DSTSTART = datetime(1, 4, 1, 2)
3015# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003016# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3017# being standard time on that day, there is no spelling in local time of
3018# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3019DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003020
3021class USTimeZone(tzinfo):
3022
3023 def __init__(self, hours, reprname, stdname, dstname):
3024 self.stdoffset = timedelta(hours=hours)
3025 self.reprname = reprname
3026 self.stdname = stdname
3027 self.dstname = dstname
3028
3029 def __repr__(self):
3030 return self.reprname
3031
3032 def tzname(self, dt):
3033 if self.dst(dt):
3034 return self.dstname
3035 else:
3036 return self.stdname
3037
3038 def utcoffset(self, dt):
3039 return self.stdoffset + self.dst(dt)
3040
3041 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003042 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003043 # An exception instead may be sensible here, in one or more of
3044 # the cases.
3045 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003046 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003047
3048 # Find first Sunday in April.
3049 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3050 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3051
3052 # Find last Sunday in October.
3053 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3054 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3055
Tim Peters621818b2002-12-29 23:44:49 +00003056 # Can't compare naive to aware objects, so strip the timezone from
3057 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003058 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003059 return HOUR
3060 else:
3061 return ZERO
3062
Tim Peters521fc152002-12-31 17:36:56 +00003063Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3064Central = USTimeZone(-6, "Central", "CST", "CDT")
3065Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3066Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003067utc_real = FixedOffset(0, "UTC", 0)
3068# For better test coverage, we want another flavor of UTC that's west of
3069# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003070utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003071
3072class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003073 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003074 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003075 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003076
Tim Peters0bf60bd2003-01-08 20:40:01 +00003077 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003078
Tim Peters521fc152002-12-31 17:36:56 +00003079 # Check a time that's inside DST.
3080 def checkinside(self, dt, tz, utc, dston, dstoff):
3081 self.assertEqual(dt.dst(), HOUR)
3082
3083 # Conversion to our own timezone is always an identity.
3084 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003085
3086 asutc = dt.astimezone(utc)
3087 there_and_back = asutc.astimezone(tz)
3088
3089 # Conversion to UTC and back isn't always an identity here,
3090 # because there are redundant spellings (in local time) of
3091 # UTC time when DST begins: the clock jumps from 1:59:59
3092 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3093 # make sense then. The classes above treat 2:MM:SS as
3094 # daylight time then (it's "after 2am"), really an alias
3095 # for 1:MM:SS standard time. The latter form is what
3096 # conversion back from UTC produces.
3097 if dt.date() == dston.date() and dt.hour == 2:
3098 # We're in the redundant hour, and coming back from
3099 # UTC gives the 1:MM:SS standard-time spelling.
3100 self.assertEqual(there_and_back + HOUR, dt)
3101 # Although during was considered to be in daylight
3102 # time, there_and_back is not.
3103 self.assertEqual(there_and_back.dst(), ZERO)
3104 # They're the same times in UTC.
3105 self.assertEqual(there_and_back.astimezone(utc),
3106 dt.astimezone(utc))
3107 else:
3108 # We're not in the redundant hour.
3109 self.assertEqual(dt, there_and_back)
3110
Tim Peters327098a2003-01-20 22:54:38 +00003111 # Because we have a redundant spelling when DST begins, there is
3112 # (unforunately) an hour when DST ends that can't be spelled at all in
3113 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3114 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3115 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3116 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3117 # expressed in local time. Nevertheless, we want conversion back
3118 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003119 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003120 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003121 if dt.date() == dstoff.date() and dt.hour == 0:
3122 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003123 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003124 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3125 nexthour_utc += HOUR
3126 nexthour_tz = nexthour_utc.astimezone(tz)
3127 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003128 else:
Tim Peters327098a2003-01-20 22:54:38 +00003129 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003130
3131 # Check a time that's outside DST.
3132 def checkoutside(self, dt, tz, utc):
3133 self.assertEqual(dt.dst(), ZERO)
3134
3135 # Conversion to our own timezone is always an identity.
3136 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003137
3138 # Converting to UTC and back is an identity too.
3139 asutc = dt.astimezone(utc)
3140 there_and_back = asutc.astimezone(tz)
3141 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003142
Tim Peters1024bf82002-12-30 17:09:40 +00003143 def convert_between_tz_and_utc(self, tz, utc):
3144 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003145 # Because 1:MM on the day DST ends is taken as being standard time,
3146 # there is no spelling in tz for the last hour of daylight time.
3147 # For purposes of the test, the last hour of DST is 0:MM, which is
3148 # taken as being daylight time (and 1:MM is taken as being standard
3149 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003150 dstoff = self.dstoff.replace(tzinfo=tz)
3151 for delta in (timedelta(weeks=13),
3152 DAY,
3153 HOUR,
3154 timedelta(minutes=1),
3155 timedelta(microseconds=1)):
3156
Tim Peters521fc152002-12-31 17:36:56 +00003157 self.checkinside(dston, tz, utc, dston, dstoff)
3158 for during in dston + delta, dstoff - delta:
3159 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003160
Tim Peters521fc152002-12-31 17:36:56 +00003161 self.checkoutside(dstoff, tz, utc)
3162 for outside in dston - delta, dstoff + delta:
3163 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003164
Tim Peters621818b2002-12-29 23:44:49 +00003165 def test_easy(self):
3166 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003167 self.convert_between_tz_and_utc(Eastern, utc_real)
3168 self.convert_between_tz_and_utc(Pacific, utc_real)
3169 self.convert_between_tz_and_utc(Eastern, utc_fake)
3170 self.convert_between_tz_and_utc(Pacific, utc_fake)
3171 # The next is really dancing near the edge. It works because
3172 # Pacific and Eastern are far enough apart that their "problem
3173 # hours" don't overlap.
3174 self.convert_between_tz_and_utc(Eastern, Pacific)
3175 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003176 # OTOH, these fail! Don't enable them. The difficulty is that
3177 # the edge case tests assume that every hour is representable in
3178 # the "utc" class. This is always true for a fixed-offset tzinfo
3179 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3180 # For these adjacent DST-aware time zones, the range of time offsets
3181 # tested ends up creating hours in the one that aren't representable
3182 # in the other. For the same reason, we would see failures in the
3183 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3184 # offset deltas in convert_between_tz_and_utc().
3185 #
3186 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3187 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003188
Tim Petersf3615152003-01-01 21:51:37 +00003189 def test_tricky(self):
3190 # 22:00 on day before daylight starts.
3191 fourback = self.dston - timedelta(hours=4)
3192 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003193 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003194 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3195 # 2", we should get the 3 spelling.
3196 # If we plug 22:00 the day before into Eastern, it "looks like std
3197 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3198 # to 22:00 lands on 2:00, which makes no sense in local time (the
3199 # local clock jumps from 1 to 3). The point here is to make sure we
3200 # get the 3 spelling.
3201 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003202 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003203 self.assertEqual(expected, got)
3204
3205 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3206 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003207 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003208 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3209 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3210 # spelling.
3211 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003212 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003213 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003214
Tim Petersadf64202003-01-04 06:03:15 +00003215 # Now on the day DST ends, we want "repeat an hour" behavior.
3216 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3217 # EST 23:MM 0:MM 1:MM 2:MM
3218 # EDT 0:MM 1:MM 2:MM 3:MM
3219 # wall 0:MM 1:MM 1:MM 2:MM against these
3220 for utc in utc_real, utc_fake:
3221 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003222 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003223 # Convert that to UTC.
3224 first_std_hour -= tz.utcoffset(None)
3225 # Adjust for possibly fake UTC.
3226 asutc = first_std_hour + utc.utcoffset(None)
3227 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3228 # tz=Eastern.
3229 asutcbase = asutc.replace(tzinfo=utc)
3230 for tzhour in (0, 1, 1, 2):
3231 expectedbase = self.dstoff.replace(hour=tzhour)
3232 for minute in 0, 30, 59:
3233 expected = expectedbase.replace(minute=minute)
3234 asutc = asutcbase.replace(minute=minute)
3235 astz = asutc.astimezone(tz)
3236 self.assertEqual(astz.replace(tzinfo=None), expected)
3237 asutcbase += HOUR
3238
3239
Tim Peters710fb152003-01-02 19:35:54 +00003240 def test_bogus_dst(self):
3241 class ok(tzinfo):
3242 def utcoffset(self, dt): return HOUR
3243 def dst(self, dt): return HOUR
3244
3245 now = self.theclass.now().replace(tzinfo=utc_real)
3246 # Doesn't blow up.
3247 now.astimezone(ok())
3248
3249 # Does blow up.
3250 class notok(ok):
3251 def dst(self, dt): return None
3252 self.assertRaises(ValueError, now.astimezone, notok())
3253
Tim Peters52dcce22003-01-23 16:36:11 +00003254 def test_fromutc(self):
3255 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3256 now = datetime.utcnow().replace(tzinfo=utc_real)
3257 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3258 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3259 enow = Eastern.fromutc(now) # doesn't blow up
3260 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3261 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3262 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3263
3264 # Always converts UTC to standard time.
3265 class FauxUSTimeZone(USTimeZone):
3266 def fromutc(self, dt):
3267 return dt + self.stdoffset
3268 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3269
3270 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3271 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3272 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3273
3274 # Check around DST start.
3275 start = self.dston.replace(hour=4, tzinfo=Eastern)
3276 fstart = start.replace(tzinfo=FEastern)
3277 for wall in 23, 0, 1, 3, 4, 5:
3278 expected = start.replace(hour=wall)
3279 if wall == 23:
3280 expected -= timedelta(days=1)
3281 got = Eastern.fromutc(start)
3282 self.assertEqual(expected, got)
3283
3284 expected = fstart + FEastern.stdoffset
3285 got = FEastern.fromutc(fstart)
3286 self.assertEqual(expected, got)
3287
3288 # Ensure astimezone() calls fromutc() too.
3289 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3290 self.assertEqual(expected, got)
3291
3292 start += HOUR
3293 fstart += HOUR
3294
3295 # Check around DST end.
3296 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3297 fstart = start.replace(tzinfo=FEastern)
3298 for wall in 0, 1, 1, 2, 3, 4:
3299 expected = start.replace(hour=wall)
3300 got = Eastern.fromutc(start)
3301 self.assertEqual(expected, got)
3302
3303 expected = fstart + FEastern.stdoffset
3304 got = FEastern.fromutc(fstart)
3305 self.assertEqual(expected, got)
3306
3307 # Ensure astimezone() calls fromutc() too.
3308 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3309 self.assertEqual(expected, got)
3310
3311 start += HOUR
3312 fstart += HOUR
3313
Tim Peters710fb152003-01-02 19:35:54 +00003314
Tim Peters528ca532004-09-16 01:30:50 +00003315#############################################################################
3316# oddballs
3317
3318class Oddballs(unittest.TestCase):
3319
3320 def test_bug_1028306(self):
3321 # Trying to compare a date to a datetime should act like a mixed-
3322 # type comparison, despite that datetime is a subclass of date.
3323 as_date = date.today()
3324 as_datetime = datetime.combine(as_date, time())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003325 self.assertTrue(as_date != as_datetime)
3326 self.assertTrue(as_datetime != as_date)
3327 self.assertTrue(not as_date == as_datetime)
3328 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003329 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3330 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3331 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3332 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3333 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3334 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3335 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3336 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3337
3338 # Neverthelss, comparison should work with the base-class (date)
3339 # projection if use of a date method is forced.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003340 self.assertTrue(as_date.__eq__(as_datetime))
Tim Peters528ca532004-09-16 01:30:50 +00003341 different_day = (as_date.day + 1) % 20 + 1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003342 self.assertTrue(not as_date.__eq__(as_datetime.replace(day=
Tim Peters528ca532004-09-16 01:30:50 +00003343 different_day)))
3344
3345 # And date should compare with other subclasses of date. If a
3346 # subclass wants to stop this, it's up to the subclass to do so.
3347 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3348 self.assertEqual(as_date, date_sc)
3349 self.assertEqual(date_sc, as_date)
3350
3351 # Ditto for datetimes.
3352 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3353 as_date.day, 0, 0, 0)
3354 self.assertEqual(as_datetime, datetime_sc)
3355 self.assertEqual(datetime_sc, as_datetime)
3356
Tim Peters2a799bf2002-12-16 20:18:38 +00003357def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003358 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003359
3360if __name__ == "__main__":
3361 test_main()