blob: 1fb3d1bf2009fc67206d9bc08a4d261b8dd2f343 [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")
1183 # str is ISO format with the separator forced to a blank.
1184 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1185
1186 t = self.theclass(2, 3, 2)
1187 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1188 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1189 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1190 # str is ISO format with the separator forced to a blank.
1191 self.assertEqual(str(t), "0002-03-02 00:00:00")
1192
Eric Smitha9f7d622008-02-17 19:46:49 +00001193 def test_format(self):
1194 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1195 self.assertEqual(dt.__format__(''), str(dt))
1196
1197 # check that a derived class's __str__() gets called
1198 class A(self.theclass):
1199 def __str__(self):
1200 return 'A'
1201 a = A(2007, 9, 10, 4, 5, 1, 123)
1202 self.assertEqual(a.__format__(''), 'A')
1203
1204 # check that a derived class's strftime gets called
1205 class B(self.theclass):
1206 def strftime(self, format_spec):
1207 return 'B'
1208 b = B(2007, 9, 10, 4, 5, 1, 123)
1209 self.assertEqual(b.__format__(''), str(dt))
1210
1211 for fmt in ["m:%m d:%d y:%y",
1212 "m:%m d:%d y:%y H:%H M:%M S:%S",
1213 "%z %Z",
1214 ]:
1215 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1216 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1217 self.assertEqual(b.__format__(fmt), 'B')
1218
Tim Peters2a799bf2002-12-16 20:18:38 +00001219 def test_more_ctime(self):
1220 # Test fields that TestDate doesn't touch.
1221 import time
1222
1223 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1224 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1225 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1226 # out. The difference is that t.ctime() produces " 2" for the day,
1227 # but platform ctime() produces "02" for the day. According to
1228 # C99, t.ctime() is correct here.
1229 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1230
1231 # So test a case where that difference doesn't matter.
1232 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1233 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1234
1235 def test_tz_independent_comparing(self):
1236 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1237 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1238 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1239 self.assertEqual(dt1, dt3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001240 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001241
1242 # Make sure comparison doesn't forget microseconds, and isn't done
1243 # via comparing a float timestamp (an IEEE double doesn't have enough
1244 # precision to span microsecond resolution across years 1 thru 9999,
1245 # so comparing via timestamp necessarily calls some distinct values
1246 # equal).
1247 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1248 us = timedelta(microseconds=1)
1249 dt2 = dt1 + us
1250 self.assertEqual(dt2 - dt1, us)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001251 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001252
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001253 def test_strftime_with_bad_tzname_replace(self):
1254 # verify ok if tzinfo.tzname().replace() returns a non-string
1255 class MyTzInfo(FixedOffset):
1256 def tzname(self, dt):
1257 class MyStr(str):
1258 def replace(self, *args):
1259 return None
1260 return MyStr('name')
1261 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1262 self.assertRaises(TypeError, t.strftime, '%Z')
1263
Tim Peters2a799bf2002-12-16 20:18:38 +00001264 def test_bad_constructor_arguments(self):
1265 # bad years
1266 self.theclass(MINYEAR, 1, 1) # no exception
1267 self.theclass(MAXYEAR, 1, 1) # no exception
1268 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1269 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1270 # bad months
1271 self.theclass(2000, 1, 1) # no exception
1272 self.theclass(2000, 12, 1) # no exception
1273 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1274 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1275 # bad days
1276 self.theclass(2000, 2, 29) # no exception
1277 self.theclass(2004, 2, 29) # no exception
1278 self.theclass(2400, 2, 29) # no exception
1279 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1280 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1281 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1282 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1283 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1284 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1285 # bad hours
1286 self.theclass(2000, 1, 31, 0) # no exception
1287 self.theclass(2000, 1, 31, 23) # no exception
1288 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1289 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1290 # bad minutes
1291 self.theclass(2000, 1, 31, 23, 0) # no exception
1292 self.theclass(2000, 1, 31, 23, 59) # no exception
1293 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1294 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1295 # bad seconds
1296 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1297 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1298 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1299 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1300 # bad microseconds
1301 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1302 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1303 self.assertRaises(ValueError, self.theclass,
1304 2000, 1, 31, 23, 59, 59, -1)
1305 self.assertRaises(ValueError, self.theclass,
1306 2000, 1, 31, 23, 59, 59,
1307 1000000)
1308
1309 def test_hash_equality(self):
1310 d = self.theclass(2000, 12, 31, 23, 30, 17)
1311 e = self.theclass(2000, 12, 31, 23, 30, 17)
1312 self.assertEqual(d, e)
1313 self.assertEqual(hash(d), hash(e))
1314
1315 dic = {d: 1}
1316 dic[e] = 2
1317 self.assertEqual(len(dic), 1)
1318 self.assertEqual(dic[d], 2)
1319 self.assertEqual(dic[e], 2)
1320
1321 d = self.theclass(2001, 1, 1, 0, 5, 17)
1322 e = self.theclass(2001, 1, 1, 0, 5, 17)
1323 self.assertEqual(d, e)
1324 self.assertEqual(hash(d), hash(e))
1325
1326 dic = {d: 1}
1327 dic[e] = 2
1328 self.assertEqual(len(dic), 1)
1329 self.assertEqual(dic[d], 2)
1330 self.assertEqual(dic[e], 2)
1331
1332 def test_computations(self):
1333 a = self.theclass(2002, 1, 31)
1334 b = self.theclass(1956, 1, 31)
1335 diff = a-b
1336 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1337 self.assertEqual(diff.seconds, 0)
1338 self.assertEqual(diff.microseconds, 0)
1339 a = self.theclass(2002, 3, 2, 17, 6)
1340 millisec = timedelta(0, 0, 1000)
1341 hour = timedelta(0, 3600)
1342 day = timedelta(1)
1343 week = timedelta(7)
1344 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1345 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1346 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1347 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1348 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1349 self.assertEqual(a - hour, a + -hour)
1350 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1351 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1352 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1353 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1354 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1355 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1356 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1357 self.assertEqual((a + week) - a, week)
1358 self.assertEqual((a + day) - a, day)
1359 self.assertEqual((a + hour) - a, hour)
1360 self.assertEqual((a + millisec) - a, millisec)
1361 self.assertEqual((a - week) - a, -week)
1362 self.assertEqual((a - day) - a, -day)
1363 self.assertEqual((a - hour) - a, -hour)
1364 self.assertEqual((a - millisec) - a, -millisec)
1365 self.assertEqual(a - (a + week), -week)
1366 self.assertEqual(a - (a + day), -day)
1367 self.assertEqual(a - (a + hour), -hour)
1368 self.assertEqual(a - (a + millisec), -millisec)
1369 self.assertEqual(a - (a - week), week)
1370 self.assertEqual(a - (a - day), day)
1371 self.assertEqual(a - (a - hour), hour)
1372 self.assertEqual(a - (a - millisec), millisec)
1373 self.assertEqual(a + (week + day + hour + millisec),
1374 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1375 self.assertEqual(a + (week + day + hour + millisec),
1376 (((a + week) + day) + hour) + millisec)
1377 self.assertEqual(a - (week + day + hour + millisec),
1378 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1379 self.assertEqual(a - (week + day + hour + millisec),
1380 (((a - week) - day) - hour) - millisec)
1381 # Add/sub ints, longs, floats should be illegal
1382 for i in 1, 1L, 1.0:
1383 self.assertRaises(TypeError, lambda: a+i)
1384 self.assertRaises(TypeError, lambda: a-i)
1385 self.assertRaises(TypeError, lambda: i+a)
1386 self.assertRaises(TypeError, lambda: i-a)
1387
1388 # delta - datetime is senseless.
1389 self.assertRaises(TypeError, lambda: day - a)
1390 # mixing datetime and (delta or datetime) via * or // is senseless
1391 self.assertRaises(TypeError, lambda: day * a)
1392 self.assertRaises(TypeError, lambda: a * day)
1393 self.assertRaises(TypeError, lambda: day // a)
1394 self.assertRaises(TypeError, lambda: a // day)
1395 self.assertRaises(TypeError, lambda: a * a)
1396 self.assertRaises(TypeError, lambda: a // a)
1397 # datetime + datetime is senseless
1398 self.assertRaises(TypeError, lambda: a + a)
1399
1400 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001401 args = 6, 7, 23, 20, 59, 1, 64**2
1402 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001403 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001404 green = pickler.dumps(orig, proto)
1405 derived = unpickler.loads(green)
1406 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001407
Guido van Rossum275666f2003-02-07 21:49:01 +00001408 def test_more_pickling(self):
1409 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1410 s = pickle.dumps(a)
1411 b = pickle.loads(s)
1412 self.assertEqual(b.year, 2003)
1413 self.assertEqual(b.month, 2)
1414 self.assertEqual(b.day, 7)
1415
Tim Peters604c0132004-06-07 23:04:33 +00001416 def test_pickling_subclass_datetime(self):
1417 args = 6, 7, 23, 20, 59, 1, 64**2
1418 orig = SubclassDatetime(*args)
1419 for pickler, unpickler, proto in pickle_choices:
1420 green = pickler.dumps(orig, proto)
1421 derived = unpickler.loads(green)
1422 self.assertEqual(orig, derived)
1423
Tim Peters2a799bf2002-12-16 20:18:38 +00001424 def test_more_compare(self):
1425 # The test_compare() inherited from TestDate covers the error cases.
1426 # We just want to test lexicographic ordering on the members datetime
1427 # has that date lacks.
1428 args = [2000, 11, 29, 20, 58, 16, 999998]
1429 t1 = self.theclass(*args)
1430 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001431 self.assertTrue(t1 == t2)
1432 self.assertTrue(t1 <= t2)
1433 self.assertTrue(t1 >= t2)
1434 self.assertTrue(not t1 != t2)
1435 self.assertTrue(not t1 < t2)
1436 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001437 self.assertEqual(cmp(t1, t2), 0)
1438 self.assertEqual(cmp(t2, t1), 0)
1439
1440 for i in range(len(args)):
1441 newargs = args[:]
1442 newargs[i] = args[i] + 1
1443 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001444 self.assertTrue(t1 < t2)
1445 self.assertTrue(t2 > t1)
1446 self.assertTrue(t1 <= t2)
1447 self.assertTrue(t2 >= t1)
1448 self.assertTrue(t1 != t2)
1449 self.assertTrue(t2 != t1)
1450 self.assertTrue(not t1 == t2)
1451 self.assertTrue(not t2 == t1)
1452 self.assertTrue(not t1 > t2)
1453 self.assertTrue(not t2 < t1)
1454 self.assertTrue(not t1 >= t2)
1455 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001456 self.assertEqual(cmp(t1, t2), -1)
1457 self.assertEqual(cmp(t2, t1), 1)
1458
1459
1460 # A helper for timestamp constructor tests.
1461 def verify_field_equality(self, expected, got):
1462 self.assertEqual(expected.tm_year, got.year)
1463 self.assertEqual(expected.tm_mon, got.month)
1464 self.assertEqual(expected.tm_mday, got.day)
1465 self.assertEqual(expected.tm_hour, got.hour)
1466 self.assertEqual(expected.tm_min, got.minute)
1467 self.assertEqual(expected.tm_sec, got.second)
1468
1469 def test_fromtimestamp(self):
1470 import time
1471
1472 ts = time.time()
1473 expected = time.localtime(ts)
1474 got = self.theclass.fromtimestamp(ts)
1475 self.verify_field_equality(expected, got)
1476
1477 def test_utcfromtimestamp(self):
1478 import time
1479
1480 ts = time.time()
1481 expected = time.gmtime(ts)
1482 got = self.theclass.utcfromtimestamp(ts)
1483 self.verify_field_equality(expected, got)
1484
Georg Brandl6d78a582006-04-28 19:09:24 +00001485 def test_microsecond_rounding(self):
1486 # Test whether fromtimestamp "rounds up" floats that are less
1487 # than one microsecond smaller than an integer.
1488 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1489 self.theclass.fromtimestamp(1))
1490
Tim Peters1b6f7a92004-06-20 02:50:16 +00001491 def test_insane_fromtimestamp(self):
1492 # It's possible that some platform maps time_t to double,
1493 # and that this test will fail there. This test should
1494 # exempt such platforms (provided they return reasonable
1495 # results!).
1496 for insane in -1e200, 1e200:
1497 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1498 insane)
1499
1500 def test_insane_utcfromtimestamp(self):
1501 # It's possible that some platform maps time_t to double,
1502 # and that this test will fail there. This test should
1503 # exempt such platforms (provided they return reasonable
1504 # results!).
1505 for insane in -1e200, 1e200:
1506 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1507 insane)
1508
Guido van Rossum2054ee92007-03-06 15:50:01 +00001509 def test_negative_float_fromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001510 # Windows doesn't accept negative timestamps
1511 if os.name == "nt":
1512 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001513 # The result is tz-dependent; at least test that this doesn't
1514 # fail (like it did before bug 1646728 was fixed).
1515 self.theclass.fromtimestamp(-1.05)
1516
1517 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001518 # Windows doesn't accept negative timestamps
1519 if os.name == "nt":
1520 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001521 d = self.theclass.utcfromtimestamp(-1.05)
1522 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1523
Tim Peters2a799bf2002-12-16 20:18:38 +00001524 def test_utcnow(self):
1525 import time
1526
1527 # Call it a success if utcnow() and utcfromtimestamp() are within
1528 # a second of each other.
1529 tolerance = timedelta(seconds=1)
1530 for dummy in range(3):
1531 from_now = self.theclass.utcnow()
1532 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1533 if abs(from_timestamp - from_now) <= tolerance:
1534 break
1535 # Else try again a few times.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001536 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001537
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001538 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001539 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001540
Skip Montanarofc070d22008-03-15 16:04:45 +00001541 string = '2004-12-01 13:02:47.197'
1542 format = '%Y-%m-%d %H:%M:%S.%f'
1543 result, frac = _strptime._strptime(string, format)
1544 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001545 got = self.theclass.strptime(string, format)
1546 self.assertEqual(expected, got)
1547
Tim Peters2a799bf2002-12-16 20:18:38 +00001548 def test_more_timetuple(self):
1549 # This tests fields beyond those tested by the TestDate.test_timetuple.
1550 t = self.theclass(2004, 12, 31, 6, 22, 33)
1551 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1552 self.assertEqual(t.timetuple(),
1553 (t.year, t.month, t.day,
1554 t.hour, t.minute, t.second,
1555 t.weekday(),
1556 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1557 -1))
1558 tt = t.timetuple()
1559 self.assertEqual(tt.tm_year, t.year)
1560 self.assertEqual(tt.tm_mon, t.month)
1561 self.assertEqual(tt.tm_mday, t.day)
1562 self.assertEqual(tt.tm_hour, t.hour)
1563 self.assertEqual(tt.tm_min, t.minute)
1564 self.assertEqual(tt.tm_sec, t.second)
1565 self.assertEqual(tt.tm_wday, t.weekday())
1566 self.assertEqual(tt.tm_yday, t.toordinal() -
1567 date(t.year, 1, 1).toordinal() + 1)
1568 self.assertEqual(tt.tm_isdst, -1)
1569
1570 def test_more_strftime(self):
1571 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001572 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1573 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1574 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001575
1576 def test_extract(self):
1577 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1578 self.assertEqual(dt.date(), date(2002, 3, 4))
1579 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1580
1581 def test_combine(self):
1582 d = date(2002, 3, 4)
1583 t = time(18, 45, 3, 1234)
1584 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1585 combine = self.theclass.combine
1586 dt = combine(d, t)
1587 self.assertEqual(dt, expected)
1588
1589 dt = combine(time=t, date=d)
1590 self.assertEqual(dt, expected)
1591
1592 self.assertEqual(d, dt.date())
1593 self.assertEqual(t, dt.time())
1594 self.assertEqual(dt, combine(dt.date(), dt.time()))
1595
1596 self.assertRaises(TypeError, combine) # need an arg
1597 self.assertRaises(TypeError, combine, d) # need two args
1598 self.assertRaises(TypeError, combine, t, d) # args reversed
1599 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1600 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1601
Tim Peters12bf3392002-12-24 05:41:27 +00001602 def test_replace(self):
1603 cls = self.theclass
1604 args = [1, 2, 3, 4, 5, 6, 7]
1605 base = cls(*args)
1606 self.assertEqual(base, base.replace())
1607
1608 i = 0
1609 for name, newval in (("year", 2),
1610 ("month", 3),
1611 ("day", 4),
1612 ("hour", 5),
1613 ("minute", 6),
1614 ("second", 7),
1615 ("microsecond", 8)):
1616 newargs = args[:]
1617 newargs[i] = newval
1618 expected = cls(*newargs)
1619 got = base.replace(**{name: newval})
1620 self.assertEqual(expected, got)
1621 i += 1
1622
1623 # Out of bounds.
1624 base = cls(2000, 2, 29)
1625 self.assertRaises(ValueError, base.replace, year=2001)
1626
Tim Peters80475bb2002-12-25 07:40:55 +00001627 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001628 # Pretty boring! The TZ test is more interesting here. astimezone()
1629 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001630 dt = self.theclass.now()
1631 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001632 self.assertRaises(TypeError, dt.astimezone) # not enough args
1633 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1634 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001635 self.assertRaises(ValueError, dt.astimezone, f) # naive
1636 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001637
Tim Peters52dcce22003-01-23 16:36:11 +00001638 class Bogus(tzinfo):
1639 def utcoffset(self, dt): return None
1640 def dst(self, dt): return timedelta(0)
1641 bog = Bogus()
1642 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1643
1644 class AlsoBogus(tzinfo):
1645 def utcoffset(self, dt): return timedelta(0)
1646 def dst(self, dt): return None
1647 alsobog = AlsoBogus()
1648 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001649
Tim Petersa98924a2003-05-17 05:55:19 +00001650 def test_subclass_datetime(self):
1651
1652 class C(self.theclass):
1653 theAnswer = 42
1654
1655 def __new__(cls, *args, **kws):
1656 temp = kws.copy()
1657 extra = temp.pop('extra')
1658 result = self.theclass.__new__(cls, *args, **temp)
1659 result.extra = extra
1660 return result
1661
1662 def newmeth(self, start):
1663 return start + self.year + self.month + self.second
1664
1665 args = 2003, 4, 14, 12, 13, 41
1666
1667 dt1 = self.theclass(*args)
1668 dt2 = C(*args, **{'extra': 7})
1669
1670 self.assertEqual(dt2.__class__, C)
1671 self.assertEqual(dt2.theAnswer, 42)
1672 self.assertEqual(dt2.extra, 7)
1673 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1674 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1675 dt1.second - 7)
1676
Tim Peters604c0132004-06-07 23:04:33 +00001677class SubclassTime(time):
1678 sub_var = 1
1679
Collin Winterc2898c52007-04-25 17:29:52 +00001680class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001681
1682 theclass = time
1683
1684 def test_basic_attributes(self):
1685 t = self.theclass(12, 0)
1686 self.assertEqual(t.hour, 12)
1687 self.assertEqual(t.minute, 0)
1688 self.assertEqual(t.second, 0)
1689 self.assertEqual(t.microsecond, 0)
1690
1691 def test_basic_attributes_nonzero(self):
1692 # Make sure all attributes are non-zero so bugs in
1693 # bit-shifting access show up.
1694 t = self.theclass(12, 59, 59, 8000)
1695 self.assertEqual(t.hour, 12)
1696 self.assertEqual(t.minute, 59)
1697 self.assertEqual(t.second, 59)
1698 self.assertEqual(t.microsecond, 8000)
1699
1700 def test_roundtrip(self):
1701 t = self.theclass(1, 2, 3, 4)
1702
1703 # Verify t -> string -> time identity.
1704 s = repr(t)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001705 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001706 s = s[9:]
1707 t2 = eval(s)
1708 self.assertEqual(t, t2)
1709
1710 # Verify identity via reconstructing from pieces.
1711 t2 = self.theclass(t.hour, t.minute, t.second,
1712 t.microsecond)
1713 self.assertEqual(t, t2)
1714
1715 def test_comparing(self):
1716 args = [1, 2, 3, 4]
1717 t1 = self.theclass(*args)
1718 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001719 self.assertTrue(t1 == t2)
1720 self.assertTrue(t1 <= t2)
1721 self.assertTrue(t1 >= t2)
1722 self.assertTrue(not t1 != t2)
1723 self.assertTrue(not t1 < t2)
1724 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001725 self.assertEqual(cmp(t1, t2), 0)
1726 self.assertEqual(cmp(t2, t1), 0)
1727
1728 for i in range(len(args)):
1729 newargs = args[:]
1730 newargs[i] = args[i] + 1
1731 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001732 self.assertTrue(t1 < t2)
1733 self.assertTrue(t2 > t1)
1734 self.assertTrue(t1 <= t2)
1735 self.assertTrue(t2 >= t1)
1736 self.assertTrue(t1 != t2)
1737 self.assertTrue(t2 != t1)
1738 self.assertTrue(not t1 == t2)
1739 self.assertTrue(not t2 == t1)
1740 self.assertTrue(not t1 > t2)
1741 self.assertTrue(not t2 < t1)
1742 self.assertTrue(not t1 >= t2)
1743 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001744 self.assertEqual(cmp(t1, t2), -1)
1745 self.assertEqual(cmp(t2, t1), 1)
1746
Tim Peters68124bb2003-02-08 03:46:31 +00001747 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001748 self.assertEqual(t1 == badarg, False)
1749 self.assertEqual(t1 != badarg, True)
1750 self.assertEqual(badarg == t1, False)
1751 self.assertEqual(badarg != t1, True)
1752
Tim Peters2a799bf2002-12-16 20:18:38 +00001753 self.assertRaises(TypeError, lambda: t1 <= badarg)
1754 self.assertRaises(TypeError, lambda: t1 < badarg)
1755 self.assertRaises(TypeError, lambda: t1 > badarg)
1756 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001757 self.assertRaises(TypeError, lambda: badarg <= t1)
1758 self.assertRaises(TypeError, lambda: badarg < t1)
1759 self.assertRaises(TypeError, lambda: badarg > t1)
1760 self.assertRaises(TypeError, lambda: badarg >= t1)
1761
1762 def test_bad_constructor_arguments(self):
1763 # bad hours
1764 self.theclass(0, 0) # no exception
1765 self.theclass(23, 0) # no exception
1766 self.assertRaises(ValueError, self.theclass, -1, 0)
1767 self.assertRaises(ValueError, self.theclass, 24, 0)
1768 # bad minutes
1769 self.theclass(23, 0) # no exception
1770 self.theclass(23, 59) # no exception
1771 self.assertRaises(ValueError, self.theclass, 23, -1)
1772 self.assertRaises(ValueError, self.theclass, 23, 60)
1773 # bad seconds
1774 self.theclass(23, 59, 0) # no exception
1775 self.theclass(23, 59, 59) # no exception
1776 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1777 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1778 # bad microseconds
1779 self.theclass(23, 59, 59, 0) # no exception
1780 self.theclass(23, 59, 59, 999999) # no exception
1781 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1782 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1783
1784 def test_hash_equality(self):
1785 d = self.theclass(23, 30, 17)
1786 e = self.theclass(23, 30, 17)
1787 self.assertEqual(d, e)
1788 self.assertEqual(hash(d), hash(e))
1789
1790 dic = {d: 1}
1791 dic[e] = 2
1792 self.assertEqual(len(dic), 1)
1793 self.assertEqual(dic[d], 2)
1794 self.assertEqual(dic[e], 2)
1795
1796 d = self.theclass(0, 5, 17)
1797 e = self.theclass(0, 5, 17)
1798 self.assertEqual(d, e)
1799 self.assertEqual(hash(d), hash(e))
1800
1801 dic = {d: 1}
1802 dic[e] = 2
1803 self.assertEqual(len(dic), 1)
1804 self.assertEqual(dic[d], 2)
1805 self.assertEqual(dic[e], 2)
1806
1807 def test_isoformat(self):
1808 t = self.theclass(4, 5, 1, 123)
1809 self.assertEqual(t.isoformat(), "04:05:01.000123")
1810 self.assertEqual(t.isoformat(), str(t))
1811
1812 t = self.theclass()
1813 self.assertEqual(t.isoformat(), "00:00:00")
1814 self.assertEqual(t.isoformat(), str(t))
1815
1816 t = self.theclass(microsecond=1)
1817 self.assertEqual(t.isoformat(), "00:00:00.000001")
1818 self.assertEqual(t.isoformat(), str(t))
1819
1820 t = self.theclass(microsecond=10)
1821 self.assertEqual(t.isoformat(), "00:00:00.000010")
1822 self.assertEqual(t.isoformat(), str(t))
1823
1824 t = self.theclass(microsecond=100)
1825 self.assertEqual(t.isoformat(), "00:00:00.000100")
1826 self.assertEqual(t.isoformat(), str(t))
1827
1828 t = self.theclass(microsecond=1000)
1829 self.assertEqual(t.isoformat(), "00:00:00.001000")
1830 self.assertEqual(t.isoformat(), str(t))
1831
1832 t = self.theclass(microsecond=10000)
1833 self.assertEqual(t.isoformat(), "00:00:00.010000")
1834 self.assertEqual(t.isoformat(), str(t))
1835
1836 t = self.theclass(microsecond=100000)
1837 self.assertEqual(t.isoformat(), "00:00:00.100000")
1838 self.assertEqual(t.isoformat(), str(t))
1839
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001840 def test_1653736(self):
1841 # verify it doesn't accept extra keyword arguments
1842 t = self.theclass(second=1)
1843 self.assertRaises(TypeError, t.isoformat, foo=3)
1844
Tim Peters2a799bf2002-12-16 20:18:38 +00001845 def test_strftime(self):
1846 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001847 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001848 # A naive object replaces %z and %Z with empty strings.
1849 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1850
Eric Smitha9f7d622008-02-17 19:46:49 +00001851 def test_format(self):
1852 t = self.theclass(1, 2, 3, 4)
1853 self.assertEqual(t.__format__(''), str(t))
1854
1855 # check that a derived class's __str__() gets called
1856 class A(self.theclass):
1857 def __str__(self):
1858 return 'A'
1859 a = A(1, 2, 3, 4)
1860 self.assertEqual(a.__format__(''), 'A')
1861
1862 # check that a derived class's strftime gets called
1863 class B(self.theclass):
1864 def strftime(self, format_spec):
1865 return 'B'
1866 b = B(1, 2, 3, 4)
1867 self.assertEqual(b.__format__(''), str(t))
1868
1869 for fmt in ['%H %M %S',
1870 ]:
1871 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1872 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1873 self.assertEqual(b.__format__(fmt), 'B')
1874
Tim Peters2a799bf2002-12-16 20:18:38 +00001875 def test_str(self):
1876 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1877 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1878 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1879 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1880 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1881
1882 def test_repr(self):
1883 name = 'datetime.' + self.theclass.__name__
1884 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1885 "%s(1, 2, 3, 4)" % name)
1886 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1887 "%s(10, 2, 3, 4000)" % name)
1888 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1889 "%s(0, 2, 3, 400000)" % name)
1890 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1891 "%s(12, 2, 3)" % name)
1892 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1893 "%s(23, 15)" % name)
1894
1895 def test_resolution_info(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001896 self.assertTrue(isinstance(self.theclass.min, self.theclass))
1897 self.assertTrue(isinstance(self.theclass.max, self.theclass))
1898 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
1899 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001900
1901 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001902 args = 20, 59, 16, 64**2
1903 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001904 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001905 green = pickler.dumps(orig, proto)
1906 derived = unpickler.loads(green)
1907 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001908
Tim Peters604c0132004-06-07 23:04:33 +00001909 def test_pickling_subclass_time(self):
1910 args = 20, 59, 16, 64**2
1911 orig = SubclassTime(*args)
1912 for pickler, unpickler, proto in pickle_choices:
1913 green = pickler.dumps(orig, proto)
1914 derived = unpickler.loads(green)
1915 self.assertEqual(orig, derived)
1916
Tim Peters2a799bf2002-12-16 20:18:38 +00001917 def test_bool(self):
1918 cls = self.theclass
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001919 self.assertTrue(cls(1))
1920 self.assertTrue(cls(0, 1))
1921 self.assertTrue(cls(0, 0, 1))
1922 self.assertTrue(cls(0, 0, 0, 1))
1923 self.assertTrue(not cls(0))
1924 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001925
Tim Peters12bf3392002-12-24 05:41:27 +00001926 def test_replace(self):
1927 cls = self.theclass
1928 args = [1, 2, 3, 4]
1929 base = cls(*args)
1930 self.assertEqual(base, base.replace())
1931
1932 i = 0
1933 for name, newval in (("hour", 5),
1934 ("minute", 6),
1935 ("second", 7),
1936 ("microsecond", 8)):
1937 newargs = args[:]
1938 newargs[i] = newval
1939 expected = cls(*newargs)
1940 got = base.replace(**{name: newval})
1941 self.assertEqual(expected, got)
1942 i += 1
1943
1944 # Out of bounds.
1945 base = cls(1)
1946 self.assertRaises(ValueError, base.replace, hour=24)
1947 self.assertRaises(ValueError, base.replace, minute=-1)
1948 self.assertRaises(ValueError, base.replace, second=100)
1949 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1950
Tim Petersa98924a2003-05-17 05:55:19 +00001951 def test_subclass_time(self):
1952
1953 class C(self.theclass):
1954 theAnswer = 42
1955
1956 def __new__(cls, *args, **kws):
1957 temp = kws.copy()
1958 extra = temp.pop('extra')
1959 result = self.theclass.__new__(cls, *args, **temp)
1960 result.extra = extra
1961 return result
1962
1963 def newmeth(self, start):
1964 return start + self.hour + self.second
1965
1966 args = 4, 5, 6
1967
1968 dt1 = self.theclass(*args)
1969 dt2 = C(*args, **{'extra': 7})
1970
1971 self.assertEqual(dt2.__class__, C)
1972 self.assertEqual(dt2.theAnswer, 42)
1973 self.assertEqual(dt2.extra, 7)
1974 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1975 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1976
Armin Rigof4afb212005-11-07 07:15:48 +00001977 def test_backdoor_resistance(self):
1978 # see TestDate.test_backdoor_resistance().
1979 base = '2:59.0'
1980 for hour_byte in ' ', '9', chr(24), '\xff':
1981 self.assertRaises(TypeError, self.theclass,
1982 hour_byte + base[1:])
1983
Tim Peters855fe882002-12-22 03:43:39 +00001984# A mixin for classes with a tzinfo= argument. Subclasses must define
1985# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001986# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001987class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001988
Tim Petersbad8ff02002-12-30 20:52:32 +00001989 def test_argument_passing(self):
1990 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001991 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001992 class introspective(tzinfo):
1993 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001994 def utcoffset(self, dt):
1995 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001996 dst = utcoffset
1997
1998 obj = cls(1, 2, 3, tzinfo=introspective())
1999
Tim Peters0bf60bd2003-01-08 20:40:01 +00002000 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002001 self.assertEqual(obj.tzname(), expected)
2002
Tim Peters0bf60bd2003-01-08 20:40:01 +00002003 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002004 self.assertEqual(obj.utcoffset(), expected)
2005 self.assertEqual(obj.dst(), expected)
2006
Tim Peters855fe882002-12-22 03:43:39 +00002007 def test_bad_tzinfo_classes(self):
2008 cls = self.theclass
2009 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002010
Tim Peters855fe882002-12-22 03:43:39 +00002011 class NiceTry(object):
2012 def __init__(self): pass
2013 def utcoffset(self, dt): pass
2014 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2015
2016 class BetterTry(tzinfo):
2017 def __init__(self): pass
2018 def utcoffset(self, dt): pass
2019 b = BetterTry()
2020 t = cls(1, 1, 1, tzinfo=b)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002021 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002022
2023 def test_utc_offset_out_of_bounds(self):
2024 class Edgy(tzinfo):
2025 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002026 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002027 def utcoffset(self, dt):
2028 return self.offset
2029
2030 cls = self.theclass
2031 for offset, legit in ((-1440, False),
2032 (-1439, True),
2033 (1439, True),
2034 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002035 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002036 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002037 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002038 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002039 else:
2040 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002041 if legit:
2042 aofs = abs(offset)
2043 h, m = divmod(aofs, 60)
2044 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002045 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002046 t = t.timetz()
2047 self.assertEqual(str(t), "01:02:03" + tag)
2048 else:
2049 self.assertRaises(ValueError, str, t)
2050
2051 def test_tzinfo_classes(self):
2052 cls = self.theclass
2053 class C1(tzinfo):
2054 def utcoffset(self, dt): return None
2055 def dst(self, dt): return None
2056 def tzname(self, dt): return None
2057 for t in (cls(1, 1, 1),
2058 cls(1, 1, 1, tzinfo=None),
2059 cls(1, 1, 1, tzinfo=C1())):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002060 self.assertTrue(t.utcoffset() is None)
2061 self.assertTrue(t.dst() is None)
2062 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002063
Tim Peters855fe882002-12-22 03:43:39 +00002064 class C3(tzinfo):
2065 def utcoffset(self, dt): return timedelta(minutes=-1439)
2066 def dst(self, dt): return timedelta(minutes=1439)
2067 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002068 t = cls(1, 1, 1, tzinfo=C3())
2069 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2070 self.assertEqual(t.dst(), timedelta(minutes=1439))
2071 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002072
2073 # Wrong types.
2074 class C4(tzinfo):
2075 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002076 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002077 def tzname(self, dt): return 0
2078 t = cls(1, 1, 1, tzinfo=C4())
2079 self.assertRaises(TypeError, t.utcoffset)
2080 self.assertRaises(TypeError, t.dst)
2081 self.assertRaises(TypeError, t.tzname)
2082
2083 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002084 class C6(tzinfo):
2085 def utcoffset(self, dt): return timedelta(hours=-24)
2086 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002087 t = cls(1, 1, 1, tzinfo=C6())
2088 self.assertRaises(ValueError, t.utcoffset)
2089 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002090
2091 # Not a whole number of minutes.
2092 class C7(tzinfo):
2093 def utcoffset(self, dt): return timedelta(seconds=61)
2094 def dst(self, dt): return timedelta(microseconds=-81)
2095 t = cls(1, 1, 1, tzinfo=C7())
2096 self.assertRaises(ValueError, t.utcoffset)
2097 self.assertRaises(ValueError, t.dst)
2098
Tim Peters4c0db782002-12-26 05:01:19 +00002099 def test_aware_compare(self):
2100 cls = self.theclass
2101
Tim Peters60c76e42002-12-27 00:41:11 +00002102 # Ensure that utcoffset() gets ignored if the comparands have
2103 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002104 class OperandDependentOffset(tzinfo):
2105 def utcoffset(self, t):
2106 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002107 # d0 and d1 equal after adjustment
2108 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002109 else:
Tim Peters397301e2003-01-02 21:28:08 +00002110 # d2 off in the weeds
2111 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002112
2113 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2114 d0 = base.replace(minute=3)
2115 d1 = base.replace(minute=9)
2116 d2 = base.replace(minute=11)
2117 for x in d0, d1, d2:
2118 for y in d0, d1, d2:
2119 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002120 expected = cmp(x.minute, y.minute)
2121 self.assertEqual(got, expected)
2122
2123 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002124 # Note that a time can't actually have an operand-depedent offset,
2125 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2126 # so skip this test for time.
2127 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002128 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2129 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2130 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2131 for x in d0, d1, d2:
2132 for y in d0, d1, d2:
2133 got = cmp(x, y)
2134 if (x is d0 or x is d1) and (y is d0 or y is d1):
2135 expected = 0
2136 elif x is y is d2:
2137 expected = 0
2138 elif x is d2:
2139 expected = -1
2140 else:
2141 assert y is d2
2142 expected = 1
2143 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002144
Tim Peters855fe882002-12-22 03:43:39 +00002145
Tim Peters0bf60bd2003-01-08 20:40:01 +00002146# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002147class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002148 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002149
2150 def test_empty(self):
2151 t = self.theclass()
2152 self.assertEqual(t.hour, 0)
2153 self.assertEqual(t.minute, 0)
2154 self.assertEqual(t.second, 0)
2155 self.assertEqual(t.microsecond, 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002156 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002157
Tim Peters2a799bf2002-12-16 20:18:38 +00002158 def test_zones(self):
2159 est = FixedOffset(-300, "EST", 1)
2160 utc = FixedOffset(0, "UTC", -2)
2161 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002162 t1 = time( 7, 47, tzinfo=est)
2163 t2 = time(12, 47, tzinfo=utc)
2164 t3 = time(13, 47, tzinfo=met)
2165 t4 = time(microsecond=40)
2166 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002167
2168 self.assertEqual(t1.tzinfo, est)
2169 self.assertEqual(t2.tzinfo, utc)
2170 self.assertEqual(t3.tzinfo, met)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002171 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002172 self.assertEqual(t5.tzinfo, utc)
2173
Tim Peters855fe882002-12-22 03:43:39 +00002174 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2175 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2176 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002177 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002178 self.assertRaises(TypeError, t1.utcoffset, "no args")
2179
2180 self.assertEqual(t1.tzname(), "EST")
2181 self.assertEqual(t2.tzname(), "UTC")
2182 self.assertEqual(t3.tzname(), "MET")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002183 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002184 self.assertRaises(TypeError, t1.tzname, "no args")
2185
Tim Peters855fe882002-12-22 03:43:39 +00002186 self.assertEqual(t1.dst(), timedelta(minutes=1))
2187 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2188 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002189 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002190 self.assertRaises(TypeError, t1.dst, "no args")
2191
2192 self.assertEqual(hash(t1), hash(t2))
2193 self.assertEqual(hash(t1), hash(t3))
2194 self.assertEqual(hash(t2), hash(t3))
2195
2196 self.assertEqual(t1, t2)
2197 self.assertEqual(t1, t3)
2198 self.assertEqual(t2, t3)
2199 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2200 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2201 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2202
2203 self.assertEqual(str(t1), "07:47:00-05:00")
2204 self.assertEqual(str(t2), "12:47:00+00:00")
2205 self.assertEqual(str(t3), "13:47:00+01:00")
2206 self.assertEqual(str(t4), "00:00:00.000040")
2207 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2208
2209 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2210 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2211 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2212 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2213 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2214
Tim Peters0bf60bd2003-01-08 20:40:01 +00002215 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002216 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2217 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2218 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2219 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2220 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2221
2222 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2223 "07:47:00 %Z=EST %z=-0500")
2224 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2225 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2226
2227 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002228 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002229 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2230 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2231
Tim Petersb92bb712002-12-21 17:44:07 +00002232 # Check that an invalid tzname result raises an exception.
2233 class Badtzname(tzinfo):
2234 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002235 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002236 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2237 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002238
2239 def test_hash_edge_cases(self):
2240 # Offsets that overflow a basic time.
2241 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2242 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2243 self.assertEqual(hash(t1), hash(t2))
2244
2245 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2246 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2247 self.assertEqual(hash(t1), hash(t2))
2248
Tim Peters2a799bf2002-12-16 20:18:38 +00002249 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002250 # Try one without a tzinfo.
2251 args = 20, 59, 16, 64**2
2252 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002253 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002254 green = pickler.dumps(orig, proto)
2255 derived = unpickler.loads(green)
2256 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002257
2258 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002259 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002260 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002261 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002262 green = pickler.dumps(orig, proto)
2263 derived = unpickler.loads(green)
2264 self.assertEqual(orig, derived)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002265 self.assertTrue(isinstance(derived.tzinfo, PicklableFixedOffset))
Tim Peters96940c92003-01-31 21:55:33 +00002266 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2267 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002268
2269 def test_more_bool(self):
2270 # Test cases with non-None tzinfo.
2271 cls = self.theclass
2272
2273 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002274 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002275
2276 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002277 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002278
2279 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002280 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002281
2282 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002283 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002284
2285 # Mostly ensuring this doesn't overflow internally.
2286 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002287 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002288
2289 # But this should yield a value error -- the utcoffset is bogus.
2290 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2291 self.assertRaises(ValueError, lambda: bool(t))
2292
2293 # Likewise.
2294 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2295 self.assertRaises(ValueError, lambda: bool(t))
2296
Tim Peters12bf3392002-12-24 05:41:27 +00002297 def test_replace(self):
2298 cls = self.theclass
2299 z100 = FixedOffset(100, "+100")
2300 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2301 args = [1, 2, 3, 4, z100]
2302 base = cls(*args)
2303 self.assertEqual(base, base.replace())
2304
2305 i = 0
2306 for name, newval in (("hour", 5),
2307 ("minute", 6),
2308 ("second", 7),
2309 ("microsecond", 8),
2310 ("tzinfo", zm200)):
2311 newargs = args[:]
2312 newargs[i] = newval
2313 expected = cls(*newargs)
2314 got = base.replace(**{name: newval})
2315 self.assertEqual(expected, got)
2316 i += 1
2317
2318 # Ensure we can get rid of a tzinfo.
2319 self.assertEqual(base.tzname(), "+100")
2320 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002321 self.assertTrue(base2.tzinfo is None)
2322 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002323
2324 # Ensure we can add one.
2325 base3 = base2.replace(tzinfo=z100)
2326 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002327 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002328
2329 # Out of bounds.
2330 base = cls(1)
2331 self.assertRaises(ValueError, base.replace, hour=24)
2332 self.assertRaises(ValueError, base.replace, minute=-1)
2333 self.assertRaises(ValueError, base.replace, second=100)
2334 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2335
Tim Peters60c76e42002-12-27 00:41:11 +00002336 def test_mixed_compare(self):
2337 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002338 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002339 self.assertEqual(t1, t2)
2340 t2 = t2.replace(tzinfo=None)
2341 self.assertEqual(t1, t2)
2342 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2343 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002344 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2345 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002346
Tim Peters0bf60bd2003-01-08 20:40:01 +00002347 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002348 class Varies(tzinfo):
2349 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002350 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002351 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002352 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002353 return self.offset
2354
2355 v = Varies()
2356 t1 = t2.replace(tzinfo=v)
2357 t2 = t2.replace(tzinfo=v)
2358 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2359 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2360 self.assertEqual(t1, t2)
2361
2362 # But if they're not identical, it isn't ignored.
2363 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002364 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002365
Tim Petersa98924a2003-05-17 05:55:19 +00002366 def test_subclass_timetz(self):
2367
2368 class C(self.theclass):
2369 theAnswer = 42
2370
2371 def __new__(cls, *args, **kws):
2372 temp = kws.copy()
2373 extra = temp.pop('extra')
2374 result = self.theclass.__new__(cls, *args, **temp)
2375 result.extra = extra
2376 return result
2377
2378 def newmeth(self, start):
2379 return start + self.hour + self.second
2380
2381 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2382
2383 dt1 = self.theclass(*args)
2384 dt2 = C(*args, **{'extra': 7})
2385
2386 self.assertEqual(dt2.__class__, C)
2387 self.assertEqual(dt2.theAnswer, 42)
2388 self.assertEqual(dt2.extra, 7)
2389 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2390 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2391
Tim Peters4c0db782002-12-26 05:01:19 +00002392
Tim Peters0bf60bd2003-01-08 20:40:01 +00002393# Testing datetime objects with a non-None tzinfo.
2394
Collin Winterc2898c52007-04-25 17:29:52 +00002395class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002396 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002397
2398 def test_trivial(self):
2399 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2400 self.assertEqual(dt.year, 1)
2401 self.assertEqual(dt.month, 2)
2402 self.assertEqual(dt.day, 3)
2403 self.assertEqual(dt.hour, 4)
2404 self.assertEqual(dt.minute, 5)
2405 self.assertEqual(dt.second, 6)
2406 self.assertEqual(dt.microsecond, 7)
2407 self.assertEqual(dt.tzinfo, None)
2408
2409 def test_even_more_compare(self):
2410 # The test_compare() and test_more_compare() inherited from TestDate
2411 # and TestDateTime covered non-tzinfo cases.
2412
2413 # Smallest possible after UTC adjustment.
2414 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2415 # Largest possible after UTC adjustment.
2416 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2417 tzinfo=FixedOffset(-1439, ""))
2418
2419 # Make sure those compare correctly, and w/o overflow.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002420 self.assertTrue(t1 < t2)
2421 self.assertTrue(t1 != t2)
2422 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002423
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002424 self.assertTrue(t1 == t1)
2425 self.assertTrue(t2 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002426
2427 # Equal afer adjustment.
2428 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2429 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2430 self.assertEqual(t1, t2)
2431
2432 # Change t1 not to subtract a minute, and t1 should be larger.
2433 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002434 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002435
2436 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2437 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002438 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002439
2440 # Back to the original t1, but make seconds resolve it.
2441 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2442 second=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002443 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002444
2445 # Likewise, but make microseconds resolve it.
2446 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2447 microsecond=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002448 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002449
2450 # Make t2 naive and it should fail.
2451 t2 = self.theclass.min
2452 self.assertRaises(TypeError, lambda: t1 == t2)
2453 self.assertEqual(t2, t2)
2454
2455 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2456 class Naive(tzinfo):
2457 def utcoffset(self, dt): return None
2458 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2459 self.assertRaises(TypeError, lambda: t1 == t2)
2460 self.assertEqual(t2, t2)
2461
2462 # OTOH, it's OK to compare two of these mixing the two ways of being
2463 # naive.
2464 t1 = self.theclass(5, 6, 7)
2465 self.assertEqual(t1, t2)
2466
2467 # Try a bogus uctoffset.
2468 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002469 def utcoffset(self, dt):
2470 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002471 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2472 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002473 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002474
Tim Peters2a799bf2002-12-16 20:18:38 +00002475 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002476 # Try one without a tzinfo.
2477 args = 6, 7, 23, 20, 59, 1, 64**2
2478 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002479 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002480 green = pickler.dumps(orig, proto)
2481 derived = unpickler.loads(green)
2482 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002483
2484 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002485 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002486 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002487 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002488 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002489 green = pickler.dumps(orig, proto)
2490 derived = unpickler.loads(green)
2491 self.assertEqual(orig, derived)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002492 self.assertTrue(isinstance(derived.tzinfo,
Tim Peters96940c92003-01-31 21:55:33 +00002493 PicklableFixedOffset))
2494 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2495 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002496
2497 def test_extreme_hashes(self):
2498 # If an attempt is made to hash these via subtracting the offset
2499 # then hashing a datetime object, OverflowError results. The
2500 # Python implementation used to blow up here.
2501 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2502 hash(t)
2503 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2504 tzinfo=FixedOffset(-1439, ""))
2505 hash(t)
2506
2507 # OTOH, an OOB offset should blow up.
2508 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2509 self.assertRaises(ValueError, hash, t)
2510
2511 def test_zones(self):
2512 est = FixedOffset(-300, "EST")
2513 utc = FixedOffset(0, "UTC")
2514 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002515 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2516 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2517 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002518 self.assertEqual(t1.tzinfo, est)
2519 self.assertEqual(t2.tzinfo, utc)
2520 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002521 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2522 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2523 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002524 self.assertEqual(t1.tzname(), "EST")
2525 self.assertEqual(t2.tzname(), "UTC")
2526 self.assertEqual(t3.tzname(), "MET")
2527 self.assertEqual(hash(t1), hash(t2))
2528 self.assertEqual(hash(t1), hash(t3))
2529 self.assertEqual(hash(t2), hash(t3))
2530 self.assertEqual(t1, t2)
2531 self.assertEqual(t1, t3)
2532 self.assertEqual(t2, t3)
2533 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2534 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2535 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002536 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002537 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2538 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2539 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2540
2541 def test_combine(self):
2542 met = FixedOffset(60, "MET")
2543 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002544 tz = time(18, 45, 3, 1234, tzinfo=met)
2545 dt = datetime.combine(d, tz)
2546 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002547 tzinfo=met))
2548
2549 def test_extract(self):
2550 met = FixedOffset(60, "MET")
2551 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2552 self.assertEqual(dt.date(), date(2002, 3, 4))
2553 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002554 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002555
2556 def test_tz_aware_arithmetic(self):
2557 import random
2558
2559 now = self.theclass.now()
2560 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002561 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002562 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002563 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002564 self.assertEqual(nowaware.timetz(), timeaware)
2565
2566 # Can't mix aware and non-aware.
2567 self.assertRaises(TypeError, lambda: now - nowaware)
2568 self.assertRaises(TypeError, lambda: nowaware - now)
2569
Tim Peters0bf60bd2003-01-08 20:40:01 +00002570 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002571 self.assertRaises(TypeError, lambda: now + nowaware)
2572 self.assertRaises(TypeError, lambda: nowaware + now)
2573 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2574
2575 # Subtracting should yield 0.
2576 self.assertEqual(now - now, timedelta(0))
2577 self.assertEqual(nowaware - nowaware, timedelta(0))
2578
2579 # Adding a delta should preserve tzinfo.
2580 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2581 nowawareplus = nowaware + delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002582 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002583 nowawareplus2 = delta + nowaware
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002584 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002585 self.assertEqual(nowawareplus, nowawareplus2)
2586
2587 # that - delta should be what we started with, and that - what we
2588 # started with should be delta.
2589 diff = nowawareplus - delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002590 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002591 self.assertEqual(nowaware, diff)
2592 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2593 self.assertEqual(nowawareplus - nowaware, delta)
2594
2595 # Make up a random timezone.
2596 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002597 # Attach it to nowawareplus.
2598 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002599 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002600 # Make sure the difference takes the timezone adjustments into account.
2601 got = nowaware - nowawareplus
2602 # Expected: (nowaware base - nowaware offset) -
2603 # (nowawareplus base - nowawareplus offset) =
2604 # (nowaware base - nowawareplus base) +
2605 # (nowawareplus offset - nowaware offset) =
2606 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002607 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002608 self.assertEqual(got, expected)
2609
2610 # Try max possible difference.
2611 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2612 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2613 tzinfo=FixedOffset(-1439, "max"))
2614 maxdiff = max - min
2615 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2616 timedelta(minutes=2*1439))
2617
2618 def test_tzinfo_now(self):
2619 meth = self.theclass.now
2620 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2621 base = meth()
2622 # Try with and without naming the keyword.
2623 off42 = FixedOffset(42, "42")
2624 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002625 again = meth(tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002626 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002627 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002628 # Bad argument with and w/o naming the keyword.
2629 self.assertRaises(TypeError, meth, 16)
2630 self.assertRaises(TypeError, meth, tzinfo=16)
2631 # Bad keyword name.
2632 self.assertRaises(TypeError, meth, tinfo=off42)
2633 # Too many args.
2634 self.assertRaises(TypeError, meth, off42, off42)
2635
Tim Peters10cadce2003-01-23 19:58:02 +00002636 # We don't know which time zone we're in, and don't have a tzinfo
2637 # class to represent it, so seeing whether a tz argument actually
2638 # does a conversion is tricky.
2639 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2640 utc = FixedOffset(0, "utc", 0)
2641 for dummy in range(3):
2642 now = datetime.now(weirdtz)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002643 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002644 utcnow = datetime.utcnow().replace(tzinfo=utc)
2645 now2 = utcnow.astimezone(weirdtz)
2646 if abs(now - now2) < timedelta(seconds=30):
2647 break
2648 # Else the code is broken, or more than 30 seconds passed between
2649 # calls; assuming the latter, just try again.
2650 else:
2651 # Three strikes and we're out.
2652 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2653
Tim Peters2a799bf2002-12-16 20:18:38 +00002654 def test_tzinfo_fromtimestamp(self):
2655 import time
2656 meth = self.theclass.fromtimestamp
2657 ts = time.time()
2658 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2659 base = meth(ts)
2660 # Try with and without naming the keyword.
2661 off42 = FixedOffset(42, "42")
2662 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002663 again = meth(ts, tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002664 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002665 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002666 # Bad argument with and w/o naming the keyword.
2667 self.assertRaises(TypeError, meth, ts, 16)
2668 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2669 # Bad keyword name.
2670 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2671 # Too many args.
2672 self.assertRaises(TypeError, meth, ts, off42, off42)
2673 # Too few args.
2674 self.assertRaises(TypeError, meth)
2675
Tim Peters2a44a8d2003-01-23 20:53:10 +00002676 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002677 timestamp = 1000000000
2678 utcdatetime = datetime.utcfromtimestamp(timestamp)
2679 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2680 # But on some flavor of Mac, it's nowhere near that. So we can't have
2681 # any idea here what time that actually is, we can only test that
2682 # relative changes match.
2683 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2684 tz = FixedOffset(utcoffset, "tz", 0)
2685 expected = utcdatetime + utcoffset
2686 got = datetime.fromtimestamp(timestamp, tz)
2687 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002688
Tim Peters2a799bf2002-12-16 20:18:38 +00002689 def test_tzinfo_utcnow(self):
2690 meth = self.theclass.utcnow
2691 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2692 base = meth()
2693 # Try with and without naming the keyword; for whatever reason,
2694 # utcnow() doesn't accept a tzinfo argument.
2695 off42 = FixedOffset(42, "42")
2696 self.assertRaises(TypeError, meth, off42)
2697 self.assertRaises(TypeError, meth, tzinfo=off42)
2698
2699 def test_tzinfo_utcfromtimestamp(self):
2700 import time
2701 meth = self.theclass.utcfromtimestamp
2702 ts = time.time()
2703 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2704 base = meth(ts)
2705 # Try with and without naming the keyword; for whatever reason,
2706 # utcfromtimestamp() doesn't accept a tzinfo argument.
2707 off42 = FixedOffset(42, "42")
2708 self.assertRaises(TypeError, meth, ts, off42)
2709 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2710
2711 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002712 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002713 # DST flag.
2714 class DST(tzinfo):
2715 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002716 if isinstance(dstvalue, int):
2717 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002718 self.dstvalue = dstvalue
2719 def dst(self, dt):
2720 return self.dstvalue
2721
2722 cls = self.theclass
2723 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2724 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2725 t = d.timetuple()
2726 self.assertEqual(1, t.tm_year)
2727 self.assertEqual(1, t.tm_mon)
2728 self.assertEqual(1, t.tm_mday)
2729 self.assertEqual(10, t.tm_hour)
2730 self.assertEqual(20, t.tm_min)
2731 self.assertEqual(30, t.tm_sec)
2732 self.assertEqual(0, t.tm_wday)
2733 self.assertEqual(1, t.tm_yday)
2734 self.assertEqual(flag, t.tm_isdst)
2735
2736 # dst() returns wrong type.
2737 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2738
2739 # dst() at the edge.
2740 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2741 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2742
2743 # dst() out of range.
2744 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2745 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2746
2747 def test_utctimetuple(self):
2748 class DST(tzinfo):
2749 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002750 if isinstance(dstvalue, int):
2751 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002752 self.dstvalue = dstvalue
2753 def dst(self, dt):
2754 return self.dstvalue
2755
2756 cls = self.theclass
2757 # This can't work: DST didn't implement utcoffset.
2758 self.assertRaises(NotImplementedError,
2759 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2760
2761 class UOFS(DST):
2762 def __init__(self, uofs, dofs=None):
2763 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002764 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002765 def utcoffset(self, dt):
2766 return self.uofs
2767
2768 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2769 # in effect for a UTC time.
2770 for dstvalue in -33, 33, 0, None:
2771 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2772 t = d.utctimetuple()
2773 self.assertEqual(d.year, t.tm_year)
2774 self.assertEqual(d.month, t.tm_mon)
2775 self.assertEqual(d.day, t.tm_mday)
2776 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2777 self.assertEqual(13, t.tm_min)
2778 self.assertEqual(d.second, t.tm_sec)
2779 self.assertEqual(d.weekday(), t.tm_wday)
2780 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2781 t.tm_yday)
2782 self.assertEqual(0, t.tm_isdst)
2783
2784 # At the edges, UTC adjustment can normalize into years out-of-range
2785 # for a datetime object. Ensure that a correct timetuple is
2786 # created anyway.
2787 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2788 # That goes back 1 minute less than a full day.
2789 t = tiny.utctimetuple()
2790 self.assertEqual(t.tm_year, MINYEAR-1)
2791 self.assertEqual(t.tm_mon, 12)
2792 self.assertEqual(t.tm_mday, 31)
2793 self.assertEqual(t.tm_hour, 0)
2794 self.assertEqual(t.tm_min, 1)
2795 self.assertEqual(t.tm_sec, 37)
2796 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2797 self.assertEqual(t.tm_isdst, 0)
2798
2799 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2800 # That goes forward 1 minute less than a full day.
2801 t = huge.utctimetuple()
2802 self.assertEqual(t.tm_year, MAXYEAR+1)
2803 self.assertEqual(t.tm_mon, 1)
2804 self.assertEqual(t.tm_mday, 1)
2805 self.assertEqual(t.tm_hour, 23)
2806 self.assertEqual(t.tm_min, 58)
2807 self.assertEqual(t.tm_sec, 37)
2808 self.assertEqual(t.tm_yday, 1)
2809 self.assertEqual(t.tm_isdst, 0)
2810
2811 def test_tzinfo_isoformat(self):
2812 zero = FixedOffset(0, "+00:00")
2813 plus = FixedOffset(220, "+03:40")
2814 minus = FixedOffset(-231, "-03:51")
2815 unknown = FixedOffset(None, "")
2816
2817 cls = self.theclass
2818 datestr = '0001-02-03'
2819 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002820 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002821 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2822 timestr = '04:05:59' + (us and '.987001' or '')
2823 ofsstr = ofs is not None and d.tzname() or ''
2824 tailstr = timestr + ofsstr
2825 iso = d.isoformat()
2826 self.assertEqual(iso, datestr + 'T' + tailstr)
2827 self.assertEqual(iso, d.isoformat('T'))
2828 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2829 self.assertEqual(str(d), datestr + ' ' + tailstr)
2830
Tim Peters12bf3392002-12-24 05:41:27 +00002831 def test_replace(self):
2832 cls = self.theclass
2833 z100 = FixedOffset(100, "+100")
2834 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2835 args = [1, 2, 3, 4, 5, 6, 7, z100]
2836 base = cls(*args)
2837 self.assertEqual(base, base.replace())
2838
2839 i = 0
2840 for name, newval in (("year", 2),
2841 ("month", 3),
2842 ("day", 4),
2843 ("hour", 5),
2844 ("minute", 6),
2845 ("second", 7),
2846 ("microsecond", 8),
2847 ("tzinfo", zm200)):
2848 newargs = args[:]
2849 newargs[i] = newval
2850 expected = cls(*newargs)
2851 got = base.replace(**{name: newval})
2852 self.assertEqual(expected, got)
2853 i += 1
2854
2855 # Ensure we can get rid of a tzinfo.
2856 self.assertEqual(base.tzname(), "+100")
2857 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002858 self.assertTrue(base2.tzinfo is None)
2859 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002860
2861 # Ensure we can add one.
2862 base3 = base2.replace(tzinfo=z100)
2863 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002864 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002865
2866 # Out of bounds.
2867 base = cls(2000, 2, 29)
2868 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002869
Tim Peters80475bb2002-12-25 07:40:55 +00002870 def test_more_astimezone(self):
2871 # The inherited test_astimezone covered some trivial and error cases.
2872 fnone = FixedOffset(None, "None")
2873 f44m = FixedOffset(44, "44")
2874 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2875
Tim Peters10cadce2003-01-23 19:58:02 +00002876 dt = self.theclass.now(tz=f44m)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002877 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002878 # Replacing with degenerate tzinfo raises an exception.
2879 self.assertRaises(ValueError, dt.astimezone, fnone)
2880 # Ditto with None tz.
2881 self.assertRaises(TypeError, dt.astimezone, None)
2882 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002883 x = dt.astimezone(dt.tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002884 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002885 self.assertEqual(x.date(), dt.date())
2886 self.assertEqual(x.time(), dt.time())
2887
2888 # Replacing with different tzinfo does adjust.
2889 got = dt.astimezone(fm5h)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002890 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002891 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2892 expected = dt - dt.utcoffset() # in effect, convert to UTC
2893 expected += fm5h.utcoffset(dt) # and from there to local time
2894 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2895 self.assertEqual(got.date(), expected.date())
2896 self.assertEqual(got.time(), expected.time())
2897 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002898 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002899 self.assertEqual(got, expected)
2900
Tim Peters4c0db782002-12-26 05:01:19 +00002901 def test_aware_subtract(self):
2902 cls = self.theclass
2903
Tim Peters60c76e42002-12-27 00:41:11 +00002904 # Ensure that utcoffset() is ignored when the operands have the
2905 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002906 class OperandDependentOffset(tzinfo):
2907 def utcoffset(self, t):
2908 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002909 # d0 and d1 equal after adjustment
2910 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002911 else:
Tim Peters397301e2003-01-02 21:28:08 +00002912 # d2 off in the weeds
2913 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002914
2915 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2916 d0 = base.replace(minute=3)
2917 d1 = base.replace(minute=9)
2918 d2 = base.replace(minute=11)
2919 for x in d0, d1, d2:
2920 for y in d0, d1, d2:
2921 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002922 expected = timedelta(minutes=x.minute - y.minute)
2923 self.assertEqual(got, expected)
2924
2925 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2926 # ignored.
2927 base = cls(8, 9, 10, 11, 12, 13, 14)
2928 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2929 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2930 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2931 for x in d0, d1, d2:
2932 for y in d0, d1, d2:
2933 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002934 if (x is d0 or x is d1) and (y is d0 or y is d1):
2935 expected = timedelta(0)
2936 elif x is y is d2:
2937 expected = timedelta(0)
2938 elif x is d2:
2939 expected = timedelta(minutes=(11-59)-0)
2940 else:
2941 assert y is d2
2942 expected = timedelta(minutes=0-(11-59))
2943 self.assertEqual(got, expected)
2944
Tim Peters60c76e42002-12-27 00:41:11 +00002945 def test_mixed_compare(self):
2946 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002947 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002948 self.assertEqual(t1, t2)
2949 t2 = t2.replace(tzinfo=None)
2950 self.assertEqual(t1, t2)
2951 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2952 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002953 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2954 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002955
Tim Peters0bf60bd2003-01-08 20:40:01 +00002956 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002957 class Varies(tzinfo):
2958 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002959 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002960 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002961 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002962 return self.offset
2963
2964 v = Varies()
2965 t1 = t2.replace(tzinfo=v)
2966 t2 = t2.replace(tzinfo=v)
2967 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2968 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2969 self.assertEqual(t1, t2)
2970
2971 # But if they're not identical, it isn't ignored.
2972 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002973 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002974
Tim Petersa98924a2003-05-17 05:55:19 +00002975 def test_subclass_datetimetz(self):
2976
2977 class C(self.theclass):
2978 theAnswer = 42
2979
2980 def __new__(cls, *args, **kws):
2981 temp = kws.copy()
2982 extra = temp.pop('extra')
2983 result = self.theclass.__new__(cls, *args, **temp)
2984 result.extra = extra
2985 return result
2986
2987 def newmeth(self, start):
2988 return start + self.hour + self.year
2989
2990 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2991
2992 dt1 = self.theclass(*args)
2993 dt2 = C(*args, **{'extra': 7})
2994
2995 self.assertEqual(dt2.__class__, C)
2996 self.assertEqual(dt2.theAnswer, 42)
2997 self.assertEqual(dt2.extra, 7)
2998 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2999 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3000
Tim Peters621818b2002-12-29 23:44:49 +00003001# Pain to set up DST-aware tzinfo classes.
3002
3003def first_sunday_on_or_after(dt):
3004 days_to_go = 6 - dt.weekday()
3005 if days_to_go:
3006 dt += timedelta(days_to_go)
3007 return dt
3008
3009ZERO = timedelta(0)
3010HOUR = timedelta(hours=1)
3011DAY = timedelta(days=1)
3012# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3013DSTSTART = datetime(1, 4, 1, 2)
3014# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003015# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3016# being standard time on that day, there is no spelling in local time of
3017# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3018DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003019
3020class USTimeZone(tzinfo):
3021
3022 def __init__(self, hours, reprname, stdname, dstname):
3023 self.stdoffset = timedelta(hours=hours)
3024 self.reprname = reprname
3025 self.stdname = stdname
3026 self.dstname = dstname
3027
3028 def __repr__(self):
3029 return self.reprname
3030
3031 def tzname(self, dt):
3032 if self.dst(dt):
3033 return self.dstname
3034 else:
3035 return self.stdname
3036
3037 def utcoffset(self, dt):
3038 return self.stdoffset + self.dst(dt)
3039
3040 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003041 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003042 # An exception instead may be sensible here, in one or more of
3043 # the cases.
3044 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003045 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003046
3047 # Find first Sunday in April.
3048 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3049 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3050
3051 # Find last Sunday in October.
3052 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3053 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3054
Tim Peters621818b2002-12-29 23:44:49 +00003055 # Can't compare naive to aware objects, so strip the timezone from
3056 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003057 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003058 return HOUR
3059 else:
3060 return ZERO
3061
Tim Peters521fc152002-12-31 17:36:56 +00003062Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3063Central = USTimeZone(-6, "Central", "CST", "CDT")
3064Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3065Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003066utc_real = FixedOffset(0, "UTC", 0)
3067# For better test coverage, we want another flavor of UTC that's west of
3068# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003069utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003070
3071class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003072 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003073 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003074 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003075
Tim Peters0bf60bd2003-01-08 20:40:01 +00003076 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003077
Tim Peters521fc152002-12-31 17:36:56 +00003078 # Check a time that's inside DST.
3079 def checkinside(self, dt, tz, utc, dston, dstoff):
3080 self.assertEqual(dt.dst(), HOUR)
3081
3082 # Conversion to our own timezone is always an identity.
3083 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003084
3085 asutc = dt.astimezone(utc)
3086 there_and_back = asutc.astimezone(tz)
3087
3088 # Conversion to UTC and back isn't always an identity here,
3089 # because there are redundant spellings (in local time) of
3090 # UTC time when DST begins: the clock jumps from 1:59:59
3091 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3092 # make sense then. The classes above treat 2:MM:SS as
3093 # daylight time then (it's "after 2am"), really an alias
3094 # for 1:MM:SS standard time. The latter form is what
3095 # conversion back from UTC produces.
3096 if dt.date() == dston.date() and dt.hour == 2:
3097 # We're in the redundant hour, and coming back from
3098 # UTC gives the 1:MM:SS standard-time spelling.
3099 self.assertEqual(there_and_back + HOUR, dt)
3100 # Although during was considered to be in daylight
3101 # time, there_and_back is not.
3102 self.assertEqual(there_and_back.dst(), ZERO)
3103 # They're the same times in UTC.
3104 self.assertEqual(there_and_back.astimezone(utc),
3105 dt.astimezone(utc))
3106 else:
3107 # We're not in the redundant hour.
3108 self.assertEqual(dt, there_and_back)
3109
Tim Peters327098a2003-01-20 22:54:38 +00003110 # Because we have a redundant spelling when DST begins, there is
3111 # (unforunately) an hour when DST ends that can't be spelled at all in
3112 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3113 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3114 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3115 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3116 # expressed in local time. Nevertheless, we want conversion back
3117 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003118 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003119 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003120 if dt.date() == dstoff.date() and dt.hour == 0:
3121 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003122 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003123 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3124 nexthour_utc += HOUR
3125 nexthour_tz = nexthour_utc.astimezone(tz)
3126 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003127 else:
Tim Peters327098a2003-01-20 22:54:38 +00003128 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003129
3130 # Check a time that's outside DST.
3131 def checkoutside(self, dt, tz, utc):
3132 self.assertEqual(dt.dst(), ZERO)
3133
3134 # Conversion to our own timezone is always an identity.
3135 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003136
3137 # Converting to UTC and back is an identity too.
3138 asutc = dt.astimezone(utc)
3139 there_and_back = asutc.astimezone(tz)
3140 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003141
Tim Peters1024bf82002-12-30 17:09:40 +00003142 def convert_between_tz_and_utc(self, tz, utc):
3143 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003144 # Because 1:MM on the day DST ends is taken as being standard time,
3145 # there is no spelling in tz for the last hour of daylight time.
3146 # For purposes of the test, the last hour of DST is 0:MM, which is
3147 # taken as being daylight time (and 1:MM is taken as being standard
3148 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003149 dstoff = self.dstoff.replace(tzinfo=tz)
3150 for delta in (timedelta(weeks=13),
3151 DAY,
3152 HOUR,
3153 timedelta(minutes=1),
3154 timedelta(microseconds=1)):
3155
Tim Peters521fc152002-12-31 17:36:56 +00003156 self.checkinside(dston, tz, utc, dston, dstoff)
3157 for during in dston + delta, dstoff - delta:
3158 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003159
Tim Peters521fc152002-12-31 17:36:56 +00003160 self.checkoutside(dstoff, tz, utc)
3161 for outside in dston - delta, dstoff + delta:
3162 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003163
Tim Peters621818b2002-12-29 23:44:49 +00003164 def test_easy(self):
3165 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003166 self.convert_between_tz_and_utc(Eastern, utc_real)
3167 self.convert_between_tz_and_utc(Pacific, utc_real)
3168 self.convert_between_tz_and_utc(Eastern, utc_fake)
3169 self.convert_between_tz_and_utc(Pacific, utc_fake)
3170 # The next is really dancing near the edge. It works because
3171 # Pacific and Eastern are far enough apart that their "problem
3172 # hours" don't overlap.
3173 self.convert_between_tz_and_utc(Eastern, Pacific)
3174 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003175 # OTOH, these fail! Don't enable them. The difficulty is that
3176 # the edge case tests assume that every hour is representable in
3177 # the "utc" class. This is always true for a fixed-offset tzinfo
3178 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3179 # For these adjacent DST-aware time zones, the range of time offsets
3180 # tested ends up creating hours in the one that aren't representable
3181 # in the other. For the same reason, we would see failures in the
3182 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3183 # offset deltas in convert_between_tz_and_utc().
3184 #
3185 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3186 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003187
Tim Petersf3615152003-01-01 21:51:37 +00003188 def test_tricky(self):
3189 # 22:00 on day before daylight starts.
3190 fourback = self.dston - timedelta(hours=4)
3191 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003192 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003193 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3194 # 2", we should get the 3 spelling.
3195 # If we plug 22:00 the day before into Eastern, it "looks like std
3196 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3197 # to 22:00 lands on 2:00, which makes no sense in local time (the
3198 # local clock jumps from 1 to 3). The point here is to make sure we
3199 # get the 3 spelling.
3200 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003201 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003202 self.assertEqual(expected, got)
3203
3204 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3205 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003206 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003207 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3208 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3209 # spelling.
3210 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003211 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003212 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003213
Tim Petersadf64202003-01-04 06:03:15 +00003214 # Now on the day DST ends, we want "repeat an hour" behavior.
3215 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3216 # EST 23:MM 0:MM 1:MM 2:MM
3217 # EDT 0:MM 1:MM 2:MM 3:MM
3218 # wall 0:MM 1:MM 1:MM 2:MM against these
3219 for utc in utc_real, utc_fake:
3220 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003221 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003222 # Convert that to UTC.
3223 first_std_hour -= tz.utcoffset(None)
3224 # Adjust for possibly fake UTC.
3225 asutc = first_std_hour + utc.utcoffset(None)
3226 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3227 # tz=Eastern.
3228 asutcbase = asutc.replace(tzinfo=utc)
3229 for tzhour in (0, 1, 1, 2):
3230 expectedbase = self.dstoff.replace(hour=tzhour)
3231 for minute in 0, 30, 59:
3232 expected = expectedbase.replace(minute=minute)
3233 asutc = asutcbase.replace(minute=minute)
3234 astz = asutc.astimezone(tz)
3235 self.assertEqual(astz.replace(tzinfo=None), expected)
3236 asutcbase += HOUR
3237
3238
Tim Peters710fb152003-01-02 19:35:54 +00003239 def test_bogus_dst(self):
3240 class ok(tzinfo):
3241 def utcoffset(self, dt): return HOUR
3242 def dst(self, dt): return HOUR
3243
3244 now = self.theclass.now().replace(tzinfo=utc_real)
3245 # Doesn't blow up.
3246 now.astimezone(ok())
3247
3248 # Does blow up.
3249 class notok(ok):
3250 def dst(self, dt): return None
3251 self.assertRaises(ValueError, now.astimezone, notok())
3252
Tim Peters52dcce22003-01-23 16:36:11 +00003253 def test_fromutc(self):
3254 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3255 now = datetime.utcnow().replace(tzinfo=utc_real)
3256 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3257 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3258 enow = Eastern.fromutc(now) # doesn't blow up
3259 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3260 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3261 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3262
3263 # Always converts UTC to standard time.
3264 class FauxUSTimeZone(USTimeZone):
3265 def fromutc(self, dt):
3266 return dt + self.stdoffset
3267 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3268
3269 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3270 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3271 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3272
3273 # Check around DST start.
3274 start = self.dston.replace(hour=4, tzinfo=Eastern)
3275 fstart = start.replace(tzinfo=FEastern)
3276 for wall in 23, 0, 1, 3, 4, 5:
3277 expected = start.replace(hour=wall)
3278 if wall == 23:
3279 expected -= timedelta(days=1)
3280 got = Eastern.fromutc(start)
3281 self.assertEqual(expected, got)
3282
3283 expected = fstart + FEastern.stdoffset
3284 got = FEastern.fromutc(fstart)
3285 self.assertEqual(expected, got)
3286
3287 # Ensure astimezone() calls fromutc() too.
3288 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3289 self.assertEqual(expected, got)
3290
3291 start += HOUR
3292 fstart += HOUR
3293
3294 # Check around DST end.
3295 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3296 fstart = start.replace(tzinfo=FEastern)
3297 for wall in 0, 1, 1, 2, 3, 4:
3298 expected = start.replace(hour=wall)
3299 got = Eastern.fromutc(start)
3300 self.assertEqual(expected, got)
3301
3302 expected = fstart + FEastern.stdoffset
3303 got = FEastern.fromutc(fstart)
3304 self.assertEqual(expected, got)
3305
3306 # Ensure astimezone() calls fromutc() too.
3307 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3308 self.assertEqual(expected, got)
3309
3310 start += HOUR
3311 fstart += HOUR
3312
Tim Peters710fb152003-01-02 19:35:54 +00003313
Tim Peters528ca532004-09-16 01:30:50 +00003314#############################################################################
3315# oddballs
3316
3317class Oddballs(unittest.TestCase):
3318
3319 def test_bug_1028306(self):
3320 # Trying to compare a date to a datetime should act like a mixed-
3321 # type comparison, despite that datetime is a subclass of date.
3322 as_date = date.today()
3323 as_datetime = datetime.combine(as_date, time())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003324 self.assertTrue(as_date != as_datetime)
3325 self.assertTrue(as_datetime != as_date)
3326 self.assertTrue(not as_date == as_datetime)
3327 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003328 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3329 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3330 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3331 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3332 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3333 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3334 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3335 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3336
3337 # Neverthelss, comparison should work with the base-class (date)
3338 # projection if use of a date method is forced.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003339 self.assertTrue(as_date.__eq__(as_datetime))
Tim Peters528ca532004-09-16 01:30:50 +00003340 different_day = (as_date.day + 1) % 20 + 1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003341 self.assertTrue(not as_date.__eq__(as_datetime.replace(day=
Tim Peters528ca532004-09-16 01:30:50 +00003342 different_day)))
3343
3344 # And date should compare with other subclasses of date. If a
3345 # subclass wants to stop this, it's up to the subclass to do so.
3346 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3347 self.assertEqual(as_date, date_sc)
3348 self.assertEqual(date_sc, as_date)
3349
3350 # Ditto for datetimes.
3351 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3352 as_date.day, 0, 0, 0)
3353 self.assertEqual(as_datetime, datetime_sc)
3354 self.assertEqual(datetime_sc, as_datetime)
3355
Tim Peters2a799bf2002-12-16 20:18:38 +00003356def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003357 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003358
3359if __name__ == "__main__":
3360 test_main()