blob: a5b53fbc87c602c416470e3699dc32c0e99980b9 [file] [log] [blame]
Tim Peters0bf60bd2003-01-08 20:40:01 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
Tim Peters2a799bf2002-12-16 20:18:38 +00005
Guido van Rossumd8faa362007-04-27 19:54:29 +00006import os
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00008import unittest
9
Mark Dickinson7c186e22010-04-20 22:32:49 +000010from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
Mark Dickinsona56c4672009-01-27 18:17:45 +000011
Benjamin Petersonee8712c2008-05-20 21:35:26 +000012from test import support
Tim Peters2a799bf2002-12-16 20:18:38 +000013
14from datetime import MINYEAR, MAXYEAR
15from datetime import timedelta
16from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000017from datetime import time
18from datetime import date, datetime
19
Guido van Rossumbe6fe542007-07-19 23:55:34 +000020pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
21assert len(pickle_choices) == 3
Guido van Rossum177e41a2003-01-30 22:06:23 +000022
Tim Peters68124bb2003-02-08 03:46:31 +000023# An arbitrary collection of objects of non-datetime types, for testing
24# mixed-type comparisons.
Mark Dickinson5c2db372009-12-05 20:28:34 +000025OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000026
Tim Peters2a799bf2002-12-16 20:18:38 +000027
28#############################################################################
29# module tests
30
31class TestModule(unittest.TestCase):
32
33 def test_constants(self):
34 import datetime
35 self.assertEqual(datetime.MINYEAR, 1)
36 self.assertEqual(datetime.MAXYEAR, 9999)
37
38#############################################################################
39# tzinfo tests
40
41class FixedOffset(tzinfo):
42 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000043 if isinstance(offset, int):
44 offset = timedelta(minutes=offset)
45 if isinstance(dstoffset, int):
46 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000047 self.__offset = offset
48 self.__name = name
49 self.__dstoffset = dstoffset
50 def __repr__(self):
51 return self.__name.lower()
52 def utcoffset(self, dt):
53 return self.__offset
54 def tzname(self, dt):
55 return self.__name
56 def dst(self, dt):
57 return self.__dstoffset
58
Tim Petersfb8472c2002-12-21 05:04:42 +000059class PicklableFixedOffset(FixedOffset):
60 def __init__(self, offset=None, name=None, dstoffset=None):
61 FixedOffset.__init__(self, offset, name, dstoffset)
62
Tim Peters2a799bf2002-12-16 20:18:38 +000063class TestTZInfo(unittest.TestCase):
64
65 def test_non_abstractness(self):
66 # In order to allow subclasses to get pickled, the C implementation
67 # wasn't able to get away with having __init__ raise
68 # NotImplementedError.
69 useless = tzinfo()
70 dt = datetime.max
71 self.assertRaises(NotImplementedError, useless.tzname, dt)
72 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
73 self.assertRaises(NotImplementedError, useless.dst, dt)
74
75 def test_subclass_must_override(self):
76 class NotEnough(tzinfo):
77 def __init__(self, offset, name):
78 self.__offset = offset
79 self.__name = name
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000080 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000081 ne = NotEnough(3, "NotByALongShot")
Ezio Melottie9615932010-01-24 19:26:24 +000082 self.assertIsInstance(ne, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000083
84 dt = datetime.now()
85 self.assertRaises(NotImplementedError, ne.tzname, dt)
86 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
87 self.assertRaises(NotImplementedError, ne.dst, dt)
88
89 def test_normal(self):
90 fo = FixedOffset(3, "Three")
Ezio Melottie9615932010-01-24 19:26:24 +000091 self.assertIsInstance(fo, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000092 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +000093 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +000094 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +000095 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +000096
97 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +000098 # There's no point to pickling tzinfo objects on their own (they
99 # carry no data), but they need to be picklable anyway else
100 # concrete subclasses can't be pickled.
101 orig = tzinfo.__new__(tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000102 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000103 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000104 green = pickler.dumps(orig, proto)
105 derived = unpickler.loads(green)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000106 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000107
108 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000109 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000110 offset = timedelta(minutes=-300)
111 orig = PicklableFixedOffset(offset, 'cookie')
Ezio Melottie9615932010-01-24 19:26:24 +0000112 self.assertIsInstance(orig, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000113 self.assertTrue(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000114 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000115 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000116 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000117 green = pickler.dumps(orig, proto)
118 derived = unpickler.loads(green)
Ezio Melottie9615932010-01-24 19:26:24 +0000119 self.assertIsInstance(derived, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000120 self.assertTrue(type(derived) is PicklableFixedOffset)
Tim Petersf2715e02003-02-19 02:35:07 +0000121 self.assertEqual(derived.utcoffset(None), offset)
122 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000123
124#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000125# Base clase for testing a particular aspect of timedelta, time, date and
126# datetime comparisons.
127
Guido van Rossumd8faa362007-04-27 19:54:29 +0000128class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000129 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
130
131 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
132 # legit constructor.
133
134 def test_harmless_mixed_comparison(self):
135 me = self.theclass(1, 1, 1)
136
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000137 self.assertFalse(me == ())
138 self.assertTrue(me != ())
139 self.assertFalse(() == me)
140 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000141
Benjamin Peterson577473f2010-01-19 00:09:57 +0000142 self.assertIn(me, [1, 20, [], me])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000143 self.assertIn([], [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000144
145 def test_harmful_mixed_comparison(self):
146 me = self.theclass(1, 1, 1)
147
148 self.assertRaises(TypeError, lambda: me < ())
149 self.assertRaises(TypeError, lambda: me <= ())
150 self.assertRaises(TypeError, lambda: me > ())
151 self.assertRaises(TypeError, lambda: me >= ())
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
Tim Peters07534a62003-02-07 22:50:28 +0000158#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000159# timedelta tests
160
Guido van Rossumd8faa362007-04-27 19:54:29 +0000161class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000162
163 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000164
165 def test_constructor(self):
166 eq = self.assertEqual
167 td = timedelta
168
169 # Check keyword args to constructor
170 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
171 milliseconds=0, microseconds=0))
172 eq(td(1), td(days=1))
173 eq(td(0, 1), td(seconds=1))
174 eq(td(0, 0, 1), td(microseconds=1))
175 eq(td(weeks=1), td(days=7))
176 eq(td(days=1), td(hours=24))
177 eq(td(hours=1), td(minutes=60))
178 eq(td(minutes=1), td(seconds=60))
179 eq(td(seconds=1), td(milliseconds=1000))
180 eq(td(milliseconds=1), td(microseconds=1000))
181
182 # Check float args to constructor
183 eq(td(weeks=1.0/7), td(days=1))
184 eq(td(days=1.0/24), td(hours=1))
185 eq(td(hours=1.0/60), td(minutes=1))
186 eq(td(minutes=1.0/60), td(seconds=1))
187 eq(td(seconds=0.001), td(milliseconds=1))
188 eq(td(milliseconds=0.001), td(microseconds=1))
189
190 def test_computations(self):
191 eq = self.assertEqual
192 td = timedelta
193
194 a = td(7) # One week
195 b = td(0, 60) # One minute
196 c = td(0, 0, 1000) # One millisecond
197 eq(a+b+c, td(7, 60, 1000))
198 eq(a-b, td(6, 24*3600 - 60))
199 eq(-a, td(-7))
200 eq(+a, td(7))
201 eq(-b, td(-1, 24*3600 - 60))
202 eq(-c, td(-1, 24*3600 - 1, 999000))
203 eq(abs(a), a)
204 eq(abs(-a), a)
205 eq(td(6, 24*3600), a)
206 eq(td(0, 0, 60*1000000), b)
207 eq(a*10, td(70))
208 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000209 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000210 eq(b*10, td(0, 600))
211 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000212 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000213 eq(c*10, td(0, 0, 10000))
214 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000215 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000216 eq(a*-1, -a)
217 eq(b*-2, -b-b)
218 eq(c*-2, -c+-c)
219 eq(b*(60*24), (b*60)*24)
220 eq(b*(60*24), (60*b)*24)
221 eq(c*1000, td(0, 1))
222 eq(1000*c, td(0, 1))
223 eq(a//7, td(1))
224 eq(b//10, td(0, 6))
225 eq(c//1000, td(0, 0, 1))
226 eq(a//10, td(0, 7*24*360))
227 eq(a//3600000, td(0, 0, 7*24*1000))
228
229 def test_disallowed_computations(self):
230 a = timedelta(42)
231
Mark Dickinson5c2db372009-12-05 20:28:34 +0000232 # Add/sub ints or floats should be illegal
233 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000234 self.assertRaises(TypeError, lambda: a+i)
235 self.assertRaises(TypeError, lambda: a-i)
236 self.assertRaises(TypeError, lambda: i+a)
237 self.assertRaises(TypeError, lambda: i-a)
238
239 # Mul/div by float isn't supported.
240 x = 2.3
241 self.assertRaises(TypeError, lambda: a*x)
242 self.assertRaises(TypeError, lambda: x*a)
243 self.assertRaises(TypeError, lambda: a/x)
244 self.assertRaises(TypeError, lambda: x/a)
245 self.assertRaises(TypeError, lambda: a // x)
246 self.assertRaises(TypeError, lambda: x // a)
247
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000248 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000249 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000250 zero = 0
251 self.assertRaises(TypeError, lambda: zero // a)
252 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Tim Peters2a799bf2002-12-16 20:18:38 +0000253
254 def test_basic_attributes(self):
255 days, seconds, us = 1, 7, 31
256 td = timedelta(days, seconds, us)
257 self.assertEqual(td.days, days)
258 self.assertEqual(td.seconds, seconds)
259 self.assertEqual(td.microseconds, us)
260
Antoine Pitroube6859d2009-11-25 23:02:32 +0000261 def test_total_seconds(self):
262 td = timedelta(days=365)
263 self.assertEqual(td.total_seconds(), 31536000.0)
264 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
265 td = timedelta(seconds=total_seconds)
266 self.assertEqual(td.total_seconds(), total_seconds)
267
Tim Peters2a799bf2002-12-16 20:18:38 +0000268 def test_carries(self):
269 t1 = timedelta(days=100,
270 weeks=-7,
271 hours=-24*(100-49),
272 minutes=-3,
273 seconds=12,
274 microseconds=(3*60 - 12) * 1e6 + 1)
275 t2 = timedelta(microseconds=1)
276 self.assertEqual(t1, t2)
277
278 def test_hash_equality(self):
279 t1 = timedelta(days=100,
280 weeks=-7,
281 hours=-24*(100-49),
282 minutes=-3,
283 seconds=12,
284 microseconds=(3*60 - 12) * 1000000)
285 t2 = timedelta()
286 self.assertEqual(hash(t1), hash(t2))
287
288 t1 += timedelta(weeks=7)
289 t2 += timedelta(days=7*7)
290 self.assertEqual(t1, t2)
291 self.assertEqual(hash(t1), hash(t2))
292
293 d = {t1: 1}
294 d[t2] = 2
295 self.assertEqual(len(d), 1)
296 self.assertEqual(d[t1], 2)
297
298 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000299 args = 12, 34, 56
300 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000301 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000302 green = pickler.dumps(orig, proto)
303 derived = unpickler.loads(green)
304 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000305
306 def test_compare(self):
307 t1 = timedelta(2, 3, 4)
308 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000309 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000310 self.assertTrue(t1 <= t2)
311 self.assertTrue(t1 >= t2)
312 self.assertTrue(not t1 != t2)
313 self.assertTrue(not t1 < t2)
314 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000315
316 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
317 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000318 self.assertTrue(t1 < t2)
319 self.assertTrue(t2 > t1)
320 self.assertTrue(t1 <= t2)
321 self.assertTrue(t2 >= t1)
322 self.assertTrue(t1 != t2)
323 self.assertTrue(t2 != t1)
324 self.assertTrue(not t1 == t2)
325 self.assertTrue(not t2 == t1)
326 self.assertTrue(not t1 > t2)
327 self.assertTrue(not t2 < t1)
328 self.assertTrue(not t1 >= t2)
329 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000330
Tim Peters68124bb2003-02-08 03:46:31 +0000331 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000332 self.assertEqual(t1 == badarg, False)
333 self.assertEqual(t1 != badarg, True)
334 self.assertEqual(badarg == t1, False)
335 self.assertEqual(badarg != t1, True)
336
Tim Peters2a799bf2002-12-16 20:18:38 +0000337 self.assertRaises(TypeError, lambda: t1 <= badarg)
338 self.assertRaises(TypeError, lambda: t1 < badarg)
339 self.assertRaises(TypeError, lambda: t1 > badarg)
340 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000341 self.assertRaises(TypeError, lambda: badarg <= t1)
342 self.assertRaises(TypeError, lambda: badarg < t1)
343 self.assertRaises(TypeError, lambda: badarg > t1)
344 self.assertRaises(TypeError, lambda: badarg >= t1)
345
346 def test_str(self):
347 td = timedelta
348 eq = self.assertEqual
349
350 eq(str(td(1)), "1 day, 0:00:00")
351 eq(str(td(-1)), "-1 day, 0:00:00")
352 eq(str(td(2)), "2 days, 0:00:00")
353 eq(str(td(-2)), "-2 days, 0:00:00")
354
355 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
356 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
357 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
358 "-210 days, 23:12:34")
359
360 eq(str(td(milliseconds=1)), "0:00:00.001000")
361 eq(str(td(microseconds=3)), "0:00:00.000003")
362
363 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
364 microseconds=999999)),
365 "999999999 days, 23:59:59.999999")
366
367 def test_roundtrip(self):
368 for td in (timedelta(days=999999999, hours=23, minutes=59,
369 seconds=59, microseconds=999999),
370 timedelta(days=-999999999),
371 timedelta(days=1, seconds=2, microseconds=3)):
372
373 # Verify td -> string -> td identity.
374 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000375 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000376 s = s[9:]
377 td2 = eval(s)
378 self.assertEqual(td, td2)
379
380 # Verify identity via reconstructing from pieces.
381 td2 = timedelta(td.days, td.seconds, td.microseconds)
382 self.assertEqual(td, td2)
383
384 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000385 self.assertIsInstance(timedelta.min, timedelta)
386 self.assertIsInstance(timedelta.max, timedelta)
387 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000388 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000389 self.assertEqual(timedelta.min, timedelta(-999999999))
390 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
391 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
392
393 def test_overflow(self):
394 tiny = timedelta.resolution
395
396 td = timedelta.min + tiny
397 td -= tiny # no problem
398 self.assertRaises(OverflowError, td.__sub__, tiny)
399 self.assertRaises(OverflowError, td.__add__, -tiny)
400
401 td = timedelta.max - tiny
402 td += tiny # no problem
403 self.assertRaises(OverflowError, td.__add__, tiny)
404 self.assertRaises(OverflowError, td.__sub__, -tiny)
405
406 self.assertRaises(OverflowError, lambda: -timedelta.max)
407
408 def test_microsecond_rounding(self):
409 td = timedelta
410 eq = self.assertEqual
411
412 # Single-field rounding.
413 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
414 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
415 eq(td(milliseconds=0.6/1000), td(microseconds=1))
416 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
417
418 # Rounding due to contributions from more than one field.
419 us_per_hour = 3600e6
420 us_per_day = us_per_hour * 24
421 eq(td(days=.4/us_per_day), td(0))
422 eq(td(hours=.2/us_per_hour), td(0))
423 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
424
425 eq(td(days=-.4/us_per_day), td(0))
426 eq(td(hours=-.2/us_per_hour), td(0))
427 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
428
429 def test_massive_normalization(self):
430 td = timedelta(microseconds=-1)
431 self.assertEqual((td.days, td.seconds, td.microseconds),
432 (-1, 24*3600-1, 999999))
433
434 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000435 self.assertTrue(timedelta(1))
436 self.assertTrue(timedelta(0, 1))
437 self.assertTrue(timedelta(0, 0, 1))
438 self.assertTrue(timedelta(microseconds=1))
439 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000440
Tim Petersb0c854d2003-05-17 15:57:00 +0000441 def test_subclass_timedelta(self):
442
443 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000444 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000445 def from_td(td):
446 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000447
448 def as_hours(self):
449 sum = (self.days * 24 +
450 self.seconds / 3600.0 +
451 self.microseconds / 3600e6)
452 return round(sum)
453
454 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000455 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000456 self.assertEqual(t1.as_hours(), 24)
457
458 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000459 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000460 self.assertEqual(t2.as_hours(), -25)
461
462 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000463 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000464 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000465 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000466 self.assertEqual(t3.days, t4.days)
467 self.assertEqual(t3.seconds, t4.seconds)
468 self.assertEqual(t3.microseconds, t4.microseconds)
469 self.assertEqual(str(t3), str(t4))
470 self.assertEqual(t4.as_hours(), -1)
471
Mark Dickinson7c186e22010-04-20 22:32:49 +0000472 def test_division(self):
473 t = timedelta(hours=1, minutes=24, seconds=19)
474 second = timedelta(seconds=1)
475 self.assertEqual(t / second, 5059.0)
476 self.assertEqual(t // second, 5059)
477
478 t = timedelta(minutes=2, seconds=30)
479 minute = timedelta(minutes=1)
480 self.assertEqual(t / minute, 2.5)
481 self.assertEqual(t // minute, 2)
482
483 zerotd = timedelta(0)
484 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
485 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
486
487 self.assertRaises(TypeError, truediv, t, 2)
488 # note: floor division of a timedelta by an integer *is*
489 # currently permitted.
490
491 def test_remainder(self):
492 t = timedelta(minutes=2, seconds=30)
493 minute = timedelta(minutes=1)
494 r = t % minute
495 self.assertEqual(r, timedelta(seconds=30))
496
497 t = timedelta(minutes=-2, seconds=30)
498 r = t % minute
499 self.assertEqual(r, timedelta(seconds=30))
500
501 zerotd = timedelta(0)
502 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
503
504 self.assertRaises(TypeError, mod, t, 10)
505
506 def test_divmod(self):
507 t = timedelta(minutes=2, seconds=30)
508 minute = timedelta(minutes=1)
509 q, r = divmod(t, minute)
510 self.assertEqual(q, 2)
511 self.assertEqual(r, timedelta(seconds=30))
512
513 t = timedelta(minutes=-2, seconds=30)
514 q, r = divmod(t, minute)
515 self.assertEqual(q, -2)
516 self.assertEqual(r, timedelta(seconds=30))
517
518 zerotd = timedelta(0)
519 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
520
521 self.assertRaises(TypeError, divmod, t, 10)
522
523
Tim Peters2a799bf2002-12-16 20:18:38 +0000524#############################################################################
525# date tests
526
527class TestDateOnly(unittest.TestCase):
528 # Tests here won't pass if also run on datetime objects, so don't
529 # subclass this to test datetimes too.
530
531 def test_delta_non_days_ignored(self):
532 dt = date(2000, 1, 2)
533 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
534 microseconds=5)
535 days = timedelta(delta.days)
536 self.assertEqual(days, timedelta(1))
537
538 dt2 = dt + delta
539 self.assertEqual(dt2, dt + days)
540
541 dt2 = delta + dt
542 self.assertEqual(dt2, dt + days)
543
544 dt2 = dt - delta
545 self.assertEqual(dt2, dt - days)
546
547 delta = -delta
548 days = timedelta(delta.days)
549 self.assertEqual(days, timedelta(-2))
550
551 dt2 = dt + delta
552 self.assertEqual(dt2, dt + days)
553
554 dt2 = delta + dt
555 self.assertEqual(dt2, dt + days)
556
557 dt2 = dt - delta
558 self.assertEqual(dt2, dt - days)
559
Tim Peters604c0132004-06-07 23:04:33 +0000560class SubclassDate(date):
561 sub_var = 1
562
Guido van Rossumd8faa362007-04-27 19:54:29 +0000563class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000564 # Tests here should pass for both dates and datetimes, except for a
565 # few tests that TestDateTime overrides.
566
567 theclass = date
568
569 def test_basic_attributes(self):
570 dt = self.theclass(2002, 3, 1)
571 self.assertEqual(dt.year, 2002)
572 self.assertEqual(dt.month, 3)
573 self.assertEqual(dt.day, 1)
574
575 def test_roundtrip(self):
576 for dt in (self.theclass(1, 2, 3),
577 self.theclass.today()):
578 # Verify dt -> string -> date identity.
579 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000580 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000581 s = s[9:]
582 dt2 = eval(s)
583 self.assertEqual(dt, dt2)
584
585 # Verify identity via reconstructing from pieces.
586 dt2 = self.theclass(dt.year, dt.month, dt.day)
587 self.assertEqual(dt, dt2)
588
589 def test_ordinal_conversions(self):
590 # Check some fixed values.
591 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
592 (1, 12, 31, 365),
593 (2, 1, 1, 366),
594 # first example from "Calendrical Calculations"
595 (1945, 11, 12, 710347)]:
596 d = self.theclass(y, m, d)
597 self.assertEqual(n, d.toordinal())
598 fromord = self.theclass.fromordinal(n)
599 self.assertEqual(d, fromord)
600 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000601 # if we're checking something fancier than a date, verify
602 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000603 self.assertEqual(fromord.hour, 0)
604 self.assertEqual(fromord.minute, 0)
605 self.assertEqual(fromord.second, 0)
606 self.assertEqual(fromord.microsecond, 0)
607
Tim Peters0bf60bd2003-01-08 20:40:01 +0000608 # Check first and last days of year spottily across the whole
609 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000610 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000611 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
612 d = self.theclass(year, 1, 1)
613 n = d.toordinal()
614 d2 = self.theclass.fromordinal(n)
615 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000616 # Verify that moving back a day gets to the end of year-1.
617 if year > 1:
618 d = self.theclass.fromordinal(n-1)
619 d2 = self.theclass(year-1, 12, 31)
620 self.assertEqual(d, d2)
621 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000622
623 # Test every day in a leap-year and a non-leap year.
624 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
625 for year, isleap in (2000, True), (2002, False):
626 n = self.theclass(year, 1, 1).toordinal()
627 for month, maxday in zip(range(1, 13), dim):
628 if month == 2 and isleap:
629 maxday += 1
630 for day in range(1, maxday+1):
631 d = self.theclass(year, month, day)
632 self.assertEqual(d.toordinal(), n)
633 self.assertEqual(d, self.theclass.fromordinal(n))
634 n += 1
635
636 def test_extreme_ordinals(self):
637 a = self.theclass.min
638 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
639 aord = a.toordinal()
640 b = a.fromordinal(aord)
641 self.assertEqual(a, b)
642
643 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
644
645 b = a + timedelta(days=1)
646 self.assertEqual(b.toordinal(), aord + 1)
647 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
648
649 a = self.theclass.max
650 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
651 aord = a.toordinal()
652 b = a.fromordinal(aord)
653 self.assertEqual(a, b)
654
655 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
656
657 b = a - timedelta(days=1)
658 self.assertEqual(b.toordinal(), aord - 1)
659 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
660
661 def test_bad_constructor_arguments(self):
662 # bad years
663 self.theclass(MINYEAR, 1, 1) # no exception
664 self.theclass(MAXYEAR, 1, 1) # no exception
665 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
666 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
667 # bad months
668 self.theclass(2000, 1, 1) # no exception
669 self.theclass(2000, 12, 1) # no exception
670 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
671 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
672 # bad days
673 self.theclass(2000, 2, 29) # no exception
674 self.theclass(2004, 2, 29) # no exception
675 self.theclass(2400, 2, 29) # no exception
676 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
677 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
678 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
679 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
680 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
681 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
682
683 def test_hash_equality(self):
684 d = self.theclass(2000, 12, 31)
685 # same thing
686 e = self.theclass(2000, 12, 31)
687 self.assertEqual(d, e)
688 self.assertEqual(hash(d), hash(e))
689
690 dic = {d: 1}
691 dic[e] = 2
692 self.assertEqual(len(dic), 1)
693 self.assertEqual(dic[d], 2)
694 self.assertEqual(dic[e], 2)
695
696 d = self.theclass(2001, 1, 1)
697 # same thing
698 e = self.theclass(2001, 1, 1)
699 self.assertEqual(d, e)
700 self.assertEqual(hash(d), hash(e))
701
702 dic = {d: 1}
703 dic[e] = 2
704 self.assertEqual(len(dic), 1)
705 self.assertEqual(dic[d], 2)
706 self.assertEqual(dic[e], 2)
707
708 def test_computations(self):
709 a = self.theclass(2002, 1, 31)
710 b = self.theclass(1956, 1, 31)
711
712 diff = a-b
713 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
714 self.assertEqual(diff.seconds, 0)
715 self.assertEqual(diff.microseconds, 0)
716
717 day = timedelta(1)
718 week = timedelta(7)
719 a = self.theclass(2002, 3, 2)
720 self.assertEqual(a + day, self.theclass(2002, 3, 3))
721 self.assertEqual(day + a, self.theclass(2002, 3, 3))
722 self.assertEqual(a - day, self.theclass(2002, 3, 1))
723 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
724 self.assertEqual(a + week, self.theclass(2002, 3, 9))
725 self.assertEqual(a - week, self.theclass(2002, 2, 23))
726 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
727 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
728 self.assertEqual((a + week) - a, week)
729 self.assertEqual((a + day) - a, day)
730 self.assertEqual((a - week) - a, -week)
731 self.assertEqual((a - day) - a, -day)
732 self.assertEqual(a - (a + week), -week)
733 self.assertEqual(a - (a + day), -day)
734 self.assertEqual(a - (a - week), week)
735 self.assertEqual(a - (a - day), day)
736
Mark Dickinson5c2db372009-12-05 20:28:34 +0000737 # Add/sub ints or floats should be illegal
738 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000739 self.assertRaises(TypeError, lambda: a+i)
740 self.assertRaises(TypeError, lambda: a-i)
741 self.assertRaises(TypeError, lambda: i+a)
742 self.assertRaises(TypeError, lambda: i-a)
743
744 # delta - date is senseless.
745 self.assertRaises(TypeError, lambda: day - a)
746 # mixing date and (delta or date) via * or // is senseless
747 self.assertRaises(TypeError, lambda: day * a)
748 self.assertRaises(TypeError, lambda: a * day)
749 self.assertRaises(TypeError, lambda: day // a)
750 self.assertRaises(TypeError, lambda: a // day)
751 self.assertRaises(TypeError, lambda: a * a)
752 self.assertRaises(TypeError, lambda: a // a)
753 # date + date is senseless
754 self.assertRaises(TypeError, lambda: a + a)
755
756 def test_overflow(self):
757 tiny = self.theclass.resolution
758
759 dt = self.theclass.min + tiny
760 dt -= tiny # no problem
761 self.assertRaises(OverflowError, dt.__sub__, tiny)
762 self.assertRaises(OverflowError, dt.__add__, -tiny)
763
764 dt = self.theclass.max - tiny
765 dt += tiny # no problem
766 self.assertRaises(OverflowError, dt.__add__, tiny)
767 self.assertRaises(OverflowError, dt.__sub__, -tiny)
768
769 def test_fromtimestamp(self):
770 import time
771
772 # Try an arbitrary fixed value.
773 year, month, day = 1999, 9, 19
774 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
775 d = self.theclass.fromtimestamp(ts)
776 self.assertEqual(d.year, year)
777 self.assertEqual(d.month, month)
778 self.assertEqual(d.day, day)
779
Tim Peters1b6f7a92004-06-20 02:50:16 +0000780 def test_insane_fromtimestamp(self):
781 # It's possible that some platform maps time_t to double,
782 # and that this test will fail there. This test should
783 # exempt such platforms (provided they return reasonable
784 # results!).
785 for insane in -1e200, 1e200:
786 self.assertRaises(ValueError, self.theclass.fromtimestamp,
787 insane)
788
Tim Peters2a799bf2002-12-16 20:18:38 +0000789 def test_today(self):
790 import time
791
792 # We claim that today() is like fromtimestamp(time.time()), so
793 # prove it.
794 for dummy in range(3):
795 today = self.theclass.today()
796 ts = time.time()
797 todayagain = self.theclass.fromtimestamp(ts)
798 if today == todayagain:
799 break
800 # There are several legit reasons that could fail:
801 # 1. It recently became midnight, between the today() and the
802 # time() calls.
803 # 2. The platform time() has such fine resolution that we'll
804 # never get the same value twice.
805 # 3. The platform time() has poor resolution, and we just
806 # happened to call today() right before a resolution quantum
807 # boundary.
808 # 4. The system clock got fiddled between calls.
809 # In any case, wait a little while and try again.
810 time.sleep(0.1)
811
812 # It worked or it didn't. If it didn't, assume it's reason #2, and
813 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000814 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000815 abs(todayagain - today) < timedelta(seconds=0.5))
816
817 def test_weekday(self):
818 for i in range(7):
819 # March 4, 2002 is a Monday
820 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
821 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
822 # January 2, 1956 is a Monday
823 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
824 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
825
826 def test_isocalendar(self):
827 # Check examples from
828 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
829 for i in range(7):
830 d = self.theclass(2003, 12, 22+i)
831 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
832 d = self.theclass(2003, 12, 29) + timedelta(i)
833 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
834 d = self.theclass(2004, 1, 5+i)
835 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
836 d = self.theclass(2009, 12, 21+i)
837 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
838 d = self.theclass(2009, 12, 28) + timedelta(i)
839 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
840 d = self.theclass(2010, 1, 4+i)
841 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
842
843 def test_iso_long_years(self):
844 # Calculate long ISO years and compare to table from
845 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
846 ISO_LONG_YEARS_TABLE = """
847 4 32 60 88
848 9 37 65 93
849 15 43 71 99
850 20 48 76
851 26 54 82
852
853 105 133 161 189
854 111 139 167 195
855 116 144 172
856 122 150 178
857 128 156 184
858
859 201 229 257 285
860 207 235 263 291
861 212 240 268 296
862 218 246 274
863 224 252 280
864
865 303 331 359 387
866 308 336 364 392
867 314 342 370 398
868 320 348 376
869 325 353 381
870 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000871 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000872 L = []
873 for i in range(400):
874 d = self.theclass(2000+i, 12, 31)
875 d1 = self.theclass(1600+i, 12, 31)
876 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
877 if d.isocalendar()[1] == 53:
878 L.append(i)
879 self.assertEqual(L, iso_long_years)
880
881 def test_isoformat(self):
882 t = self.theclass(2, 3, 2)
883 self.assertEqual(t.isoformat(), "0002-03-02")
884
885 def test_ctime(self):
886 t = self.theclass(2002, 3, 2)
887 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
888
889 def test_strftime(self):
890 t = self.theclass(2005, 3, 2)
891 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000892 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000893 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000894
895 self.assertRaises(TypeError, t.strftime) # needs an arg
896 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
897 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
898
Georg Brandlf78e02b2008-06-10 17:40:04 +0000899 # test that unicode input is allowed (issue 2782)
900 self.assertEqual(t.strftime("%m"), "03")
901
Tim Peters2a799bf2002-12-16 20:18:38 +0000902 # A naive object replaces %z and %Z w/ empty strings.
903 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
904
Benjamin Petersone1cdfd72009-01-18 21:02:37 +0000905 #make sure that invalid format specifiers are handled correctly
906 #self.assertRaises(ValueError, t.strftime, "%e")
907 #self.assertRaises(ValueError, t.strftime, "%")
908 #self.assertRaises(ValueError, t.strftime, "%#")
909
910 #oh well, some systems just ignore those invalid ones.
911 #at least, excercise them to make sure that no crashes
912 #are generated
913 for f in ["%e", "%", "%#"]:
914 try:
915 t.strftime(f)
916 except ValueError:
917 pass
918
919 #check that this standard extension works
920 t.strftime("%f")
921
Georg Brandlf78e02b2008-06-10 17:40:04 +0000922
Eric Smith1ba31142007-09-11 18:06:02 +0000923 def test_format(self):
924 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000925 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000926
927 # check that a derived class's __str__() gets called
928 class A(self.theclass):
929 def __str__(self):
930 return 'A'
931 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000932 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000933
934 # check that a derived class's strftime gets called
935 class B(self.theclass):
936 def strftime(self, format_spec):
937 return 'B'
938 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000939 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000940
941 for fmt in ["m:%m d:%d y:%y",
942 "m:%m d:%d y:%y H:%H M:%M S:%S",
943 "%z %Z",
944 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +0000945 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
946 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
947 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +0000948
Tim Peters2a799bf2002-12-16 20:18:38 +0000949 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000950 self.assertIsInstance(self.theclass.min, self.theclass)
951 self.assertIsInstance(self.theclass.max, self.theclass)
952 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000953 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000954
955 def test_extreme_timedelta(self):
956 big = self.theclass.max - self.theclass.min
957 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
958 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
959 # n == 315537897599999999 ~= 2**58.13
960 justasbig = timedelta(0, 0, n)
961 self.assertEqual(big, justasbig)
962 self.assertEqual(self.theclass.min + big, self.theclass.max)
963 self.assertEqual(self.theclass.max - big, self.theclass.min)
964
965 def test_timetuple(self):
966 for i in range(7):
967 # January 2, 1956 is a Monday (0)
968 d = self.theclass(1956, 1, 2+i)
969 t = d.timetuple()
970 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
971 # February 1, 1956 is a Wednesday (2)
972 d = self.theclass(1956, 2, 1+i)
973 t = d.timetuple()
974 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
975 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
976 # of the year.
977 d = self.theclass(1956, 3, 1+i)
978 t = d.timetuple()
979 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
980 self.assertEqual(t.tm_year, 1956)
981 self.assertEqual(t.tm_mon, 3)
982 self.assertEqual(t.tm_mday, 1+i)
983 self.assertEqual(t.tm_hour, 0)
984 self.assertEqual(t.tm_min, 0)
985 self.assertEqual(t.tm_sec, 0)
986 self.assertEqual(t.tm_wday, (3+i)%7)
987 self.assertEqual(t.tm_yday, 61+i)
988 self.assertEqual(t.tm_isdst, -1)
989
990 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000991 args = 6, 7, 23
992 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000993 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000994 green = pickler.dumps(orig, proto)
995 derived = unpickler.loads(green)
996 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000997
998 def test_compare(self):
999 t1 = self.theclass(2, 3, 4)
1000 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001001 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001002 self.assertTrue(t1 <= t2)
1003 self.assertTrue(t1 >= t2)
1004 self.assertTrue(not t1 != t2)
1005 self.assertTrue(not t1 < t2)
1006 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001007
1008 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1009 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001010 self.assertTrue(t1 < t2)
1011 self.assertTrue(t2 > t1)
1012 self.assertTrue(t1 <= t2)
1013 self.assertTrue(t2 >= t1)
1014 self.assertTrue(t1 != t2)
1015 self.assertTrue(t2 != t1)
1016 self.assertTrue(not t1 == t2)
1017 self.assertTrue(not t2 == t1)
1018 self.assertTrue(not t1 > t2)
1019 self.assertTrue(not t2 < t1)
1020 self.assertTrue(not t1 >= t2)
1021 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001022
Tim Peters68124bb2003-02-08 03:46:31 +00001023 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001024 self.assertEqual(t1 == badarg, False)
1025 self.assertEqual(t1 != badarg, True)
1026 self.assertEqual(badarg == t1, False)
1027 self.assertEqual(badarg != t1, True)
1028
Tim Peters2a799bf2002-12-16 20:18:38 +00001029 self.assertRaises(TypeError, lambda: t1 < badarg)
1030 self.assertRaises(TypeError, lambda: t1 > badarg)
1031 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001032 self.assertRaises(TypeError, lambda: badarg <= t1)
1033 self.assertRaises(TypeError, lambda: badarg < t1)
1034 self.assertRaises(TypeError, lambda: badarg > t1)
1035 self.assertRaises(TypeError, lambda: badarg >= t1)
1036
Tim Peters8d81a012003-01-24 22:36:34 +00001037 def test_mixed_compare(self):
1038 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001039
1040 # Our class can be compared for equality to other classes
1041 self.assertEqual(our == 1, False)
1042 self.assertEqual(1 == our, False)
1043 self.assertEqual(our != 1, True)
1044 self.assertEqual(1 != our, True)
1045
1046 # But the ordering is undefined
1047 self.assertRaises(TypeError, lambda: our < 1)
1048 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001049
Guido van Rossum19960592006-08-24 17:29:38 +00001050 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001051
Guido van Rossum19960592006-08-24 17:29:38 +00001052 class SomeClass:
1053 pass
1054
1055 their = SomeClass()
1056 self.assertEqual(our == their, False)
1057 self.assertEqual(their == our, False)
1058 self.assertEqual(our != their, True)
1059 self.assertEqual(their != our, True)
1060 self.assertRaises(TypeError, lambda: our < their)
1061 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001062
Guido van Rossum19960592006-08-24 17:29:38 +00001063 # However, if the other class explicitly defines ordering
1064 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001065
Guido van Rossum19960592006-08-24 17:29:38 +00001066 class LargerThanAnything:
1067 def __lt__(self, other):
1068 return False
1069 def __le__(self, other):
1070 return isinstance(other, LargerThanAnything)
1071 def __eq__(self, other):
1072 return isinstance(other, LargerThanAnything)
1073 def __ne__(self, other):
1074 return not isinstance(other, LargerThanAnything)
1075 def __gt__(self, other):
1076 return not isinstance(other, LargerThanAnything)
1077 def __ge__(self, other):
1078 return True
1079
1080 their = LargerThanAnything()
1081 self.assertEqual(our == their, False)
1082 self.assertEqual(their == our, False)
1083 self.assertEqual(our != their, True)
1084 self.assertEqual(their != our, True)
1085 self.assertEqual(our < their, True)
1086 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001087
Tim Peters2a799bf2002-12-16 20:18:38 +00001088 def test_bool(self):
1089 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001090 self.assertTrue(self.theclass.min)
1091 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001092
Guido van Rossum04110fb2007-08-24 16:32:05 +00001093 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001094 # For nasty technical reasons, we can't handle years before 1900.
1095 cls = self.theclass
1096 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1097 for y in 1, 49, 51, 99, 100, 1000, 1899:
1098 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001099
1100 def test_replace(self):
1101 cls = self.theclass
1102 args = [1, 2, 3]
1103 base = cls(*args)
1104 self.assertEqual(base, base.replace())
1105
1106 i = 0
1107 for name, newval in (("year", 2),
1108 ("month", 3),
1109 ("day", 4)):
1110 newargs = args[:]
1111 newargs[i] = newval
1112 expected = cls(*newargs)
1113 got = base.replace(**{name: newval})
1114 self.assertEqual(expected, got)
1115 i += 1
1116
1117 # Out of bounds.
1118 base = cls(2000, 2, 29)
1119 self.assertRaises(ValueError, base.replace, year=2001)
1120
Tim Petersa98924a2003-05-17 05:55:19 +00001121 def test_subclass_date(self):
1122
1123 class C(self.theclass):
1124 theAnswer = 42
1125
1126 def __new__(cls, *args, **kws):
1127 temp = kws.copy()
1128 extra = temp.pop('extra')
1129 result = self.theclass.__new__(cls, *args, **temp)
1130 result.extra = extra
1131 return result
1132
1133 def newmeth(self, start):
1134 return start + self.year + self.month
1135
1136 args = 2003, 4, 14
1137
1138 dt1 = self.theclass(*args)
1139 dt2 = C(*args, **{'extra': 7})
1140
1141 self.assertEqual(dt2.__class__, C)
1142 self.assertEqual(dt2.theAnswer, 42)
1143 self.assertEqual(dt2.extra, 7)
1144 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1145 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1146
Tim Peters604c0132004-06-07 23:04:33 +00001147 def test_pickling_subclass_date(self):
1148
1149 args = 6, 7, 23
1150 orig = SubclassDate(*args)
1151 for pickler, unpickler, proto in pickle_choices:
1152 green = pickler.dumps(orig, proto)
1153 derived = unpickler.loads(green)
1154 self.assertEqual(orig, derived)
1155
Tim Peters3f606292004-03-21 23:38:41 +00001156 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001157 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001158 # This is a low-overhead backdoor. A user can (by intent or
1159 # mistake) pass a string directly, which (if it's the right length)
1160 # will get treated like a pickle, and bypass the normal sanity
1161 # checks in the constructor. This can create insane objects.
1162 # The constructor doesn't want to burn the time to validate all
1163 # fields, but does check the month field. This stops, e.g.,
1164 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001165 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001166 if not issubclass(self.theclass, datetime):
1167 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001168 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001169 self.assertRaises(TypeError, self.theclass,
1170 base[:2] + month_byte + base[3:])
1171 for ord_byte in range(1, 13):
1172 # This shouldn't blow up because of the month byte alone. If
1173 # the implementation changes to do more-careful checking, it may
1174 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001175 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001176
Tim Peters2a799bf2002-12-16 20:18:38 +00001177#############################################################################
1178# datetime tests
1179
Tim Peters604c0132004-06-07 23:04:33 +00001180class SubclassDatetime(datetime):
1181 sub_var = 1
1182
Tim Peters2a799bf2002-12-16 20:18:38 +00001183class TestDateTime(TestDate):
1184
1185 theclass = datetime
1186
1187 def test_basic_attributes(self):
1188 dt = self.theclass(2002, 3, 1, 12, 0)
1189 self.assertEqual(dt.year, 2002)
1190 self.assertEqual(dt.month, 3)
1191 self.assertEqual(dt.day, 1)
1192 self.assertEqual(dt.hour, 12)
1193 self.assertEqual(dt.minute, 0)
1194 self.assertEqual(dt.second, 0)
1195 self.assertEqual(dt.microsecond, 0)
1196
1197 def test_basic_attributes_nonzero(self):
1198 # Make sure all attributes are non-zero so bugs in
1199 # bit-shifting access show up.
1200 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1201 self.assertEqual(dt.year, 2002)
1202 self.assertEqual(dt.month, 3)
1203 self.assertEqual(dt.day, 1)
1204 self.assertEqual(dt.hour, 12)
1205 self.assertEqual(dt.minute, 59)
1206 self.assertEqual(dt.second, 59)
1207 self.assertEqual(dt.microsecond, 8000)
1208
1209 def test_roundtrip(self):
1210 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1211 self.theclass.now()):
1212 # Verify dt -> string -> datetime identity.
1213 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001214 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001215 s = s[9:]
1216 dt2 = eval(s)
1217 self.assertEqual(dt, dt2)
1218
1219 # Verify identity via reconstructing from pieces.
1220 dt2 = self.theclass(dt.year, dt.month, dt.day,
1221 dt.hour, dt.minute, dt.second,
1222 dt.microsecond)
1223 self.assertEqual(dt, dt2)
1224
1225 def test_isoformat(self):
1226 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1227 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1228 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1229 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001230 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001231 # str is ISO format with the separator forced to a blank.
1232 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1233
1234 t = self.theclass(2, 3, 2)
1235 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1236 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1237 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1238 # str is ISO format with the separator forced to a blank.
1239 self.assertEqual(str(t), "0002-03-02 00:00:00")
1240
Eric Smith1ba31142007-09-11 18:06:02 +00001241 def test_format(self):
1242 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001243 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001244
1245 # check that a derived class's __str__() gets called
1246 class A(self.theclass):
1247 def __str__(self):
1248 return 'A'
1249 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001250 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001251
1252 # check that a derived class's strftime gets called
1253 class B(self.theclass):
1254 def strftime(self, format_spec):
1255 return 'B'
1256 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001257 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001258
1259 for fmt in ["m:%m d:%d y:%y",
1260 "m:%m d:%d y:%y H:%H M:%M S:%S",
1261 "%z %Z",
1262 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001263 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1264 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1265 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001266
Tim Peters2a799bf2002-12-16 20:18:38 +00001267 def test_more_ctime(self):
1268 # Test fields that TestDate doesn't touch.
1269 import time
1270
1271 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1272 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1273 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1274 # out. The difference is that t.ctime() produces " 2" for the day,
1275 # but platform ctime() produces "02" for the day. According to
1276 # C99, t.ctime() is correct here.
1277 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1278
1279 # So test a case where that difference doesn't matter.
1280 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1281 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1282
1283 def test_tz_independent_comparing(self):
1284 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1285 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1286 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1287 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001288 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001289
1290 # Make sure comparison doesn't forget microseconds, and isn't done
1291 # via comparing a float timestamp (an IEEE double doesn't have enough
1292 # precision to span microsecond resolution across years 1 thru 9999,
1293 # so comparing via timestamp necessarily calls some distinct values
1294 # equal).
1295 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1296 us = timedelta(microseconds=1)
1297 dt2 = dt1 + us
1298 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001299 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001300
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001301 def test_strftime_with_bad_tzname_replace(self):
1302 # verify ok if tzinfo.tzname().replace() returns a non-string
1303 class MyTzInfo(FixedOffset):
1304 def tzname(self, dt):
1305 class MyStr(str):
1306 def replace(self, *args):
1307 return None
1308 return MyStr('name')
1309 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1310 self.assertRaises(TypeError, t.strftime, '%Z')
1311
Tim Peters2a799bf2002-12-16 20:18:38 +00001312 def test_bad_constructor_arguments(self):
1313 # bad years
1314 self.theclass(MINYEAR, 1, 1) # no exception
1315 self.theclass(MAXYEAR, 1, 1) # no exception
1316 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1317 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1318 # bad months
1319 self.theclass(2000, 1, 1) # no exception
1320 self.theclass(2000, 12, 1) # no exception
1321 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1322 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1323 # bad days
1324 self.theclass(2000, 2, 29) # no exception
1325 self.theclass(2004, 2, 29) # no exception
1326 self.theclass(2400, 2, 29) # no exception
1327 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1328 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1329 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1330 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1331 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1332 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1333 # bad hours
1334 self.theclass(2000, 1, 31, 0) # no exception
1335 self.theclass(2000, 1, 31, 23) # no exception
1336 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1337 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1338 # bad minutes
1339 self.theclass(2000, 1, 31, 23, 0) # no exception
1340 self.theclass(2000, 1, 31, 23, 59) # no exception
1341 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1342 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1343 # bad seconds
1344 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1345 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1346 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1347 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1348 # bad microseconds
1349 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1350 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1351 self.assertRaises(ValueError, self.theclass,
1352 2000, 1, 31, 23, 59, 59, -1)
1353 self.assertRaises(ValueError, self.theclass,
1354 2000, 1, 31, 23, 59, 59,
1355 1000000)
1356
1357 def test_hash_equality(self):
1358 d = self.theclass(2000, 12, 31, 23, 30, 17)
1359 e = self.theclass(2000, 12, 31, 23, 30, 17)
1360 self.assertEqual(d, e)
1361 self.assertEqual(hash(d), hash(e))
1362
1363 dic = {d: 1}
1364 dic[e] = 2
1365 self.assertEqual(len(dic), 1)
1366 self.assertEqual(dic[d], 2)
1367 self.assertEqual(dic[e], 2)
1368
1369 d = self.theclass(2001, 1, 1, 0, 5, 17)
1370 e = self.theclass(2001, 1, 1, 0, 5, 17)
1371 self.assertEqual(d, e)
1372 self.assertEqual(hash(d), hash(e))
1373
1374 dic = {d: 1}
1375 dic[e] = 2
1376 self.assertEqual(len(dic), 1)
1377 self.assertEqual(dic[d], 2)
1378 self.assertEqual(dic[e], 2)
1379
1380 def test_computations(self):
1381 a = self.theclass(2002, 1, 31)
1382 b = self.theclass(1956, 1, 31)
1383 diff = a-b
1384 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1385 self.assertEqual(diff.seconds, 0)
1386 self.assertEqual(diff.microseconds, 0)
1387 a = self.theclass(2002, 3, 2, 17, 6)
1388 millisec = timedelta(0, 0, 1000)
1389 hour = timedelta(0, 3600)
1390 day = timedelta(1)
1391 week = timedelta(7)
1392 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1393 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1394 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1395 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1396 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1397 self.assertEqual(a - hour, a + -hour)
1398 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1399 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1400 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1401 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1402 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1403 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1404 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1405 self.assertEqual((a + week) - a, week)
1406 self.assertEqual((a + day) - a, day)
1407 self.assertEqual((a + hour) - a, hour)
1408 self.assertEqual((a + millisec) - a, millisec)
1409 self.assertEqual((a - week) - a, -week)
1410 self.assertEqual((a - day) - a, -day)
1411 self.assertEqual((a - hour) - a, -hour)
1412 self.assertEqual((a - millisec) - a, -millisec)
1413 self.assertEqual(a - (a + week), -week)
1414 self.assertEqual(a - (a + day), -day)
1415 self.assertEqual(a - (a + hour), -hour)
1416 self.assertEqual(a - (a + millisec), -millisec)
1417 self.assertEqual(a - (a - week), week)
1418 self.assertEqual(a - (a - day), day)
1419 self.assertEqual(a - (a - hour), hour)
1420 self.assertEqual(a - (a - millisec), millisec)
1421 self.assertEqual(a + (week + day + hour + millisec),
1422 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1423 self.assertEqual(a + (week + day + hour + millisec),
1424 (((a + week) + day) + hour) + millisec)
1425 self.assertEqual(a - (week + day + hour + millisec),
1426 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1427 self.assertEqual(a - (week + day + hour + millisec),
1428 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001429 # Add/sub ints or floats should be illegal
1430 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001431 self.assertRaises(TypeError, lambda: a+i)
1432 self.assertRaises(TypeError, lambda: a-i)
1433 self.assertRaises(TypeError, lambda: i+a)
1434 self.assertRaises(TypeError, lambda: i-a)
1435
1436 # delta - datetime is senseless.
1437 self.assertRaises(TypeError, lambda: day - a)
1438 # mixing datetime and (delta or datetime) via * or // is senseless
1439 self.assertRaises(TypeError, lambda: day * a)
1440 self.assertRaises(TypeError, lambda: a * day)
1441 self.assertRaises(TypeError, lambda: day // a)
1442 self.assertRaises(TypeError, lambda: a // day)
1443 self.assertRaises(TypeError, lambda: a * a)
1444 self.assertRaises(TypeError, lambda: a // a)
1445 # datetime + datetime is senseless
1446 self.assertRaises(TypeError, lambda: a + a)
1447
1448 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001449 args = 6, 7, 23, 20, 59, 1, 64**2
1450 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001451 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001452 green = pickler.dumps(orig, proto)
1453 derived = unpickler.loads(green)
1454 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001455
Guido van Rossum275666f2003-02-07 21:49:01 +00001456 def test_more_pickling(self):
1457 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1458 s = pickle.dumps(a)
1459 b = pickle.loads(s)
1460 self.assertEqual(b.year, 2003)
1461 self.assertEqual(b.month, 2)
1462 self.assertEqual(b.day, 7)
1463
Tim Peters604c0132004-06-07 23:04:33 +00001464 def test_pickling_subclass_datetime(self):
1465 args = 6, 7, 23, 20, 59, 1, 64**2
1466 orig = SubclassDatetime(*args)
1467 for pickler, unpickler, proto in pickle_choices:
1468 green = pickler.dumps(orig, proto)
1469 derived = unpickler.loads(green)
1470 self.assertEqual(orig, derived)
1471
Tim Peters2a799bf2002-12-16 20:18:38 +00001472 def test_more_compare(self):
1473 # The test_compare() inherited from TestDate covers the error cases.
1474 # We just want to test lexicographic ordering on the members datetime
1475 # has that date lacks.
1476 args = [2000, 11, 29, 20, 58, 16, 999998]
1477 t1 = self.theclass(*args)
1478 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001479 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001480 self.assertTrue(t1 <= t2)
1481 self.assertTrue(t1 >= t2)
1482 self.assertTrue(not t1 != t2)
1483 self.assertTrue(not t1 < t2)
1484 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001485
1486 for i in range(len(args)):
1487 newargs = args[:]
1488 newargs[i] = args[i] + 1
1489 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001490 self.assertTrue(t1 < t2)
1491 self.assertTrue(t2 > t1)
1492 self.assertTrue(t1 <= t2)
1493 self.assertTrue(t2 >= t1)
1494 self.assertTrue(t1 != t2)
1495 self.assertTrue(t2 != t1)
1496 self.assertTrue(not t1 == t2)
1497 self.assertTrue(not t2 == t1)
1498 self.assertTrue(not t1 > t2)
1499 self.assertTrue(not t2 < t1)
1500 self.assertTrue(not t1 >= t2)
1501 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001502
1503
1504 # A helper for timestamp constructor tests.
1505 def verify_field_equality(self, expected, got):
1506 self.assertEqual(expected.tm_year, got.year)
1507 self.assertEqual(expected.tm_mon, got.month)
1508 self.assertEqual(expected.tm_mday, got.day)
1509 self.assertEqual(expected.tm_hour, got.hour)
1510 self.assertEqual(expected.tm_min, got.minute)
1511 self.assertEqual(expected.tm_sec, got.second)
1512
1513 def test_fromtimestamp(self):
1514 import time
1515
1516 ts = time.time()
1517 expected = time.localtime(ts)
1518 got = self.theclass.fromtimestamp(ts)
1519 self.verify_field_equality(expected, got)
1520
1521 def test_utcfromtimestamp(self):
1522 import time
1523
1524 ts = time.time()
1525 expected = time.gmtime(ts)
1526 got = self.theclass.utcfromtimestamp(ts)
1527 self.verify_field_equality(expected, got)
1528
Thomas Wouters477c8d52006-05-27 19:21:47 +00001529 def test_microsecond_rounding(self):
1530 # Test whether fromtimestamp "rounds up" floats that are less
1531 # than one microsecond smaller than an integer.
1532 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1533 self.theclass.fromtimestamp(1))
1534
Tim Peters1b6f7a92004-06-20 02:50:16 +00001535 def test_insane_fromtimestamp(self):
1536 # It's possible that some platform maps time_t to double,
1537 # and that this test will fail there. This test should
1538 # exempt such platforms (provided they return reasonable
1539 # results!).
1540 for insane in -1e200, 1e200:
1541 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1542 insane)
1543
1544 def test_insane_utcfromtimestamp(self):
1545 # It's possible that some platform maps time_t to double,
1546 # and that this test will fail there. This test should
1547 # exempt such platforms (provided they return reasonable
1548 # results!).
1549 for insane in -1e200, 1e200:
1550 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1551 insane)
1552
Guido van Rossumd8faa362007-04-27 19:54:29 +00001553 def test_negative_float_fromtimestamp(self):
1554 # Windows doesn't accept negative timestamps
1555 if os.name == "nt":
1556 return
1557 # The result is tz-dependent; at least test that this doesn't
1558 # fail (like it did before bug 1646728 was fixed).
1559 self.theclass.fromtimestamp(-1.05)
1560
1561 def test_negative_float_utcfromtimestamp(self):
1562 # Windows doesn't accept negative timestamps
1563 if os.name == "nt":
1564 return
1565 d = self.theclass.utcfromtimestamp(-1.05)
1566 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1567
Tim Peters2a799bf2002-12-16 20:18:38 +00001568 def test_utcnow(self):
1569 import time
1570
1571 # Call it a success if utcnow() and utcfromtimestamp() are within
1572 # a second of each other.
1573 tolerance = timedelta(seconds=1)
1574 for dummy in range(3):
1575 from_now = self.theclass.utcnow()
1576 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1577 if abs(from_timestamp - from_now) <= tolerance:
1578 break
1579 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001580 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001581
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001582 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001583 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001584
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001585 string = '2004-12-01 13:02:47.197'
1586 format = '%Y-%m-%d %H:%M:%S.%f'
1587 result, frac = _strptime._strptime(string, format)
1588 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001589 got = self.theclass.strptime(string, format)
1590 self.assertEqual(expected, got)
1591
Tim Peters2a799bf2002-12-16 20:18:38 +00001592 def test_more_timetuple(self):
1593 # This tests fields beyond those tested by the TestDate.test_timetuple.
1594 t = self.theclass(2004, 12, 31, 6, 22, 33)
1595 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1596 self.assertEqual(t.timetuple(),
1597 (t.year, t.month, t.day,
1598 t.hour, t.minute, t.second,
1599 t.weekday(),
1600 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1601 -1))
1602 tt = t.timetuple()
1603 self.assertEqual(tt.tm_year, t.year)
1604 self.assertEqual(tt.tm_mon, t.month)
1605 self.assertEqual(tt.tm_mday, t.day)
1606 self.assertEqual(tt.tm_hour, t.hour)
1607 self.assertEqual(tt.tm_min, t.minute)
1608 self.assertEqual(tt.tm_sec, t.second)
1609 self.assertEqual(tt.tm_wday, t.weekday())
1610 self.assertEqual(tt.tm_yday, t.toordinal() -
1611 date(t.year, 1, 1).toordinal() + 1)
1612 self.assertEqual(tt.tm_isdst, -1)
1613
1614 def test_more_strftime(self):
1615 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001616 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1617 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1618 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001619
1620 def test_extract(self):
1621 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1622 self.assertEqual(dt.date(), date(2002, 3, 4))
1623 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1624
1625 def test_combine(self):
1626 d = date(2002, 3, 4)
1627 t = time(18, 45, 3, 1234)
1628 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1629 combine = self.theclass.combine
1630 dt = combine(d, t)
1631 self.assertEqual(dt, expected)
1632
1633 dt = combine(time=t, date=d)
1634 self.assertEqual(dt, expected)
1635
1636 self.assertEqual(d, dt.date())
1637 self.assertEqual(t, dt.time())
1638 self.assertEqual(dt, combine(dt.date(), dt.time()))
1639
1640 self.assertRaises(TypeError, combine) # need an arg
1641 self.assertRaises(TypeError, combine, d) # need two args
1642 self.assertRaises(TypeError, combine, t, d) # args reversed
1643 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1644 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1645
Tim Peters12bf3392002-12-24 05:41:27 +00001646 def test_replace(self):
1647 cls = self.theclass
1648 args = [1, 2, 3, 4, 5, 6, 7]
1649 base = cls(*args)
1650 self.assertEqual(base, base.replace())
1651
1652 i = 0
1653 for name, newval in (("year", 2),
1654 ("month", 3),
1655 ("day", 4),
1656 ("hour", 5),
1657 ("minute", 6),
1658 ("second", 7),
1659 ("microsecond", 8)):
1660 newargs = args[:]
1661 newargs[i] = newval
1662 expected = cls(*newargs)
1663 got = base.replace(**{name: newval})
1664 self.assertEqual(expected, got)
1665 i += 1
1666
1667 # Out of bounds.
1668 base = cls(2000, 2, 29)
1669 self.assertRaises(ValueError, base.replace, year=2001)
1670
Tim Peters80475bb2002-12-25 07:40:55 +00001671 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001672 # Pretty boring! The TZ test is more interesting here. astimezone()
1673 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001674 dt = self.theclass.now()
1675 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001676 self.assertRaises(TypeError, dt.astimezone) # not enough args
1677 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1678 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001679 self.assertRaises(ValueError, dt.astimezone, f) # naive
1680 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001681
Tim Peters52dcce22003-01-23 16:36:11 +00001682 class Bogus(tzinfo):
1683 def utcoffset(self, dt): return None
1684 def dst(self, dt): return timedelta(0)
1685 bog = Bogus()
1686 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1687
1688 class AlsoBogus(tzinfo):
1689 def utcoffset(self, dt): return timedelta(0)
1690 def dst(self, dt): return None
1691 alsobog = AlsoBogus()
1692 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001693
Tim Petersa98924a2003-05-17 05:55:19 +00001694 def test_subclass_datetime(self):
1695
1696 class C(self.theclass):
1697 theAnswer = 42
1698
1699 def __new__(cls, *args, **kws):
1700 temp = kws.copy()
1701 extra = temp.pop('extra')
1702 result = self.theclass.__new__(cls, *args, **temp)
1703 result.extra = extra
1704 return result
1705
1706 def newmeth(self, start):
1707 return start + self.year + self.month + self.second
1708
1709 args = 2003, 4, 14, 12, 13, 41
1710
1711 dt1 = self.theclass(*args)
1712 dt2 = C(*args, **{'extra': 7})
1713
1714 self.assertEqual(dt2.__class__, C)
1715 self.assertEqual(dt2.theAnswer, 42)
1716 self.assertEqual(dt2.extra, 7)
1717 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1718 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1719 dt1.second - 7)
1720
Tim Peters604c0132004-06-07 23:04:33 +00001721class SubclassTime(time):
1722 sub_var = 1
1723
Guido van Rossumd8faa362007-04-27 19:54:29 +00001724class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001725
1726 theclass = time
1727
1728 def test_basic_attributes(self):
1729 t = self.theclass(12, 0)
1730 self.assertEqual(t.hour, 12)
1731 self.assertEqual(t.minute, 0)
1732 self.assertEqual(t.second, 0)
1733 self.assertEqual(t.microsecond, 0)
1734
1735 def test_basic_attributes_nonzero(self):
1736 # Make sure all attributes are non-zero so bugs in
1737 # bit-shifting access show up.
1738 t = self.theclass(12, 59, 59, 8000)
1739 self.assertEqual(t.hour, 12)
1740 self.assertEqual(t.minute, 59)
1741 self.assertEqual(t.second, 59)
1742 self.assertEqual(t.microsecond, 8000)
1743
1744 def test_roundtrip(self):
1745 t = self.theclass(1, 2, 3, 4)
1746
1747 # Verify t -> string -> time identity.
1748 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001749 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001750 s = s[9:]
1751 t2 = eval(s)
1752 self.assertEqual(t, t2)
1753
1754 # Verify identity via reconstructing from pieces.
1755 t2 = self.theclass(t.hour, t.minute, t.second,
1756 t.microsecond)
1757 self.assertEqual(t, t2)
1758
1759 def test_comparing(self):
1760 args = [1, 2, 3, 4]
1761 t1 = self.theclass(*args)
1762 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001763 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001764 self.assertTrue(t1 <= t2)
1765 self.assertTrue(t1 >= t2)
1766 self.assertTrue(not t1 != t2)
1767 self.assertTrue(not t1 < t2)
1768 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001769
1770 for i in range(len(args)):
1771 newargs = args[:]
1772 newargs[i] = args[i] + 1
1773 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001774 self.assertTrue(t1 < t2)
1775 self.assertTrue(t2 > t1)
1776 self.assertTrue(t1 <= t2)
1777 self.assertTrue(t2 >= t1)
1778 self.assertTrue(t1 != t2)
1779 self.assertTrue(t2 != t1)
1780 self.assertTrue(not t1 == t2)
1781 self.assertTrue(not t2 == t1)
1782 self.assertTrue(not t1 > t2)
1783 self.assertTrue(not t2 < t1)
1784 self.assertTrue(not t1 >= t2)
1785 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001786
Tim Peters68124bb2003-02-08 03:46:31 +00001787 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001788 self.assertEqual(t1 == badarg, False)
1789 self.assertEqual(t1 != badarg, True)
1790 self.assertEqual(badarg == t1, False)
1791 self.assertEqual(badarg != t1, True)
1792
Tim Peters2a799bf2002-12-16 20:18:38 +00001793 self.assertRaises(TypeError, lambda: t1 <= badarg)
1794 self.assertRaises(TypeError, lambda: t1 < badarg)
1795 self.assertRaises(TypeError, lambda: t1 > badarg)
1796 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001797 self.assertRaises(TypeError, lambda: badarg <= t1)
1798 self.assertRaises(TypeError, lambda: badarg < t1)
1799 self.assertRaises(TypeError, lambda: badarg > t1)
1800 self.assertRaises(TypeError, lambda: badarg >= t1)
1801
1802 def test_bad_constructor_arguments(self):
1803 # bad hours
1804 self.theclass(0, 0) # no exception
1805 self.theclass(23, 0) # no exception
1806 self.assertRaises(ValueError, self.theclass, -1, 0)
1807 self.assertRaises(ValueError, self.theclass, 24, 0)
1808 # bad minutes
1809 self.theclass(23, 0) # no exception
1810 self.theclass(23, 59) # no exception
1811 self.assertRaises(ValueError, self.theclass, 23, -1)
1812 self.assertRaises(ValueError, self.theclass, 23, 60)
1813 # bad seconds
1814 self.theclass(23, 59, 0) # no exception
1815 self.theclass(23, 59, 59) # no exception
1816 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1817 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1818 # bad microseconds
1819 self.theclass(23, 59, 59, 0) # no exception
1820 self.theclass(23, 59, 59, 999999) # no exception
1821 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1822 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1823
1824 def test_hash_equality(self):
1825 d = self.theclass(23, 30, 17)
1826 e = self.theclass(23, 30, 17)
1827 self.assertEqual(d, e)
1828 self.assertEqual(hash(d), hash(e))
1829
1830 dic = {d: 1}
1831 dic[e] = 2
1832 self.assertEqual(len(dic), 1)
1833 self.assertEqual(dic[d], 2)
1834 self.assertEqual(dic[e], 2)
1835
1836 d = self.theclass(0, 5, 17)
1837 e = self.theclass(0, 5, 17)
1838 self.assertEqual(d, e)
1839 self.assertEqual(hash(d), hash(e))
1840
1841 dic = {d: 1}
1842 dic[e] = 2
1843 self.assertEqual(len(dic), 1)
1844 self.assertEqual(dic[d], 2)
1845 self.assertEqual(dic[e], 2)
1846
1847 def test_isoformat(self):
1848 t = self.theclass(4, 5, 1, 123)
1849 self.assertEqual(t.isoformat(), "04:05:01.000123")
1850 self.assertEqual(t.isoformat(), str(t))
1851
1852 t = self.theclass()
1853 self.assertEqual(t.isoformat(), "00:00:00")
1854 self.assertEqual(t.isoformat(), str(t))
1855
1856 t = self.theclass(microsecond=1)
1857 self.assertEqual(t.isoformat(), "00:00:00.000001")
1858 self.assertEqual(t.isoformat(), str(t))
1859
1860 t = self.theclass(microsecond=10)
1861 self.assertEqual(t.isoformat(), "00:00:00.000010")
1862 self.assertEqual(t.isoformat(), str(t))
1863
1864 t = self.theclass(microsecond=100)
1865 self.assertEqual(t.isoformat(), "00:00:00.000100")
1866 self.assertEqual(t.isoformat(), str(t))
1867
1868 t = self.theclass(microsecond=1000)
1869 self.assertEqual(t.isoformat(), "00:00:00.001000")
1870 self.assertEqual(t.isoformat(), str(t))
1871
1872 t = self.theclass(microsecond=10000)
1873 self.assertEqual(t.isoformat(), "00:00:00.010000")
1874 self.assertEqual(t.isoformat(), str(t))
1875
1876 t = self.theclass(microsecond=100000)
1877 self.assertEqual(t.isoformat(), "00:00:00.100000")
1878 self.assertEqual(t.isoformat(), str(t))
1879
Thomas Wouterscf297e42007-02-23 15:07:44 +00001880 def test_1653736(self):
1881 # verify it doesn't accept extra keyword arguments
1882 t = self.theclass(second=1)
1883 self.assertRaises(TypeError, t.isoformat, foo=3)
1884
Tim Peters2a799bf2002-12-16 20:18:38 +00001885 def test_strftime(self):
1886 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001887 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001888 # A naive object replaces %z and %Z with empty strings.
1889 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1890
Eric Smith1ba31142007-09-11 18:06:02 +00001891 def test_format(self):
1892 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001893 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001894
1895 # check that a derived class's __str__() gets called
1896 class A(self.theclass):
1897 def __str__(self):
1898 return 'A'
1899 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001900 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001901
1902 # check that a derived class's strftime gets called
1903 class B(self.theclass):
1904 def strftime(self, format_spec):
1905 return 'B'
1906 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001907 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001908
1909 for fmt in ['%H %M %S',
1910 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001911 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1912 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1913 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001914
Tim Peters2a799bf2002-12-16 20:18:38 +00001915 def test_str(self):
1916 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1917 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1918 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1919 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1920 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1921
1922 def test_repr(self):
1923 name = 'datetime.' + self.theclass.__name__
1924 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1925 "%s(1, 2, 3, 4)" % name)
1926 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1927 "%s(10, 2, 3, 4000)" % name)
1928 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1929 "%s(0, 2, 3, 400000)" % name)
1930 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1931 "%s(12, 2, 3)" % name)
1932 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1933 "%s(23, 15)" % name)
1934
1935 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00001936 self.assertIsInstance(self.theclass.min, self.theclass)
1937 self.assertIsInstance(self.theclass.max, self.theclass)
1938 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001939 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001940
1941 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001942 args = 20, 59, 16, 64**2
1943 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001944 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001945 green = pickler.dumps(orig, proto)
1946 derived = unpickler.loads(green)
1947 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001948
Tim Peters604c0132004-06-07 23:04:33 +00001949 def test_pickling_subclass_time(self):
1950 args = 20, 59, 16, 64**2
1951 orig = SubclassTime(*args)
1952 for pickler, unpickler, proto in pickle_choices:
1953 green = pickler.dumps(orig, proto)
1954 derived = unpickler.loads(green)
1955 self.assertEqual(orig, derived)
1956
Tim Peters2a799bf2002-12-16 20:18:38 +00001957 def test_bool(self):
1958 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001959 self.assertTrue(cls(1))
1960 self.assertTrue(cls(0, 1))
1961 self.assertTrue(cls(0, 0, 1))
1962 self.assertTrue(cls(0, 0, 0, 1))
1963 self.assertTrue(not cls(0))
1964 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001965
Tim Peters12bf3392002-12-24 05:41:27 +00001966 def test_replace(self):
1967 cls = self.theclass
1968 args = [1, 2, 3, 4]
1969 base = cls(*args)
1970 self.assertEqual(base, base.replace())
1971
1972 i = 0
1973 for name, newval in (("hour", 5),
1974 ("minute", 6),
1975 ("second", 7),
1976 ("microsecond", 8)):
1977 newargs = args[:]
1978 newargs[i] = newval
1979 expected = cls(*newargs)
1980 got = base.replace(**{name: newval})
1981 self.assertEqual(expected, got)
1982 i += 1
1983
1984 # Out of bounds.
1985 base = cls(1)
1986 self.assertRaises(ValueError, base.replace, hour=24)
1987 self.assertRaises(ValueError, base.replace, minute=-1)
1988 self.assertRaises(ValueError, base.replace, second=100)
1989 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1990
Tim Petersa98924a2003-05-17 05:55:19 +00001991 def test_subclass_time(self):
1992
1993 class C(self.theclass):
1994 theAnswer = 42
1995
1996 def __new__(cls, *args, **kws):
1997 temp = kws.copy()
1998 extra = temp.pop('extra')
1999 result = self.theclass.__new__(cls, *args, **temp)
2000 result.extra = extra
2001 return result
2002
2003 def newmeth(self, start):
2004 return start + self.hour + self.second
2005
2006 args = 4, 5, 6
2007
2008 dt1 = self.theclass(*args)
2009 dt2 = C(*args, **{'extra': 7})
2010
2011 self.assertEqual(dt2.__class__, C)
2012 self.assertEqual(dt2.theAnswer, 42)
2013 self.assertEqual(dt2.extra, 7)
2014 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2015 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2016
Armin Rigof4afb212005-11-07 07:15:48 +00002017 def test_backdoor_resistance(self):
2018 # see TestDate.test_backdoor_resistance().
2019 base = '2:59.0'
2020 for hour_byte in ' ', '9', chr(24), '\xff':
2021 self.assertRaises(TypeError, self.theclass,
2022 hour_byte + base[1:])
2023
Tim Peters855fe882002-12-22 03:43:39 +00002024# A mixin for classes with a tzinfo= argument. Subclasses must define
2025# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002026# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002027class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002028
Tim Petersbad8ff02002-12-30 20:52:32 +00002029 def test_argument_passing(self):
2030 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002031 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002032 class introspective(tzinfo):
2033 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002034 def utcoffset(self, dt):
2035 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002036 dst = utcoffset
2037
2038 obj = cls(1, 2, 3, tzinfo=introspective())
2039
Tim Peters0bf60bd2003-01-08 20:40:01 +00002040 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002041 self.assertEqual(obj.tzname(), expected)
2042
Tim Peters0bf60bd2003-01-08 20:40:01 +00002043 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002044 self.assertEqual(obj.utcoffset(), expected)
2045 self.assertEqual(obj.dst(), expected)
2046
Tim Peters855fe882002-12-22 03:43:39 +00002047 def test_bad_tzinfo_classes(self):
2048 cls = self.theclass
2049 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002050
Tim Peters855fe882002-12-22 03:43:39 +00002051 class NiceTry(object):
2052 def __init__(self): pass
2053 def utcoffset(self, dt): pass
2054 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2055
2056 class BetterTry(tzinfo):
2057 def __init__(self): pass
2058 def utcoffset(self, dt): pass
2059 b = BetterTry()
2060 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002061 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002062
2063 def test_utc_offset_out_of_bounds(self):
2064 class Edgy(tzinfo):
2065 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002066 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002067 def utcoffset(self, dt):
2068 return self.offset
2069
2070 cls = self.theclass
2071 for offset, legit in ((-1440, False),
2072 (-1439, True),
2073 (1439, True),
2074 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002075 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002076 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002077 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002078 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002079 else:
2080 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002081 if legit:
2082 aofs = abs(offset)
2083 h, m = divmod(aofs, 60)
2084 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002085 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002086 t = t.timetz()
2087 self.assertEqual(str(t), "01:02:03" + tag)
2088 else:
2089 self.assertRaises(ValueError, str, t)
2090
2091 def test_tzinfo_classes(self):
2092 cls = self.theclass
2093 class C1(tzinfo):
2094 def utcoffset(self, dt): return None
2095 def dst(self, dt): return None
2096 def tzname(self, dt): return None
2097 for t in (cls(1, 1, 1),
2098 cls(1, 1, 1, tzinfo=None),
2099 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002100 self.assertTrue(t.utcoffset() is None)
2101 self.assertTrue(t.dst() is None)
2102 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002103
Tim Peters855fe882002-12-22 03:43:39 +00002104 class C3(tzinfo):
2105 def utcoffset(self, dt): return timedelta(minutes=-1439)
2106 def dst(self, dt): return timedelta(minutes=1439)
2107 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002108 t = cls(1, 1, 1, tzinfo=C3())
2109 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2110 self.assertEqual(t.dst(), timedelta(minutes=1439))
2111 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002112
2113 # Wrong types.
2114 class C4(tzinfo):
2115 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002116 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002117 def tzname(self, dt): return 0
2118 t = cls(1, 1, 1, tzinfo=C4())
2119 self.assertRaises(TypeError, t.utcoffset)
2120 self.assertRaises(TypeError, t.dst)
2121 self.assertRaises(TypeError, t.tzname)
2122
2123 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002124 class C6(tzinfo):
2125 def utcoffset(self, dt): return timedelta(hours=-24)
2126 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002127 t = cls(1, 1, 1, tzinfo=C6())
2128 self.assertRaises(ValueError, t.utcoffset)
2129 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002130
2131 # Not a whole number of minutes.
2132 class C7(tzinfo):
2133 def utcoffset(self, dt): return timedelta(seconds=61)
2134 def dst(self, dt): return timedelta(microseconds=-81)
2135 t = cls(1, 1, 1, tzinfo=C7())
2136 self.assertRaises(ValueError, t.utcoffset)
2137 self.assertRaises(ValueError, t.dst)
2138
Tim Peters4c0db782002-12-26 05:01:19 +00002139 def test_aware_compare(self):
2140 cls = self.theclass
2141
Tim Peters60c76e42002-12-27 00:41:11 +00002142 # Ensure that utcoffset() gets ignored if the comparands have
2143 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002144 class OperandDependentOffset(tzinfo):
2145 def utcoffset(self, t):
2146 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002147 # d0 and d1 equal after adjustment
2148 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002149 else:
Tim Peters397301e2003-01-02 21:28:08 +00002150 # d2 off in the weeds
2151 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002152
2153 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2154 d0 = base.replace(minute=3)
2155 d1 = base.replace(minute=9)
2156 d2 = base.replace(minute=11)
2157 for x in d0, d1, d2:
2158 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002159 for op in lt, le, gt, ge, eq, ne:
2160 got = op(x, y)
2161 expected = op(x.minute, y.minute)
2162 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002163
2164 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002165 # Note that a time can't actually have an operand-depedent offset,
2166 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2167 # so skip this test for time.
2168 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002169 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2170 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2171 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2172 for x in d0, d1, d2:
2173 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002174 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002175 if (x is d0 or x is d1) and (y is d0 or y is d1):
2176 expected = 0
2177 elif x is y is d2:
2178 expected = 0
2179 elif x is d2:
2180 expected = -1
2181 else:
2182 assert y is d2
2183 expected = 1
2184 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002185
Tim Peters855fe882002-12-22 03:43:39 +00002186
Tim Peters0bf60bd2003-01-08 20:40:01 +00002187# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002188class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002189 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002190
2191 def test_empty(self):
2192 t = self.theclass()
2193 self.assertEqual(t.hour, 0)
2194 self.assertEqual(t.minute, 0)
2195 self.assertEqual(t.second, 0)
2196 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002197 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002198
Tim Peters2a799bf2002-12-16 20:18:38 +00002199 def test_zones(self):
2200 est = FixedOffset(-300, "EST", 1)
2201 utc = FixedOffset(0, "UTC", -2)
2202 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002203 t1 = time( 7, 47, tzinfo=est)
2204 t2 = time(12, 47, tzinfo=utc)
2205 t3 = time(13, 47, tzinfo=met)
2206 t4 = time(microsecond=40)
2207 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002208
2209 self.assertEqual(t1.tzinfo, est)
2210 self.assertEqual(t2.tzinfo, utc)
2211 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002212 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002213 self.assertEqual(t5.tzinfo, utc)
2214
Tim Peters855fe882002-12-22 03:43:39 +00002215 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2216 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2217 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002218 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002219 self.assertRaises(TypeError, t1.utcoffset, "no args")
2220
2221 self.assertEqual(t1.tzname(), "EST")
2222 self.assertEqual(t2.tzname(), "UTC")
2223 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002224 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002225 self.assertRaises(TypeError, t1.tzname, "no args")
2226
Tim Peters855fe882002-12-22 03:43:39 +00002227 self.assertEqual(t1.dst(), timedelta(minutes=1))
2228 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2229 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002230 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002231 self.assertRaises(TypeError, t1.dst, "no args")
2232
2233 self.assertEqual(hash(t1), hash(t2))
2234 self.assertEqual(hash(t1), hash(t3))
2235 self.assertEqual(hash(t2), hash(t3))
2236
2237 self.assertEqual(t1, t2)
2238 self.assertEqual(t1, t3)
2239 self.assertEqual(t2, t3)
2240 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2241 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2242 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2243
2244 self.assertEqual(str(t1), "07:47:00-05:00")
2245 self.assertEqual(str(t2), "12:47:00+00:00")
2246 self.assertEqual(str(t3), "13:47:00+01:00")
2247 self.assertEqual(str(t4), "00:00:00.000040")
2248 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2249
2250 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2251 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2252 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2253 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2254 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2255
Tim Peters0bf60bd2003-01-08 20:40:01 +00002256 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002257 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2258 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2259 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2260 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2261 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2262
2263 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2264 "07:47:00 %Z=EST %z=-0500")
2265 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2266 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2267
2268 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002269 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002270 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2271 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2272
Tim Petersb92bb712002-12-21 17:44:07 +00002273 # Check that an invalid tzname result raises an exception.
2274 class Badtzname(tzinfo):
2275 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002276 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002277 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2278 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002279
2280 def test_hash_edge_cases(self):
2281 # Offsets that overflow a basic time.
2282 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2283 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2284 self.assertEqual(hash(t1), hash(t2))
2285
2286 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2287 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2288 self.assertEqual(hash(t1), hash(t2))
2289
Tim Peters2a799bf2002-12-16 20:18:38 +00002290 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002291 # Try one without a tzinfo.
2292 args = 20, 59, 16, 64**2
2293 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002294 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002295 green = pickler.dumps(orig, proto)
2296 derived = unpickler.loads(green)
2297 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002298
2299 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002300 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002301 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002302 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002303 green = pickler.dumps(orig, proto)
2304 derived = unpickler.loads(green)
2305 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002306 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002307 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2308 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002309
2310 def test_more_bool(self):
2311 # Test cases with non-None tzinfo.
2312 cls = self.theclass
2313
2314 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002315 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002316
2317 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002318 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002319
2320 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002321 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002322
2323 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002324 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002325
2326 # Mostly ensuring this doesn't overflow internally.
2327 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002328 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002329
2330 # But this should yield a value error -- the utcoffset is bogus.
2331 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2332 self.assertRaises(ValueError, lambda: bool(t))
2333
2334 # Likewise.
2335 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2336 self.assertRaises(ValueError, lambda: bool(t))
2337
Tim Peters12bf3392002-12-24 05:41:27 +00002338 def test_replace(self):
2339 cls = self.theclass
2340 z100 = FixedOffset(100, "+100")
2341 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2342 args = [1, 2, 3, 4, z100]
2343 base = cls(*args)
2344 self.assertEqual(base, base.replace())
2345
2346 i = 0
2347 for name, newval in (("hour", 5),
2348 ("minute", 6),
2349 ("second", 7),
2350 ("microsecond", 8),
2351 ("tzinfo", zm200)):
2352 newargs = args[:]
2353 newargs[i] = newval
2354 expected = cls(*newargs)
2355 got = base.replace(**{name: newval})
2356 self.assertEqual(expected, got)
2357 i += 1
2358
2359 # Ensure we can get rid of a tzinfo.
2360 self.assertEqual(base.tzname(), "+100")
2361 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002362 self.assertTrue(base2.tzinfo is None)
2363 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002364
2365 # Ensure we can add one.
2366 base3 = base2.replace(tzinfo=z100)
2367 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002368 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002369
2370 # Out of bounds.
2371 base = cls(1)
2372 self.assertRaises(ValueError, base.replace, hour=24)
2373 self.assertRaises(ValueError, base.replace, minute=-1)
2374 self.assertRaises(ValueError, base.replace, second=100)
2375 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2376
Tim Peters60c76e42002-12-27 00:41:11 +00002377 def test_mixed_compare(self):
2378 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002379 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002380 self.assertEqual(t1, t2)
2381 t2 = t2.replace(tzinfo=None)
2382 self.assertEqual(t1, t2)
2383 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2384 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002385 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2386 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002387
Tim Peters0bf60bd2003-01-08 20:40:01 +00002388 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002389 class Varies(tzinfo):
2390 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002391 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002392 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002393 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002394 return self.offset
2395
2396 v = Varies()
2397 t1 = t2.replace(tzinfo=v)
2398 t2 = t2.replace(tzinfo=v)
2399 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2400 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2401 self.assertEqual(t1, t2)
2402
2403 # But if they're not identical, it isn't ignored.
2404 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002405 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002406
Tim Petersa98924a2003-05-17 05:55:19 +00002407 def test_subclass_timetz(self):
2408
2409 class C(self.theclass):
2410 theAnswer = 42
2411
2412 def __new__(cls, *args, **kws):
2413 temp = kws.copy()
2414 extra = temp.pop('extra')
2415 result = self.theclass.__new__(cls, *args, **temp)
2416 result.extra = extra
2417 return result
2418
2419 def newmeth(self, start):
2420 return start + self.hour + self.second
2421
2422 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2423
2424 dt1 = self.theclass(*args)
2425 dt2 = C(*args, **{'extra': 7})
2426
2427 self.assertEqual(dt2.__class__, C)
2428 self.assertEqual(dt2.theAnswer, 42)
2429 self.assertEqual(dt2.extra, 7)
2430 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2431 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2432
Tim Peters4c0db782002-12-26 05:01:19 +00002433
Tim Peters0bf60bd2003-01-08 20:40:01 +00002434# Testing datetime objects with a non-None tzinfo.
2435
Guido van Rossumd8faa362007-04-27 19:54:29 +00002436class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002437 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002438
2439 def test_trivial(self):
2440 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2441 self.assertEqual(dt.year, 1)
2442 self.assertEqual(dt.month, 2)
2443 self.assertEqual(dt.day, 3)
2444 self.assertEqual(dt.hour, 4)
2445 self.assertEqual(dt.minute, 5)
2446 self.assertEqual(dt.second, 6)
2447 self.assertEqual(dt.microsecond, 7)
2448 self.assertEqual(dt.tzinfo, None)
2449
2450 def test_even_more_compare(self):
2451 # The test_compare() and test_more_compare() inherited from TestDate
2452 # and TestDateTime covered non-tzinfo cases.
2453
2454 # Smallest possible after UTC adjustment.
2455 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2456 # Largest possible after UTC adjustment.
2457 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2458 tzinfo=FixedOffset(-1439, ""))
2459
2460 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002461 self.assertTrue(t1 < t2)
2462 self.assertTrue(t1 != t2)
2463 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002464
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002465 self.assertEqual(t1, t1)
2466 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002467
2468 # Equal afer adjustment.
2469 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2470 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2471 self.assertEqual(t1, t2)
2472
2473 # Change t1 not to subtract a minute, and t1 should be larger.
2474 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002475 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002476
2477 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2478 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002479 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002480
2481 # Back to the original t1, but make seconds resolve it.
2482 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2483 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002484 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002485
2486 # Likewise, but make microseconds resolve it.
2487 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2488 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002489 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002490
2491 # Make t2 naive and it should fail.
2492 t2 = self.theclass.min
2493 self.assertRaises(TypeError, lambda: t1 == t2)
2494 self.assertEqual(t2, t2)
2495
2496 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2497 class Naive(tzinfo):
2498 def utcoffset(self, dt): return None
2499 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2500 self.assertRaises(TypeError, lambda: t1 == t2)
2501 self.assertEqual(t2, t2)
2502
2503 # OTOH, it's OK to compare two of these mixing the two ways of being
2504 # naive.
2505 t1 = self.theclass(5, 6, 7)
2506 self.assertEqual(t1, t2)
2507
2508 # Try a bogus uctoffset.
2509 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002510 def utcoffset(self, dt):
2511 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002512 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2513 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002514 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002515
Tim Peters2a799bf2002-12-16 20:18:38 +00002516 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002517 # Try one without a tzinfo.
2518 args = 6, 7, 23, 20, 59, 1, 64**2
2519 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002520 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002521 green = pickler.dumps(orig, proto)
2522 derived = unpickler.loads(green)
2523 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002524
2525 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002526 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002527 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002528 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002529 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002530 green = pickler.dumps(orig, proto)
2531 derived = unpickler.loads(green)
2532 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002533 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002534 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2535 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002536
2537 def test_extreme_hashes(self):
2538 # If an attempt is made to hash these via subtracting the offset
2539 # then hashing a datetime object, OverflowError results. The
2540 # Python implementation used to blow up here.
2541 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2542 hash(t)
2543 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2544 tzinfo=FixedOffset(-1439, ""))
2545 hash(t)
2546
2547 # OTOH, an OOB offset should blow up.
2548 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2549 self.assertRaises(ValueError, hash, t)
2550
2551 def test_zones(self):
2552 est = FixedOffset(-300, "EST")
2553 utc = FixedOffset(0, "UTC")
2554 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002555 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2556 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2557 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002558 self.assertEqual(t1.tzinfo, est)
2559 self.assertEqual(t2.tzinfo, utc)
2560 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002561 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2562 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2563 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002564 self.assertEqual(t1.tzname(), "EST")
2565 self.assertEqual(t2.tzname(), "UTC")
2566 self.assertEqual(t3.tzname(), "MET")
2567 self.assertEqual(hash(t1), hash(t2))
2568 self.assertEqual(hash(t1), hash(t3))
2569 self.assertEqual(hash(t2), hash(t3))
2570 self.assertEqual(t1, t2)
2571 self.assertEqual(t1, t3)
2572 self.assertEqual(t2, t3)
2573 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2574 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2575 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002576 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002577 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2578 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2579 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2580
2581 def test_combine(self):
2582 met = FixedOffset(60, "MET")
2583 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002584 tz = time(18, 45, 3, 1234, tzinfo=met)
2585 dt = datetime.combine(d, tz)
2586 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002587 tzinfo=met))
2588
2589 def test_extract(self):
2590 met = FixedOffset(60, "MET")
2591 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2592 self.assertEqual(dt.date(), date(2002, 3, 4))
2593 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002594 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002595
2596 def test_tz_aware_arithmetic(self):
2597 import random
2598
2599 now = self.theclass.now()
2600 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002601 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002602 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002603 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002604 self.assertEqual(nowaware.timetz(), timeaware)
2605
2606 # Can't mix aware and non-aware.
2607 self.assertRaises(TypeError, lambda: now - nowaware)
2608 self.assertRaises(TypeError, lambda: nowaware - now)
2609
Tim Peters0bf60bd2003-01-08 20:40:01 +00002610 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002611 self.assertRaises(TypeError, lambda: now + nowaware)
2612 self.assertRaises(TypeError, lambda: nowaware + now)
2613 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2614
2615 # Subtracting should yield 0.
2616 self.assertEqual(now - now, timedelta(0))
2617 self.assertEqual(nowaware - nowaware, timedelta(0))
2618
2619 # Adding a delta should preserve tzinfo.
2620 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2621 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002622 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002623 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002624 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002625 self.assertEqual(nowawareplus, nowawareplus2)
2626
2627 # that - delta should be what we started with, and that - what we
2628 # started with should be delta.
2629 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002630 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002631 self.assertEqual(nowaware, diff)
2632 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2633 self.assertEqual(nowawareplus - nowaware, delta)
2634
2635 # Make up a random timezone.
2636 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002637 # Attach it to nowawareplus.
2638 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002639 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002640 # Make sure the difference takes the timezone adjustments into account.
2641 got = nowaware - nowawareplus
2642 # Expected: (nowaware base - nowaware offset) -
2643 # (nowawareplus base - nowawareplus offset) =
2644 # (nowaware base - nowawareplus base) +
2645 # (nowawareplus offset - nowaware offset) =
2646 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002647 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002648 self.assertEqual(got, expected)
2649
2650 # Try max possible difference.
2651 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2652 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2653 tzinfo=FixedOffset(-1439, "max"))
2654 maxdiff = max - min
2655 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2656 timedelta(minutes=2*1439))
2657
2658 def test_tzinfo_now(self):
2659 meth = self.theclass.now
2660 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2661 base = meth()
2662 # Try with and without naming the keyword.
2663 off42 = FixedOffset(42, "42")
2664 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002665 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002666 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002667 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002668 # Bad argument with and w/o naming the keyword.
2669 self.assertRaises(TypeError, meth, 16)
2670 self.assertRaises(TypeError, meth, tzinfo=16)
2671 # Bad keyword name.
2672 self.assertRaises(TypeError, meth, tinfo=off42)
2673 # Too many args.
2674 self.assertRaises(TypeError, meth, off42, off42)
2675
Tim Peters10cadce2003-01-23 19:58:02 +00002676 # We don't know which time zone we're in, and don't have a tzinfo
2677 # class to represent it, so seeing whether a tz argument actually
2678 # does a conversion is tricky.
2679 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2680 utc = FixedOffset(0, "utc", 0)
2681 for dummy in range(3):
2682 now = datetime.now(weirdtz)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002683 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002684 utcnow = datetime.utcnow().replace(tzinfo=utc)
2685 now2 = utcnow.astimezone(weirdtz)
2686 if abs(now - now2) < timedelta(seconds=30):
2687 break
2688 # Else the code is broken, or more than 30 seconds passed between
2689 # calls; assuming the latter, just try again.
2690 else:
2691 # Three strikes and we're out.
2692 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2693
Tim Peters2a799bf2002-12-16 20:18:38 +00002694 def test_tzinfo_fromtimestamp(self):
2695 import time
2696 meth = self.theclass.fromtimestamp
2697 ts = time.time()
2698 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2699 base = meth(ts)
2700 # Try with and without naming the keyword.
2701 off42 = FixedOffset(42, "42")
2702 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002703 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002704 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002705 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002706 # Bad argument with and w/o naming the keyword.
2707 self.assertRaises(TypeError, meth, ts, 16)
2708 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2709 # Bad keyword name.
2710 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2711 # Too many args.
2712 self.assertRaises(TypeError, meth, ts, off42, off42)
2713 # Too few args.
2714 self.assertRaises(TypeError, meth)
2715
Tim Peters2a44a8d2003-01-23 20:53:10 +00002716 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002717 timestamp = 1000000000
2718 utcdatetime = datetime.utcfromtimestamp(timestamp)
2719 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2720 # But on some flavor of Mac, it's nowhere near that. So we can't have
2721 # any idea here what time that actually is, we can only test that
2722 # relative changes match.
2723 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2724 tz = FixedOffset(utcoffset, "tz", 0)
2725 expected = utcdatetime + utcoffset
2726 got = datetime.fromtimestamp(timestamp, tz)
2727 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002728
Tim Peters2a799bf2002-12-16 20:18:38 +00002729 def test_tzinfo_utcnow(self):
2730 meth = self.theclass.utcnow
2731 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2732 base = meth()
2733 # Try with and without naming the keyword; for whatever reason,
2734 # utcnow() doesn't accept a tzinfo argument.
2735 off42 = FixedOffset(42, "42")
2736 self.assertRaises(TypeError, meth, off42)
2737 self.assertRaises(TypeError, meth, tzinfo=off42)
2738
2739 def test_tzinfo_utcfromtimestamp(self):
2740 import time
2741 meth = self.theclass.utcfromtimestamp
2742 ts = time.time()
2743 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2744 base = meth(ts)
2745 # Try with and without naming the keyword; for whatever reason,
2746 # utcfromtimestamp() doesn't accept a tzinfo argument.
2747 off42 = FixedOffset(42, "42")
2748 self.assertRaises(TypeError, meth, ts, off42)
2749 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2750
2751 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002752 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002753 # DST flag.
2754 class DST(tzinfo):
2755 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002756 if isinstance(dstvalue, int):
2757 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002758 self.dstvalue = dstvalue
2759 def dst(self, dt):
2760 return self.dstvalue
2761
2762 cls = self.theclass
2763 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2764 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2765 t = d.timetuple()
2766 self.assertEqual(1, t.tm_year)
2767 self.assertEqual(1, t.tm_mon)
2768 self.assertEqual(1, t.tm_mday)
2769 self.assertEqual(10, t.tm_hour)
2770 self.assertEqual(20, t.tm_min)
2771 self.assertEqual(30, t.tm_sec)
2772 self.assertEqual(0, t.tm_wday)
2773 self.assertEqual(1, t.tm_yday)
2774 self.assertEqual(flag, t.tm_isdst)
2775
2776 # dst() returns wrong type.
2777 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2778
2779 # dst() at the edge.
2780 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2781 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2782
2783 # dst() out of range.
2784 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2785 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2786
2787 def test_utctimetuple(self):
2788 class DST(tzinfo):
2789 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002790 if isinstance(dstvalue, int):
2791 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002792 self.dstvalue = dstvalue
2793 def dst(self, dt):
2794 return self.dstvalue
2795
2796 cls = self.theclass
2797 # This can't work: DST didn't implement utcoffset.
2798 self.assertRaises(NotImplementedError,
2799 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2800
2801 class UOFS(DST):
2802 def __init__(self, uofs, dofs=None):
2803 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002804 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002805 def utcoffset(self, dt):
2806 return self.uofs
2807
2808 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2809 # in effect for a UTC time.
2810 for dstvalue in -33, 33, 0, None:
2811 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2812 t = d.utctimetuple()
2813 self.assertEqual(d.year, t.tm_year)
2814 self.assertEqual(d.month, t.tm_mon)
2815 self.assertEqual(d.day, t.tm_mday)
2816 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2817 self.assertEqual(13, t.tm_min)
2818 self.assertEqual(d.second, t.tm_sec)
2819 self.assertEqual(d.weekday(), t.tm_wday)
2820 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2821 t.tm_yday)
2822 self.assertEqual(0, t.tm_isdst)
2823
2824 # At the edges, UTC adjustment can normalize into years out-of-range
2825 # for a datetime object. Ensure that a correct timetuple is
2826 # created anyway.
2827 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2828 # That goes back 1 minute less than a full day.
2829 t = tiny.utctimetuple()
2830 self.assertEqual(t.tm_year, MINYEAR-1)
2831 self.assertEqual(t.tm_mon, 12)
2832 self.assertEqual(t.tm_mday, 31)
2833 self.assertEqual(t.tm_hour, 0)
2834 self.assertEqual(t.tm_min, 1)
2835 self.assertEqual(t.tm_sec, 37)
2836 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2837 self.assertEqual(t.tm_isdst, 0)
2838
2839 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2840 # That goes forward 1 minute less than a full day.
2841 t = huge.utctimetuple()
2842 self.assertEqual(t.tm_year, MAXYEAR+1)
2843 self.assertEqual(t.tm_mon, 1)
2844 self.assertEqual(t.tm_mday, 1)
2845 self.assertEqual(t.tm_hour, 23)
2846 self.assertEqual(t.tm_min, 58)
2847 self.assertEqual(t.tm_sec, 37)
2848 self.assertEqual(t.tm_yday, 1)
2849 self.assertEqual(t.tm_isdst, 0)
2850
2851 def test_tzinfo_isoformat(self):
2852 zero = FixedOffset(0, "+00:00")
2853 plus = FixedOffset(220, "+03:40")
2854 minus = FixedOffset(-231, "-03:51")
2855 unknown = FixedOffset(None, "")
2856
2857 cls = self.theclass
2858 datestr = '0001-02-03'
2859 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002860 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002861 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2862 timestr = '04:05:59' + (us and '.987001' or '')
2863 ofsstr = ofs is not None and d.tzname() or ''
2864 tailstr = timestr + ofsstr
2865 iso = d.isoformat()
2866 self.assertEqual(iso, datestr + 'T' + tailstr)
2867 self.assertEqual(iso, d.isoformat('T'))
2868 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002869 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002870 self.assertEqual(str(d), datestr + ' ' + tailstr)
2871
Tim Peters12bf3392002-12-24 05:41:27 +00002872 def test_replace(self):
2873 cls = self.theclass
2874 z100 = FixedOffset(100, "+100")
2875 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2876 args = [1, 2, 3, 4, 5, 6, 7, z100]
2877 base = cls(*args)
2878 self.assertEqual(base, base.replace())
2879
2880 i = 0
2881 for name, newval in (("year", 2),
2882 ("month", 3),
2883 ("day", 4),
2884 ("hour", 5),
2885 ("minute", 6),
2886 ("second", 7),
2887 ("microsecond", 8),
2888 ("tzinfo", zm200)):
2889 newargs = args[:]
2890 newargs[i] = newval
2891 expected = cls(*newargs)
2892 got = base.replace(**{name: newval})
2893 self.assertEqual(expected, got)
2894 i += 1
2895
2896 # Ensure we can get rid of a tzinfo.
2897 self.assertEqual(base.tzname(), "+100")
2898 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002899 self.assertTrue(base2.tzinfo is None)
2900 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002901
2902 # Ensure we can add one.
2903 base3 = base2.replace(tzinfo=z100)
2904 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002905 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002906
2907 # Out of bounds.
2908 base = cls(2000, 2, 29)
2909 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002910
Tim Peters80475bb2002-12-25 07:40:55 +00002911 def test_more_astimezone(self):
2912 # The inherited test_astimezone covered some trivial and error cases.
2913 fnone = FixedOffset(None, "None")
2914 f44m = FixedOffset(44, "44")
2915 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2916
Tim Peters10cadce2003-01-23 19:58:02 +00002917 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002918 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002919 # Replacing with degenerate tzinfo raises an exception.
2920 self.assertRaises(ValueError, dt.astimezone, fnone)
2921 # Ditto with None tz.
2922 self.assertRaises(TypeError, dt.astimezone, None)
2923 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002924 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002925 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002926 self.assertEqual(x.date(), dt.date())
2927 self.assertEqual(x.time(), dt.time())
2928
2929 # Replacing with different tzinfo does adjust.
2930 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002931 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002932 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2933 expected = dt - dt.utcoffset() # in effect, convert to UTC
2934 expected += fm5h.utcoffset(dt) # and from there to local time
2935 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2936 self.assertEqual(got.date(), expected.date())
2937 self.assertEqual(got.time(), expected.time())
2938 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002939 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002940 self.assertEqual(got, expected)
2941
Tim Peters4c0db782002-12-26 05:01:19 +00002942 def test_aware_subtract(self):
2943 cls = self.theclass
2944
Tim Peters60c76e42002-12-27 00:41:11 +00002945 # Ensure that utcoffset() is ignored when the operands have the
2946 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002947 class OperandDependentOffset(tzinfo):
2948 def utcoffset(self, t):
2949 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002950 # d0 and d1 equal after adjustment
2951 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002952 else:
Tim Peters397301e2003-01-02 21:28:08 +00002953 # d2 off in the weeds
2954 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002955
2956 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2957 d0 = base.replace(minute=3)
2958 d1 = base.replace(minute=9)
2959 d2 = base.replace(minute=11)
2960 for x in d0, d1, d2:
2961 for y in d0, d1, d2:
2962 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002963 expected = timedelta(minutes=x.minute - y.minute)
2964 self.assertEqual(got, expected)
2965
2966 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2967 # ignored.
2968 base = cls(8, 9, 10, 11, 12, 13, 14)
2969 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2970 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2971 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2972 for x in d0, d1, d2:
2973 for y in d0, d1, d2:
2974 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002975 if (x is d0 or x is d1) and (y is d0 or y is d1):
2976 expected = timedelta(0)
2977 elif x is y is d2:
2978 expected = timedelta(0)
2979 elif x is d2:
2980 expected = timedelta(minutes=(11-59)-0)
2981 else:
2982 assert y is d2
2983 expected = timedelta(minutes=0-(11-59))
2984 self.assertEqual(got, expected)
2985
Tim Peters60c76e42002-12-27 00:41:11 +00002986 def test_mixed_compare(self):
2987 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002988 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002989 self.assertEqual(t1, t2)
2990 t2 = t2.replace(tzinfo=None)
2991 self.assertEqual(t1, t2)
2992 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2993 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002994 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2995 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002996
Tim Peters0bf60bd2003-01-08 20:40:01 +00002997 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002998 class Varies(tzinfo):
2999 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003000 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003001 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003002 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003003 return self.offset
3004
3005 v = Varies()
3006 t1 = t2.replace(tzinfo=v)
3007 t2 = t2.replace(tzinfo=v)
3008 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3009 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3010 self.assertEqual(t1, t2)
3011
3012 # But if they're not identical, it isn't ignored.
3013 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003014 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003015
Tim Petersa98924a2003-05-17 05:55:19 +00003016 def test_subclass_datetimetz(self):
3017
3018 class C(self.theclass):
3019 theAnswer = 42
3020
3021 def __new__(cls, *args, **kws):
3022 temp = kws.copy()
3023 extra = temp.pop('extra')
3024 result = self.theclass.__new__(cls, *args, **temp)
3025 result.extra = extra
3026 return result
3027
3028 def newmeth(self, start):
3029 return start + self.hour + self.year
3030
3031 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3032
3033 dt1 = self.theclass(*args)
3034 dt2 = C(*args, **{'extra': 7})
3035
3036 self.assertEqual(dt2.__class__, C)
3037 self.assertEqual(dt2.theAnswer, 42)
3038 self.assertEqual(dt2.extra, 7)
3039 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3040 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3041
Tim Peters621818b2002-12-29 23:44:49 +00003042# Pain to set up DST-aware tzinfo classes.
3043
3044def first_sunday_on_or_after(dt):
3045 days_to_go = 6 - dt.weekday()
3046 if days_to_go:
3047 dt += timedelta(days_to_go)
3048 return dt
3049
3050ZERO = timedelta(0)
3051HOUR = timedelta(hours=1)
3052DAY = timedelta(days=1)
3053# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3054DSTSTART = datetime(1, 4, 1, 2)
3055# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003056# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3057# being standard time on that day, there is no spelling in local time of
3058# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3059DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003060
3061class USTimeZone(tzinfo):
3062
3063 def __init__(self, hours, reprname, stdname, dstname):
3064 self.stdoffset = timedelta(hours=hours)
3065 self.reprname = reprname
3066 self.stdname = stdname
3067 self.dstname = dstname
3068
3069 def __repr__(self):
3070 return self.reprname
3071
3072 def tzname(self, dt):
3073 if self.dst(dt):
3074 return self.dstname
3075 else:
3076 return self.stdname
3077
3078 def utcoffset(self, dt):
3079 return self.stdoffset + self.dst(dt)
3080
3081 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003082 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003083 # An exception instead may be sensible here, in one or more of
3084 # the cases.
3085 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003086 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003087
3088 # Find first Sunday in April.
3089 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3090 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3091
3092 # Find last Sunday in October.
3093 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3094 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3095
Tim Peters621818b2002-12-29 23:44:49 +00003096 # Can't compare naive to aware objects, so strip the timezone from
3097 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003098 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003099 return HOUR
3100 else:
3101 return ZERO
3102
Tim Peters521fc152002-12-31 17:36:56 +00003103Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3104Central = USTimeZone(-6, "Central", "CST", "CDT")
3105Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3106Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003107utc_real = FixedOffset(0, "UTC", 0)
3108# For better test coverage, we want another flavor of UTC that's west of
3109# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003110utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003111
3112class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003113 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003114 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003115 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003116
Tim Peters0bf60bd2003-01-08 20:40:01 +00003117 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003118
Tim Peters521fc152002-12-31 17:36:56 +00003119 # Check a time that's inside DST.
3120 def checkinside(self, dt, tz, utc, dston, dstoff):
3121 self.assertEqual(dt.dst(), HOUR)
3122
3123 # Conversion to our own timezone is always an identity.
3124 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003125
3126 asutc = dt.astimezone(utc)
3127 there_and_back = asutc.astimezone(tz)
3128
3129 # Conversion to UTC and back isn't always an identity here,
3130 # because there are redundant spellings (in local time) of
3131 # UTC time when DST begins: the clock jumps from 1:59:59
3132 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3133 # make sense then. The classes above treat 2:MM:SS as
3134 # daylight time then (it's "after 2am"), really an alias
3135 # for 1:MM:SS standard time. The latter form is what
3136 # conversion back from UTC produces.
3137 if dt.date() == dston.date() and dt.hour == 2:
3138 # We're in the redundant hour, and coming back from
3139 # UTC gives the 1:MM:SS standard-time spelling.
3140 self.assertEqual(there_and_back + HOUR, dt)
3141 # Although during was considered to be in daylight
3142 # time, there_and_back is not.
3143 self.assertEqual(there_and_back.dst(), ZERO)
3144 # They're the same times in UTC.
3145 self.assertEqual(there_and_back.astimezone(utc),
3146 dt.astimezone(utc))
3147 else:
3148 # We're not in the redundant hour.
3149 self.assertEqual(dt, there_and_back)
3150
Tim Peters327098a2003-01-20 22:54:38 +00003151 # Because we have a redundant spelling when DST begins, there is
3152 # (unforunately) an hour when DST ends that can't be spelled at all in
3153 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3154 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3155 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3156 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3157 # expressed in local time. Nevertheless, we want conversion back
3158 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003159 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003160 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003161 if dt.date() == dstoff.date() and dt.hour == 0:
3162 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003163 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003164 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3165 nexthour_utc += HOUR
3166 nexthour_tz = nexthour_utc.astimezone(tz)
3167 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003168 else:
Tim Peters327098a2003-01-20 22:54:38 +00003169 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003170
3171 # Check a time that's outside DST.
3172 def checkoutside(self, dt, tz, utc):
3173 self.assertEqual(dt.dst(), ZERO)
3174
3175 # Conversion to our own timezone is always an identity.
3176 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003177
3178 # Converting to UTC and back is an identity too.
3179 asutc = dt.astimezone(utc)
3180 there_and_back = asutc.astimezone(tz)
3181 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003182
Tim Peters1024bf82002-12-30 17:09:40 +00003183 def convert_between_tz_and_utc(self, tz, utc):
3184 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003185 # Because 1:MM on the day DST ends is taken as being standard time,
3186 # there is no spelling in tz for the last hour of daylight time.
3187 # For purposes of the test, the last hour of DST is 0:MM, which is
3188 # taken as being daylight time (and 1:MM is taken as being standard
3189 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003190 dstoff = self.dstoff.replace(tzinfo=tz)
3191 for delta in (timedelta(weeks=13),
3192 DAY,
3193 HOUR,
3194 timedelta(minutes=1),
3195 timedelta(microseconds=1)):
3196
Tim Peters521fc152002-12-31 17:36:56 +00003197 self.checkinside(dston, tz, utc, dston, dstoff)
3198 for during in dston + delta, dstoff - delta:
3199 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003200
Tim Peters521fc152002-12-31 17:36:56 +00003201 self.checkoutside(dstoff, tz, utc)
3202 for outside in dston - delta, dstoff + delta:
3203 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003204
Tim Peters621818b2002-12-29 23:44:49 +00003205 def test_easy(self):
3206 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003207 self.convert_between_tz_and_utc(Eastern, utc_real)
3208 self.convert_between_tz_and_utc(Pacific, utc_real)
3209 self.convert_between_tz_and_utc(Eastern, utc_fake)
3210 self.convert_between_tz_and_utc(Pacific, utc_fake)
3211 # The next is really dancing near the edge. It works because
3212 # Pacific and Eastern are far enough apart that their "problem
3213 # hours" don't overlap.
3214 self.convert_between_tz_and_utc(Eastern, Pacific)
3215 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003216 # OTOH, these fail! Don't enable them. The difficulty is that
3217 # the edge case tests assume that every hour is representable in
3218 # the "utc" class. This is always true for a fixed-offset tzinfo
3219 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3220 # For these adjacent DST-aware time zones, the range of time offsets
3221 # tested ends up creating hours in the one that aren't representable
3222 # in the other. For the same reason, we would see failures in the
3223 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3224 # offset deltas in convert_between_tz_and_utc().
3225 #
3226 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3227 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003228
Tim Petersf3615152003-01-01 21:51:37 +00003229 def test_tricky(self):
3230 # 22:00 on day before daylight starts.
3231 fourback = self.dston - timedelta(hours=4)
3232 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003233 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003234 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3235 # 2", we should get the 3 spelling.
3236 # If we plug 22:00 the day before into Eastern, it "looks like std
3237 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3238 # to 22:00 lands on 2:00, which makes no sense in local time (the
3239 # local clock jumps from 1 to 3). The point here is to make sure we
3240 # get the 3 spelling.
3241 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003242 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003243 self.assertEqual(expected, got)
3244
3245 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3246 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003247 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003248 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3249 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3250 # spelling.
3251 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003252 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003253 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003254
Tim Petersadf64202003-01-04 06:03:15 +00003255 # Now on the day DST ends, we want "repeat an hour" behavior.
3256 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3257 # EST 23:MM 0:MM 1:MM 2:MM
3258 # EDT 0:MM 1:MM 2:MM 3:MM
3259 # wall 0:MM 1:MM 1:MM 2:MM against these
3260 for utc in utc_real, utc_fake:
3261 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003262 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003263 # Convert that to UTC.
3264 first_std_hour -= tz.utcoffset(None)
3265 # Adjust for possibly fake UTC.
3266 asutc = first_std_hour + utc.utcoffset(None)
3267 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3268 # tz=Eastern.
3269 asutcbase = asutc.replace(tzinfo=utc)
3270 for tzhour in (0, 1, 1, 2):
3271 expectedbase = self.dstoff.replace(hour=tzhour)
3272 for minute in 0, 30, 59:
3273 expected = expectedbase.replace(minute=minute)
3274 asutc = asutcbase.replace(minute=minute)
3275 astz = asutc.astimezone(tz)
3276 self.assertEqual(astz.replace(tzinfo=None), expected)
3277 asutcbase += HOUR
3278
3279
Tim Peters710fb152003-01-02 19:35:54 +00003280 def test_bogus_dst(self):
3281 class ok(tzinfo):
3282 def utcoffset(self, dt): return HOUR
3283 def dst(self, dt): return HOUR
3284
3285 now = self.theclass.now().replace(tzinfo=utc_real)
3286 # Doesn't blow up.
3287 now.astimezone(ok())
3288
3289 # Does blow up.
3290 class notok(ok):
3291 def dst(self, dt): return None
3292 self.assertRaises(ValueError, now.astimezone, notok())
3293
Tim Peters52dcce22003-01-23 16:36:11 +00003294 def test_fromutc(self):
3295 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3296 now = datetime.utcnow().replace(tzinfo=utc_real)
3297 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3298 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3299 enow = Eastern.fromutc(now) # doesn't blow up
3300 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3301 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3302 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3303
3304 # Always converts UTC to standard time.
3305 class FauxUSTimeZone(USTimeZone):
3306 def fromutc(self, dt):
3307 return dt + self.stdoffset
3308 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3309
3310 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3311 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3312 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3313
3314 # Check around DST start.
3315 start = self.dston.replace(hour=4, tzinfo=Eastern)
3316 fstart = start.replace(tzinfo=FEastern)
3317 for wall in 23, 0, 1, 3, 4, 5:
3318 expected = start.replace(hour=wall)
3319 if wall == 23:
3320 expected -= timedelta(days=1)
3321 got = Eastern.fromutc(start)
3322 self.assertEqual(expected, got)
3323
3324 expected = fstart + FEastern.stdoffset
3325 got = FEastern.fromutc(fstart)
3326 self.assertEqual(expected, got)
3327
3328 # Ensure astimezone() calls fromutc() too.
3329 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3330 self.assertEqual(expected, got)
3331
3332 start += HOUR
3333 fstart += HOUR
3334
3335 # Check around DST end.
3336 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3337 fstart = start.replace(tzinfo=FEastern)
3338 for wall in 0, 1, 1, 2, 3, 4:
3339 expected = start.replace(hour=wall)
3340 got = Eastern.fromutc(start)
3341 self.assertEqual(expected, got)
3342
3343 expected = fstart + FEastern.stdoffset
3344 got = FEastern.fromutc(fstart)
3345 self.assertEqual(expected, got)
3346
3347 # Ensure astimezone() calls fromutc() too.
3348 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3349 self.assertEqual(expected, got)
3350
3351 start += HOUR
3352 fstart += HOUR
3353
Tim Peters710fb152003-01-02 19:35:54 +00003354
Tim Peters528ca532004-09-16 01:30:50 +00003355#############################################################################
3356# oddballs
3357
3358class Oddballs(unittest.TestCase):
3359
3360 def test_bug_1028306(self):
3361 # Trying to compare a date to a datetime should act like a mixed-
3362 # type comparison, despite that datetime is a subclass of date.
3363 as_date = date.today()
3364 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003365 self.assertTrue(as_date != as_datetime)
3366 self.assertTrue(as_datetime != as_date)
3367 self.assertTrue(not as_date == as_datetime)
3368 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003369 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3370 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3371 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3372 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3373 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3374 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3375 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3376 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3377
3378 # Neverthelss, comparison should work with the base-class (date)
3379 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003380 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003381 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003382 as_different = as_datetime.replace(day= different_day)
3383 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003384
3385 # And date should compare with other subclasses of date. If a
3386 # subclass wants to stop this, it's up to the subclass to do so.
3387 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3388 self.assertEqual(as_date, date_sc)
3389 self.assertEqual(date_sc, as_date)
3390
3391 # Ditto for datetimes.
3392 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3393 as_date.day, 0, 0, 0)
3394 self.assertEqual(as_datetime, datetime_sc)
3395 self.assertEqual(datetime_sc, as_datetime)
3396
Tim Peters2a799bf2002-12-16 20:18:38 +00003397def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003398 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003399
3400if __name__ == "__main__":
3401 test_main()