blob: f65fbca3751d117bf69736fa47ce14eb4273b4d4 [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)
Mark Dickinson0381e3f2010-05-08 14:35:02 +0000267 # Issue8644: Test that td.total_seconds() has the same
268 # accuracy as td / timedelta(seconds=1).
269 for ms in [-1, -2, -123]:
270 td = timedelta(microseconds=ms)
271 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
Antoine Pitroube6859d2009-11-25 23:02:32 +0000272
Tim Peters2a799bf2002-12-16 20:18:38 +0000273 def test_carries(self):
274 t1 = timedelta(days=100,
275 weeks=-7,
276 hours=-24*(100-49),
277 minutes=-3,
278 seconds=12,
279 microseconds=(3*60 - 12) * 1e6 + 1)
280 t2 = timedelta(microseconds=1)
281 self.assertEqual(t1, t2)
282
283 def test_hash_equality(self):
284 t1 = timedelta(days=100,
285 weeks=-7,
286 hours=-24*(100-49),
287 minutes=-3,
288 seconds=12,
289 microseconds=(3*60 - 12) * 1000000)
290 t2 = timedelta()
291 self.assertEqual(hash(t1), hash(t2))
292
293 t1 += timedelta(weeks=7)
294 t2 += timedelta(days=7*7)
295 self.assertEqual(t1, t2)
296 self.assertEqual(hash(t1), hash(t2))
297
298 d = {t1: 1}
299 d[t2] = 2
300 self.assertEqual(len(d), 1)
301 self.assertEqual(d[t1], 2)
302
303 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000304 args = 12, 34, 56
305 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000306 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000307 green = pickler.dumps(orig, proto)
308 derived = unpickler.loads(green)
309 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000310
311 def test_compare(self):
312 t1 = timedelta(2, 3, 4)
313 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000314 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000315 self.assertTrue(t1 <= t2)
316 self.assertTrue(t1 >= t2)
317 self.assertTrue(not t1 != t2)
318 self.assertTrue(not t1 < t2)
319 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000320
321 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
322 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000323 self.assertTrue(t1 < t2)
324 self.assertTrue(t2 > t1)
325 self.assertTrue(t1 <= t2)
326 self.assertTrue(t2 >= t1)
327 self.assertTrue(t1 != t2)
328 self.assertTrue(t2 != t1)
329 self.assertTrue(not t1 == t2)
330 self.assertTrue(not t2 == t1)
331 self.assertTrue(not t1 > t2)
332 self.assertTrue(not t2 < t1)
333 self.assertTrue(not t1 >= t2)
334 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000335
Tim Peters68124bb2003-02-08 03:46:31 +0000336 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000337 self.assertEqual(t1 == badarg, False)
338 self.assertEqual(t1 != badarg, True)
339 self.assertEqual(badarg == t1, False)
340 self.assertEqual(badarg != t1, True)
341
Tim Peters2a799bf2002-12-16 20:18:38 +0000342 self.assertRaises(TypeError, lambda: t1 <= badarg)
343 self.assertRaises(TypeError, lambda: t1 < badarg)
344 self.assertRaises(TypeError, lambda: t1 > badarg)
345 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000346 self.assertRaises(TypeError, lambda: badarg <= t1)
347 self.assertRaises(TypeError, lambda: badarg < t1)
348 self.assertRaises(TypeError, lambda: badarg > t1)
349 self.assertRaises(TypeError, lambda: badarg >= t1)
350
351 def test_str(self):
352 td = timedelta
353 eq = self.assertEqual
354
355 eq(str(td(1)), "1 day, 0:00:00")
356 eq(str(td(-1)), "-1 day, 0:00:00")
357 eq(str(td(2)), "2 days, 0:00:00")
358 eq(str(td(-2)), "-2 days, 0:00:00")
359
360 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
361 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
362 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
363 "-210 days, 23:12:34")
364
365 eq(str(td(milliseconds=1)), "0:00:00.001000")
366 eq(str(td(microseconds=3)), "0:00:00.000003")
367
368 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
369 microseconds=999999)),
370 "999999999 days, 23:59:59.999999")
371
372 def test_roundtrip(self):
373 for td in (timedelta(days=999999999, hours=23, minutes=59,
374 seconds=59, microseconds=999999),
375 timedelta(days=-999999999),
376 timedelta(days=1, seconds=2, microseconds=3)):
377
378 # Verify td -> string -> td identity.
379 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000380 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000381 s = s[9:]
382 td2 = eval(s)
383 self.assertEqual(td, td2)
384
385 # Verify identity via reconstructing from pieces.
386 td2 = timedelta(td.days, td.seconds, td.microseconds)
387 self.assertEqual(td, td2)
388
389 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000390 self.assertIsInstance(timedelta.min, timedelta)
391 self.assertIsInstance(timedelta.max, timedelta)
392 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000393 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000394 self.assertEqual(timedelta.min, timedelta(-999999999))
395 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
396 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
397
398 def test_overflow(self):
399 tiny = timedelta.resolution
400
401 td = timedelta.min + tiny
402 td -= tiny # no problem
403 self.assertRaises(OverflowError, td.__sub__, tiny)
404 self.assertRaises(OverflowError, td.__add__, -tiny)
405
406 td = timedelta.max - tiny
407 td += tiny # no problem
408 self.assertRaises(OverflowError, td.__add__, tiny)
409 self.assertRaises(OverflowError, td.__sub__, -tiny)
410
411 self.assertRaises(OverflowError, lambda: -timedelta.max)
412
413 def test_microsecond_rounding(self):
414 td = timedelta
415 eq = self.assertEqual
416
417 # Single-field rounding.
418 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
419 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
420 eq(td(milliseconds=0.6/1000), td(microseconds=1))
421 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
422
423 # Rounding due to contributions from more than one field.
424 us_per_hour = 3600e6
425 us_per_day = us_per_hour * 24
426 eq(td(days=.4/us_per_day), td(0))
427 eq(td(hours=.2/us_per_hour), td(0))
428 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
429
430 eq(td(days=-.4/us_per_day), td(0))
431 eq(td(hours=-.2/us_per_hour), td(0))
432 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
433
434 def test_massive_normalization(self):
435 td = timedelta(microseconds=-1)
436 self.assertEqual((td.days, td.seconds, td.microseconds),
437 (-1, 24*3600-1, 999999))
438
439 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000440 self.assertTrue(timedelta(1))
441 self.assertTrue(timedelta(0, 1))
442 self.assertTrue(timedelta(0, 0, 1))
443 self.assertTrue(timedelta(microseconds=1))
444 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000445
Tim Petersb0c854d2003-05-17 15:57:00 +0000446 def test_subclass_timedelta(self):
447
448 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000449 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000450 def from_td(td):
451 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000452
453 def as_hours(self):
454 sum = (self.days * 24 +
455 self.seconds / 3600.0 +
456 self.microseconds / 3600e6)
457 return round(sum)
458
459 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000460 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000461 self.assertEqual(t1.as_hours(), 24)
462
463 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000464 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000465 self.assertEqual(t2.as_hours(), -25)
466
467 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000468 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000469 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000470 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000471 self.assertEqual(t3.days, t4.days)
472 self.assertEqual(t3.seconds, t4.seconds)
473 self.assertEqual(t3.microseconds, t4.microseconds)
474 self.assertEqual(str(t3), str(t4))
475 self.assertEqual(t4.as_hours(), -1)
476
Mark Dickinson7c186e22010-04-20 22:32:49 +0000477 def test_division(self):
478 t = timedelta(hours=1, minutes=24, seconds=19)
479 second = timedelta(seconds=1)
480 self.assertEqual(t / second, 5059.0)
481 self.assertEqual(t // second, 5059)
482
483 t = timedelta(minutes=2, seconds=30)
484 minute = timedelta(minutes=1)
485 self.assertEqual(t / minute, 2.5)
486 self.assertEqual(t // minute, 2)
487
488 zerotd = timedelta(0)
489 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
490 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
491
492 self.assertRaises(TypeError, truediv, t, 2)
493 # note: floor division of a timedelta by an integer *is*
494 # currently permitted.
495
496 def test_remainder(self):
497 t = timedelta(minutes=2, seconds=30)
498 minute = timedelta(minutes=1)
499 r = t % minute
500 self.assertEqual(r, timedelta(seconds=30))
501
502 t = timedelta(minutes=-2, seconds=30)
503 r = t % minute
504 self.assertEqual(r, timedelta(seconds=30))
505
506 zerotd = timedelta(0)
507 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
508
509 self.assertRaises(TypeError, mod, t, 10)
510
511 def test_divmod(self):
512 t = timedelta(minutes=2, seconds=30)
513 minute = timedelta(minutes=1)
514 q, r = divmod(t, minute)
515 self.assertEqual(q, 2)
516 self.assertEqual(r, timedelta(seconds=30))
517
518 t = timedelta(minutes=-2, seconds=30)
519 q, r = divmod(t, minute)
520 self.assertEqual(q, -2)
521 self.assertEqual(r, timedelta(seconds=30))
522
523 zerotd = timedelta(0)
524 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
525
526 self.assertRaises(TypeError, divmod, t, 10)
527
528
Tim Peters2a799bf2002-12-16 20:18:38 +0000529#############################################################################
530# date tests
531
532class TestDateOnly(unittest.TestCase):
533 # Tests here won't pass if also run on datetime objects, so don't
534 # subclass this to test datetimes too.
535
536 def test_delta_non_days_ignored(self):
537 dt = date(2000, 1, 2)
538 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
539 microseconds=5)
540 days = timedelta(delta.days)
541 self.assertEqual(days, timedelta(1))
542
543 dt2 = dt + delta
544 self.assertEqual(dt2, dt + days)
545
546 dt2 = delta + dt
547 self.assertEqual(dt2, dt + days)
548
549 dt2 = dt - delta
550 self.assertEqual(dt2, dt - days)
551
552 delta = -delta
553 days = timedelta(delta.days)
554 self.assertEqual(days, timedelta(-2))
555
556 dt2 = dt + delta
557 self.assertEqual(dt2, dt + days)
558
559 dt2 = delta + dt
560 self.assertEqual(dt2, dt + days)
561
562 dt2 = dt - delta
563 self.assertEqual(dt2, dt - days)
564
Tim Peters604c0132004-06-07 23:04:33 +0000565class SubclassDate(date):
566 sub_var = 1
567
Guido van Rossumd8faa362007-04-27 19:54:29 +0000568class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000569 # Tests here should pass for both dates and datetimes, except for a
570 # few tests that TestDateTime overrides.
571
572 theclass = date
573
574 def test_basic_attributes(self):
575 dt = self.theclass(2002, 3, 1)
576 self.assertEqual(dt.year, 2002)
577 self.assertEqual(dt.month, 3)
578 self.assertEqual(dt.day, 1)
579
580 def test_roundtrip(self):
581 for dt in (self.theclass(1, 2, 3),
582 self.theclass.today()):
583 # Verify dt -> string -> date identity.
584 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000585 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000586 s = s[9:]
587 dt2 = eval(s)
588 self.assertEqual(dt, dt2)
589
590 # Verify identity via reconstructing from pieces.
591 dt2 = self.theclass(dt.year, dt.month, dt.day)
592 self.assertEqual(dt, dt2)
593
594 def test_ordinal_conversions(self):
595 # Check some fixed values.
596 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
597 (1, 12, 31, 365),
598 (2, 1, 1, 366),
599 # first example from "Calendrical Calculations"
600 (1945, 11, 12, 710347)]:
601 d = self.theclass(y, m, d)
602 self.assertEqual(n, d.toordinal())
603 fromord = self.theclass.fromordinal(n)
604 self.assertEqual(d, fromord)
605 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000606 # if we're checking something fancier than a date, verify
607 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000608 self.assertEqual(fromord.hour, 0)
609 self.assertEqual(fromord.minute, 0)
610 self.assertEqual(fromord.second, 0)
611 self.assertEqual(fromord.microsecond, 0)
612
Tim Peters0bf60bd2003-01-08 20:40:01 +0000613 # Check first and last days of year spottily across the whole
614 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000615 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000616 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
617 d = self.theclass(year, 1, 1)
618 n = d.toordinal()
619 d2 = self.theclass.fromordinal(n)
620 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000621 # Verify that moving back a day gets to the end of year-1.
622 if year > 1:
623 d = self.theclass.fromordinal(n-1)
624 d2 = self.theclass(year-1, 12, 31)
625 self.assertEqual(d, d2)
626 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000627
628 # Test every day in a leap-year and a non-leap year.
629 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
630 for year, isleap in (2000, True), (2002, False):
631 n = self.theclass(year, 1, 1).toordinal()
632 for month, maxday in zip(range(1, 13), dim):
633 if month == 2 and isleap:
634 maxday += 1
635 for day in range(1, maxday+1):
636 d = self.theclass(year, month, day)
637 self.assertEqual(d.toordinal(), n)
638 self.assertEqual(d, self.theclass.fromordinal(n))
639 n += 1
640
641 def test_extreme_ordinals(self):
642 a = self.theclass.min
643 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
644 aord = a.toordinal()
645 b = a.fromordinal(aord)
646 self.assertEqual(a, b)
647
648 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
649
650 b = a + timedelta(days=1)
651 self.assertEqual(b.toordinal(), aord + 1)
652 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
653
654 a = self.theclass.max
655 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
656 aord = a.toordinal()
657 b = a.fromordinal(aord)
658 self.assertEqual(a, b)
659
660 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
661
662 b = a - timedelta(days=1)
663 self.assertEqual(b.toordinal(), aord - 1)
664 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
665
666 def test_bad_constructor_arguments(self):
667 # bad years
668 self.theclass(MINYEAR, 1, 1) # no exception
669 self.theclass(MAXYEAR, 1, 1) # no exception
670 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
671 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
672 # bad months
673 self.theclass(2000, 1, 1) # no exception
674 self.theclass(2000, 12, 1) # no exception
675 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
676 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
677 # bad days
678 self.theclass(2000, 2, 29) # no exception
679 self.theclass(2004, 2, 29) # no exception
680 self.theclass(2400, 2, 29) # no exception
681 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
682 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
683 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
684 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
685 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
686 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
687
688 def test_hash_equality(self):
689 d = self.theclass(2000, 12, 31)
690 # same thing
691 e = self.theclass(2000, 12, 31)
692 self.assertEqual(d, e)
693 self.assertEqual(hash(d), hash(e))
694
695 dic = {d: 1}
696 dic[e] = 2
697 self.assertEqual(len(dic), 1)
698 self.assertEqual(dic[d], 2)
699 self.assertEqual(dic[e], 2)
700
701 d = self.theclass(2001, 1, 1)
702 # same thing
703 e = self.theclass(2001, 1, 1)
704 self.assertEqual(d, e)
705 self.assertEqual(hash(d), hash(e))
706
707 dic = {d: 1}
708 dic[e] = 2
709 self.assertEqual(len(dic), 1)
710 self.assertEqual(dic[d], 2)
711 self.assertEqual(dic[e], 2)
712
713 def test_computations(self):
714 a = self.theclass(2002, 1, 31)
715 b = self.theclass(1956, 1, 31)
716
717 diff = a-b
718 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
719 self.assertEqual(diff.seconds, 0)
720 self.assertEqual(diff.microseconds, 0)
721
722 day = timedelta(1)
723 week = timedelta(7)
724 a = self.theclass(2002, 3, 2)
725 self.assertEqual(a + day, self.theclass(2002, 3, 3))
726 self.assertEqual(day + a, self.theclass(2002, 3, 3))
727 self.assertEqual(a - day, self.theclass(2002, 3, 1))
728 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
729 self.assertEqual(a + week, self.theclass(2002, 3, 9))
730 self.assertEqual(a - week, self.theclass(2002, 2, 23))
731 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
732 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
733 self.assertEqual((a + week) - a, week)
734 self.assertEqual((a + day) - a, day)
735 self.assertEqual((a - week) - a, -week)
736 self.assertEqual((a - day) - a, -day)
737 self.assertEqual(a - (a + week), -week)
738 self.assertEqual(a - (a + day), -day)
739 self.assertEqual(a - (a - week), week)
740 self.assertEqual(a - (a - day), day)
741
Mark Dickinson5c2db372009-12-05 20:28:34 +0000742 # Add/sub ints or floats should be illegal
743 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000744 self.assertRaises(TypeError, lambda: a+i)
745 self.assertRaises(TypeError, lambda: a-i)
746 self.assertRaises(TypeError, lambda: i+a)
747 self.assertRaises(TypeError, lambda: i-a)
748
749 # delta - date is senseless.
750 self.assertRaises(TypeError, lambda: day - a)
751 # mixing date and (delta or date) via * or // is senseless
752 self.assertRaises(TypeError, lambda: day * a)
753 self.assertRaises(TypeError, lambda: a * day)
754 self.assertRaises(TypeError, lambda: day // a)
755 self.assertRaises(TypeError, lambda: a // day)
756 self.assertRaises(TypeError, lambda: a * a)
757 self.assertRaises(TypeError, lambda: a // a)
758 # date + date is senseless
759 self.assertRaises(TypeError, lambda: a + a)
760
761 def test_overflow(self):
762 tiny = self.theclass.resolution
763
764 dt = self.theclass.min + tiny
765 dt -= tiny # no problem
766 self.assertRaises(OverflowError, dt.__sub__, tiny)
767 self.assertRaises(OverflowError, dt.__add__, -tiny)
768
769 dt = self.theclass.max - tiny
770 dt += tiny # no problem
771 self.assertRaises(OverflowError, dt.__add__, tiny)
772 self.assertRaises(OverflowError, dt.__sub__, -tiny)
773
774 def test_fromtimestamp(self):
775 import time
776
777 # Try an arbitrary fixed value.
778 year, month, day = 1999, 9, 19
779 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
780 d = self.theclass.fromtimestamp(ts)
781 self.assertEqual(d.year, year)
782 self.assertEqual(d.month, month)
783 self.assertEqual(d.day, day)
784
Tim Peters1b6f7a92004-06-20 02:50:16 +0000785 def test_insane_fromtimestamp(self):
786 # It's possible that some platform maps time_t to double,
787 # and that this test will fail there. This test should
788 # exempt such platforms (provided they return reasonable
789 # results!).
790 for insane in -1e200, 1e200:
791 self.assertRaises(ValueError, self.theclass.fromtimestamp,
792 insane)
793
Tim Peters2a799bf2002-12-16 20:18:38 +0000794 def test_today(self):
795 import time
796
797 # We claim that today() is like fromtimestamp(time.time()), so
798 # prove it.
799 for dummy in range(3):
800 today = self.theclass.today()
801 ts = time.time()
802 todayagain = self.theclass.fromtimestamp(ts)
803 if today == todayagain:
804 break
805 # There are several legit reasons that could fail:
806 # 1. It recently became midnight, between the today() and the
807 # time() calls.
808 # 2. The platform time() has such fine resolution that we'll
809 # never get the same value twice.
810 # 3. The platform time() has poor resolution, and we just
811 # happened to call today() right before a resolution quantum
812 # boundary.
813 # 4. The system clock got fiddled between calls.
814 # In any case, wait a little while and try again.
815 time.sleep(0.1)
816
817 # It worked or it didn't. If it didn't, assume it's reason #2, and
818 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000819 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000820 abs(todayagain - today) < timedelta(seconds=0.5))
821
822 def test_weekday(self):
823 for i in range(7):
824 # March 4, 2002 is a Monday
825 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
826 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
827 # January 2, 1956 is a Monday
828 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
829 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
830
831 def test_isocalendar(self):
832 # Check examples from
833 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
834 for i in range(7):
835 d = self.theclass(2003, 12, 22+i)
836 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
837 d = self.theclass(2003, 12, 29) + timedelta(i)
838 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
839 d = self.theclass(2004, 1, 5+i)
840 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
841 d = self.theclass(2009, 12, 21+i)
842 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
843 d = self.theclass(2009, 12, 28) + timedelta(i)
844 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
845 d = self.theclass(2010, 1, 4+i)
846 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
847
848 def test_iso_long_years(self):
849 # Calculate long ISO years and compare to table from
850 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
851 ISO_LONG_YEARS_TABLE = """
852 4 32 60 88
853 9 37 65 93
854 15 43 71 99
855 20 48 76
856 26 54 82
857
858 105 133 161 189
859 111 139 167 195
860 116 144 172
861 122 150 178
862 128 156 184
863
864 201 229 257 285
865 207 235 263 291
866 212 240 268 296
867 218 246 274
868 224 252 280
869
870 303 331 359 387
871 308 336 364 392
872 314 342 370 398
873 320 348 376
874 325 353 381
875 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000876 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000877 L = []
878 for i in range(400):
879 d = self.theclass(2000+i, 12, 31)
880 d1 = self.theclass(1600+i, 12, 31)
881 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
882 if d.isocalendar()[1] == 53:
883 L.append(i)
884 self.assertEqual(L, iso_long_years)
885
886 def test_isoformat(self):
887 t = self.theclass(2, 3, 2)
888 self.assertEqual(t.isoformat(), "0002-03-02")
889
890 def test_ctime(self):
891 t = self.theclass(2002, 3, 2)
892 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
893
894 def test_strftime(self):
895 t = self.theclass(2005, 3, 2)
896 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000897 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000898 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000899
900 self.assertRaises(TypeError, t.strftime) # needs an arg
901 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
902 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
903
Georg Brandlf78e02b2008-06-10 17:40:04 +0000904 # test that unicode input is allowed (issue 2782)
905 self.assertEqual(t.strftime("%m"), "03")
906
Tim Peters2a799bf2002-12-16 20:18:38 +0000907 # A naive object replaces %z and %Z w/ empty strings.
908 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
909
Benjamin Petersone1cdfd72009-01-18 21:02:37 +0000910 #make sure that invalid format specifiers are handled correctly
911 #self.assertRaises(ValueError, t.strftime, "%e")
912 #self.assertRaises(ValueError, t.strftime, "%")
913 #self.assertRaises(ValueError, t.strftime, "%#")
914
915 #oh well, some systems just ignore those invalid ones.
916 #at least, excercise them to make sure that no crashes
917 #are generated
918 for f in ["%e", "%", "%#"]:
919 try:
920 t.strftime(f)
921 except ValueError:
922 pass
923
924 #check that this standard extension works
925 t.strftime("%f")
926
Georg Brandlf78e02b2008-06-10 17:40:04 +0000927
Eric Smith1ba31142007-09-11 18:06:02 +0000928 def test_format(self):
929 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000930 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000931
932 # check that a derived class's __str__() gets called
933 class A(self.theclass):
934 def __str__(self):
935 return 'A'
936 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000937 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000938
939 # check that a derived class's strftime gets called
940 class B(self.theclass):
941 def strftime(self, format_spec):
942 return 'B'
943 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000944 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000945
946 for fmt in ["m:%m d:%d y:%y",
947 "m:%m d:%d y:%y H:%H M:%M S:%S",
948 "%z %Z",
949 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +0000950 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
951 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
952 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +0000953
Tim Peters2a799bf2002-12-16 20:18:38 +0000954 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000955 self.assertIsInstance(self.theclass.min, self.theclass)
956 self.assertIsInstance(self.theclass.max, self.theclass)
957 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000958 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000959
960 def test_extreme_timedelta(self):
961 big = self.theclass.max - self.theclass.min
962 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
963 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
964 # n == 315537897599999999 ~= 2**58.13
965 justasbig = timedelta(0, 0, n)
966 self.assertEqual(big, justasbig)
967 self.assertEqual(self.theclass.min + big, self.theclass.max)
968 self.assertEqual(self.theclass.max - big, self.theclass.min)
969
970 def test_timetuple(self):
971 for i in range(7):
972 # January 2, 1956 is a Monday (0)
973 d = self.theclass(1956, 1, 2+i)
974 t = d.timetuple()
975 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
976 # February 1, 1956 is a Wednesday (2)
977 d = self.theclass(1956, 2, 1+i)
978 t = d.timetuple()
979 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
980 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
981 # of the year.
982 d = self.theclass(1956, 3, 1+i)
983 t = d.timetuple()
984 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
985 self.assertEqual(t.tm_year, 1956)
986 self.assertEqual(t.tm_mon, 3)
987 self.assertEqual(t.tm_mday, 1+i)
988 self.assertEqual(t.tm_hour, 0)
989 self.assertEqual(t.tm_min, 0)
990 self.assertEqual(t.tm_sec, 0)
991 self.assertEqual(t.tm_wday, (3+i)%7)
992 self.assertEqual(t.tm_yday, 61+i)
993 self.assertEqual(t.tm_isdst, -1)
994
995 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000996 args = 6, 7, 23
997 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000998 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000999 green = pickler.dumps(orig, proto)
1000 derived = unpickler.loads(green)
1001 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001002
1003 def test_compare(self):
1004 t1 = self.theclass(2, 3, 4)
1005 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001006 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001007 self.assertTrue(t1 <= t2)
1008 self.assertTrue(t1 >= t2)
1009 self.assertTrue(not t1 != t2)
1010 self.assertTrue(not t1 < t2)
1011 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001012
1013 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1014 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001015 self.assertTrue(t1 < t2)
1016 self.assertTrue(t2 > t1)
1017 self.assertTrue(t1 <= t2)
1018 self.assertTrue(t2 >= t1)
1019 self.assertTrue(t1 != t2)
1020 self.assertTrue(t2 != t1)
1021 self.assertTrue(not t1 == t2)
1022 self.assertTrue(not t2 == t1)
1023 self.assertTrue(not t1 > t2)
1024 self.assertTrue(not t2 < t1)
1025 self.assertTrue(not t1 >= t2)
1026 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001027
Tim Peters68124bb2003-02-08 03:46:31 +00001028 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001029 self.assertEqual(t1 == badarg, False)
1030 self.assertEqual(t1 != badarg, True)
1031 self.assertEqual(badarg == t1, False)
1032 self.assertEqual(badarg != t1, True)
1033
Tim Peters2a799bf2002-12-16 20:18:38 +00001034 self.assertRaises(TypeError, lambda: t1 < badarg)
1035 self.assertRaises(TypeError, lambda: t1 > badarg)
1036 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001037 self.assertRaises(TypeError, lambda: badarg <= t1)
1038 self.assertRaises(TypeError, lambda: badarg < t1)
1039 self.assertRaises(TypeError, lambda: badarg > t1)
1040 self.assertRaises(TypeError, lambda: badarg >= t1)
1041
Tim Peters8d81a012003-01-24 22:36:34 +00001042 def test_mixed_compare(self):
1043 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001044
1045 # Our class can be compared for equality to other classes
1046 self.assertEqual(our == 1, False)
1047 self.assertEqual(1 == our, False)
1048 self.assertEqual(our != 1, True)
1049 self.assertEqual(1 != our, True)
1050
1051 # But the ordering is undefined
1052 self.assertRaises(TypeError, lambda: our < 1)
1053 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001054
Guido van Rossum19960592006-08-24 17:29:38 +00001055 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001056
Guido van Rossum19960592006-08-24 17:29:38 +00001057 class SomeClass:
1058 pass
1059
1060 their = SomeClass()
1061 self.assertEqual(our == their, False)
1062 self.assertEqual(their == our, False)
1063 self.assertEqual(our != their, True)
1064 self.assertEqual(their != our, True)
1065 self.assertRaises(TypeError, lambda: our < their)
1066 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001067
Guido van Rossum19960592006-08-24 17:29:38 +00001068 # However, if the other class explicitly defines ordering
1069 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001070
Guido van Rossum19960592006-08-24 17:29:38 +00001071 class LargerThanAnything:
1072 def __lt__(self, other):
1073 return False
1074 def __le__(self, other):
1075 return isinstance(other, LargerThanAnything)
1076 def __eq__(self, other):
1077 return isinstance(other, LargerThanAnything)
1078 def __ne__(self, other):
1079 return not isinstance(other, LargerThanAnything)
1080 def __gt__(self, other):
1081 return not isinstance(other, LargerThanAnything)
1082 def __ge__(self, other):
1083 return True
1084
1085 their = LargerThanAnything()
1086 self.assertEqual(our == their, False)
1087 self.assertEqual(their == our, False)
1088 self.assertEqual(our != their, True)
1089 self.assertEqual(their != our, True)
1090 self.assertEqual(our < their, True)
1091 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001092
Tim Peters2a799bf2002-12-16 20:18:38 +00001093 def test_bool(self):
1094 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001095 self.assertTrue(self.theclass.min)
1096 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001097
Guido van Rossum04110fb2007-08-24 16:32:05 +00001098 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001099 # For nasty technical reasons, we can't handle years before 1900.
1100 cls = self.theclass
1101 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1102 for y in 1, 49, 51, 99, 100, 1000, 1899:
1103 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001104
1105 def test_replace(self):
1106 cls = self.theclass
1107 args = [1, 2, 3]
1108 base = cls(*args)
1109 self.assertEqual(base, base.replace())
1110
1111 i = 0
1112 for name, newval in (("year", 2),
1113 ("month", 3),
1114 ("day", 4)):
1115 newargs = args[:]
1116 newargs[i] = newval
1117 expected = cls(*newargs)
1118 got = base.replace(**{name: newval})
1119 self.assertEqual(expected, got)
1120 i += 1
1121
1122 # Out of bounds.
1123 base = cls(2000, 2, 29)
1124 self.assertRaises(ValueError, base.replace, year=2001)
1125
Tim Petersa98924a2003-05-17 05:55:19 +00001126 def test_subclass_date(self):
1127
1128 class C(self.theclass):
1129 theAnswer = 42
1130
1131 def __new__(cls, *args, **kws):
1132 temp = kws.copy()
1133 extra = temp.pop('extra')
1134 result = self.theclass.__new__(cls, *args, **temp)
1135 result.extra = extra
1136 return result
1137
1138 def newmeth(self, start):
1139 return start + self.year + self.month
1140
1141 args = 2003, 4, 14
1142
1143 dt1 = self.theclass(*args)
1144 dt2 = C(*args, **{'extra': 7})
1145
1146 self.assertEqual(dt2.__class__, C)
1147 self.assertEqual(dt2.theAnswer, 42)
1148 self.assertEqual(dt2.extra, 7)
1149 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1150 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1151
Tim Peters604c0132004-06-07 23:04:33 +00001152 def test_pickling_subclass_date(self):
1153
1154 args = 6, 7, 23
1155 orig = SubclassDate(*args)
1156 for pickler, unpickler, proto in pickle_choices:
1157 green = pickler.dumps(orig, proto)
1158 derived = unpickler.loads(green)
1159 self.assertEqual(orig, derived)
1160
Tim Peters3f606292004-03-21 23:38:41 +00001161 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001162 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001163 # This is a low-overhead backdoor. A user can (by intent or
1164 # mistake) pass a string directly, which (if it's the right length)
1165 # will get treated like a pickle, and bypass the normal sanity
1166 # checks in the constructor. This can create insane objects.
1167 # The constructor doesn't want to burn the time to validate all
1168 # fields, but does check the month field. This stops, e.g.,
1169 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001170 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001171 if not issubclass(self.theclass, datetime):
1172 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001173 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001174 self.assertRaises(TypeError, self.theclass,
1175 base[:2] + month_byte + base[3:])
1176 for ord_byte in range(1, 13):
1177 # This shouldn't blow up because of the month byte alone. If
1178 # the implementation changes to do more-careful checking, it may
1179 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001180 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001181
Tim Peters2a799bf2002-12-16 20:18:38 +00001182#############################################################################
1183# datetime tests
1184
Tim Peters604c0132004-06-07 23:04:33 +00001185class SubclassDatetime(datetime):
1186 sub_var = 1
1187
Tim Peters2a799bf2002-12-16 20:18:38 +00001188class TestDateTime(TestDate):
1189
1190 theclass = datetime
1191
1192 def test_basic_attributes(self):
1193 dt = self.theclass(2002, 3, 1, 12, 0)
1194 self.assertEqual(dt.year, 2002)
1195 self.assertEqual(dt.month, 3)
1196 self.assertEqual(dt.day, 1)
1197 self.assertEqual(dt.hour, 12)
1198 self.assertEqual(dt.minute, 0)
1199 self.assertEqual(dt.second, 0)
1200 self.assertEqual(dt.microsecond, 0)
1201
1202 def test_basic_attributes_nonzero(self):
1203 # Make sure all attributes are non-zero so bugs in
1204 # bit-shifting access show up.
1205 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1206 self.assertEqual(dt.year, 2002)
1207 self.assertEqual(dt.month, 3)
1208 self.assertEqual(dt.day, 1)
1209 self.assertEqual(dt.hour, 12)
1210 self.assertEqual(dt.minute, 59)
1211 self.assertEqual(dt.second, 59)
1212 self.assertEqual(dt.microsecond, 8000)
1213
1214 def test_roundtrip(self):
1215 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1216 self.theclass.now()):
1217 # Verify dt -> string -> datetime identity.
1218 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001219 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001220 s = s[9:]
1221 dt2 = eval(s)
1222 self.assertEqual(dt, dt2)
1223
1224 # Verify identity via reconstructing from pieces.
1225 dt2 = self.theclass(dt.year, dt.month, dt.day,
1226 dt.hour, dt.minute, dt.second,
1227 dt.microsecond)
1228 self.assertEqual(dt, dt2)
1229
1230 def test_isoformat(self):
1231 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1232 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1233 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1234 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001235 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001236 # str is ISO format with the separator forced to a blank.
1237 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1238
1239 t = self.theclass(2, 3, 2)
1240 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1241 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1242 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1243 # str is ISO format with the separator forced to a blank.
1244 self.assertEqual(str(t), "0002-03-02 00:00:00")
1245
Eric Smith1ba31142007-09-11 18:06:02 +00001246 def test_format(self):
1247 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001248 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001249
1250 # check that a derived class's __str__() gets called
1251 class A(self.theclass):
1252 def __str__(self):
1253 return 'A'
1254 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001255 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001256
1257 # check that a derived class's strftime gets called
1258 class B(self.theclass):
1259 def strftime(self, format_spec):
1260 return 'B'
1261 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001262 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001263
1264 for fmt in ["m:%m d:%d y:%y",
1265 "m:%m d:%d y:%y H:%H M:%M S:%S",
1266 "%z %Z",
1267 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001268 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1269 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1270 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001271
Tim Peters2a799bf2002-12-16 20:18:38 +00001272 def test_more_ctime(self):
1273 # Test fields that TestDate doesn't touch.
1274 import time
1275
1276 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1277 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1278 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1279 # out. The difference is that t.ctime() produces " 2" for the day,
1280 # but platform ctime() produces "02" for the day. According to
1281 # C99, t.ctime() is correct here.
1282 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1283
1284 # So test a case where that difference doesn't matter.
1285 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1286 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1287
1288 def test_tz_independent_comparing(self):
1289 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1290 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1291 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1292 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001293 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001294
1295 # Make sure comparison doesn't forget microseconds, and isn't done
1296 # via comparing a float timestamp (an IEEE double doesn't have enough
1297 # precision to span microsecond resolution across years 1 thru 9999,
1298 # so comparing via timestamp necessarily calls some distinct values
1299 # equal).
1300 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1301 us = timedelta(microseconds=1)
1302 dt2 = dt1 + us
1303 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001304 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001305
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001306 def test_strftime_with_bad_tzname_replace(self):
1307 # verify ok if tzinfo.tzname().replace() returns a non-string
1308 class MyTzInfo(FixedOffset):
1309 def tzname(self, dt):
1310 class MyStr(str):
1311 def replace(self, *args):
1312 return None
1313 return MyStr('name')
1314 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1315 self.assertRaises(TypeError, t.strftime, '%Z')
1316
Tim Peters2a799bf2002-12-16 20:18:38 +00001317 def test_bad_constructor_arguments(self):
1318 # bad years
1319 self.theclass(MINYEAR, 1, 1) # no exception
1320 self.theclass(MAXYEAR, 1, 1) # no exception
1321 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1322 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1323 # bad months
1324 self.theclass(2000, 1, 1) # no exception
1325 self.theclass(2000, 12, 1) # no exception
1326 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1327 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1328 # bad days
1329 self.theclass(2000, 2, 29) # no exception
1330 self.theclass(2004, 2, 29) # no exception
1331 self.theclass(2400, 2, 29) # no exception
1332 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1333 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1334 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1335 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1336 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1337 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1338 # bad hours
1339 self.theclass(2000, 1, 31, 0) # no exception
1340 self.theclass(2000, 1, 31, 23) # no exception
1341 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1342 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1343 # bad minutes
1344 self.theclass(2000, 1, 31, 23, 0) # no exception
1345 self.theclass(2000, 1, 31, 23, 59) # no exception
1346 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1347 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1348 # bad seconds
1349 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1350 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1351 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1352 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1353 # bad microseconds
1354 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1355 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1356 self.assertRaises(ValueError, self.theclass,
1357 2000, 1, 31, 23, 59, 59, -1)
1358 self.assertRaises(ValueError, self.theclass,
1359 2000, 1, 31, 23, 59, 59,
1360 1000000)
1361
1362 def test_hash_equality(self):
1363 d = self.theclass(2000, 12, 31, 23, 30, 17)
1364 e = self.theclass(2000, 12, 31, 23, 30, 17)
1365 self.assertEqual(d, e)
1366 self.assertEqual(hash(d), hash(e))
1367
1368 dic = {d: 1}
1369 dic[e] = 2
1370 self.assertEqual(len(dic), 1)
1371 self.assertEqual(dic[d], 2)
1372 self.assertEqual(dic[e], 2)
1373
1374 d = self.theclass(2001, 1, 1, 0, 5, 17)
1375 e = self.theclass(2001, 1, 1, 0, 5, 17)
1376 self.assertEqual(d, e)
1377 self.assertEqual(hash(d), hash(e))
1378
1379 dic = {d: 1}
1380 dic[e] = 2
1381 self.assertEqual(len(dic), 1)
1382 self.assertEqual(dic[d], 2)
1383 self.assertEqual(dic[e], 2)
1384
1385 def test_computations(self):
1386 a = self.theclass(2002, 1, 31)
1387 b = self.theclass(1956, 1, 31)
1388 diff = a-b
1389 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1390 self.assertEqual(diff.seconds, 0)
1391 self.assertEqual(diff.microseconds, 0)
1392 a = self.theclass(2002, 3, 2, 17, 6)
1393 millisec = timedelta(0, 0, 1000)
1394 hour = timedelta(0, 3600)
1395 day = timedelta(1)
1396 week = timedelta(7)
1397 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1398 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1399 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1400 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1401 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1402 self.assertEqual(a - hour, a + -hour)
1403 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1404 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1405 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1406 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1407 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1408 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1409 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1410 self.assertEqual((a + week) - a, week)
1411 self.assertEqual((a + day) - a, day)
1412 self.assertEqual((a + hour) - a, hour)
1413 self.assertEqual((a + millisec) - a, millisec)
1414 self.assertEqual((a - week) - a, -week)
1415 self.assertEqual((a - day) - a, -day)
1416 self.assertEqual((a - hour) - a, -hour)
1417 self.assertEqual((a - millisec) - a, -millisec)
1418 self.assertEqual(a - (a + week), -week)
1419 self.assertEqual(a - (a + day), -day)
1420 self.assertEqual(a - (a + hour), -hour)
1421 self.assertEqual(a - (a + millisec), -millisec)
1422 self.assertEqual(a - (a - week), week)
1423 self.assertEqual(a - (a - day), day)
1424 self.assertEqual(a - (a - hour), hour)
1425 self.assertEqual(a - (a - millisec), millisec)
1426 self.assertEqual(a + (week + day + hour + millisec),
1427 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1428 self.assertEqual(a + (week + day + hour + millisec),
1429 (((a + week) + day) + hour) + millisec)
1430 self.assertEqual(a - (week + day + hour + millisec),
1431 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1432 self.assertEqual(a - (week + day + hour + millisec),
1433 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001434 # Add/sub ints or floats should be illegal
1435 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001436 self.assertRaises(TypeError, lambda: a+i)
1437 self.assertRaises(TypeError, lambda: a-i)
1438 self.assertRaises(TypeError, lambda: i+a)
1439 self.assertRaises(TypeError, lambda: i-a)
1440
1441 # delta - datetime is senseless.
1442 self.assertRaises(TypeError, lambda: day - a)
1443 # mixing datetime and (delta or datetime) via * or // is senseless
1444 self.assertRaises(TypeError, lambda: day * a)
1445 self.assertRaises(TypeError, lambda: a * day)
1446 self.assertRaises(TypeError, lambda: day // a)
1447 self.assertRaises(TypeError, lambda: a // day)
1448 self.assertRaises(TypeError, lambda: a * a)
1449 self.assertRaises(TypeError, lambda: a // a)
1450 # datetime + datetime is senseless
1451 self.assertRaises(TypeError, lambda: a + a)
1452
1453 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001454 args = 6, 7, 23, 20, 59, 1, 64**2
1455 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001456 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001457 green = pickler.dumps(orig, proto)
1458 derived = unpickler.loads(green)
1459 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001460
Guido van Rossum275666f2003-02-07 21:49:01 +00001461 def test_more_pickling(self):
1462 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1463 s = pickle.dumps(a)
1464 b = pickle.loads(s)
1465 self.assertEqual(b.year, 2003)
1466 self.assertEqual(b.month, 2)
1467 self.assertEqual(b.day, 7)
1468
Tim Peters604c0132004-06-07 23:04:33 +00001469 def test_pickling_subclass_datetime(self):
1470 args = 6, 7, 23, 20, 59, 1, 64**2
1471 orig = SubclassDatetime(*args)
1472 for pickler, unpickler, proto in pickle_choices:
1473 green = pickler.dumps(orig, proto)
1474 derived = unpickler.loads(green)
1475 self.assertEqual(orig, derived)
1476
Tim Peters2a799bf2002-12-16 20:18:38 +00001477 def test_more_compare(self):
1478 # The test_compare() inherited from TestDate covers the error cases.
1479 # We just want to test lexicographic ordering on the members datetime
1480 # has that date lacks.
1481 args = [2000, 11, 29, 20, 58, 16, 999998]
1482 t1 = self.theclass(*args)
1483 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001484 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001485 self.assertTrue(t1 <= t2)
1486 self.assertTrue(t1 >= t2)
1487 self.assertTrue(not t1 != t2)
1488 self.assertTrue(not t1 < t2)
1489 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001490
1491 for i in range(len(args)):
1492 newargs = args[:]
1493 newargs[i] = args[i] + 1
1494 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001495 self.assertTrue(t1 < t2)
1496 self.assertTrue(t2 > t1)
1497 self.assertTrue(t1 <= t2)
1498 self.assertTrue(t2 >= t1)
1499 self.assertTrue(t1 != t2)
1500 self.assertTrue(t2 != t1)
1501 self.assertTrue(not t1 == t2)
1502 self.assertTrue(not t2 == t1)
1503 self.assertTrue(not t1 > t2)
1504 self.assertTrue(not t2 < t1)
1505 self.assertTrue(not t1 >= t2)
1506 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001507
1508
1509 # A helper for timestamp constructor tests.
1510 def verify_field_equality(self, expected, got):
1511 self.assertEqual(expected.tm_year, got.year)
1512 self.assertEqual(expected.tm_mon, got.month)
1513 self.assertEqual(expected.tm_mday, got.day)
1514 self.assertEqual(expected.tm_hour, got.hour)
1515 self.assertEqual(expected.tm_min, got.minute)
1516 self.assertEqual(expected.tm_sec, got.second)
1517
1518 def test_fromtimestamp(self):
1519 import time
1520
1521 ts = time.time()
1522 expected = time.localtime(ts)
1523 got = self.theclass.fromtimestamp(ts)
1524 self.verify_field_equality(expected, got)
1525
1526 def test_utcfromtimestamp(self):
1527 import time
1528
1529 ts = time.time()
1530 expected = time.gmtime(ts)
1531 got = self.theclass.utcfromtimestamp(ts)
1532 self.verify_field_equality(expected, got)
1533
Thomas Wouters477c8d52006-05-27 19:21:47 +00001534 def test_microsecond_rounding(self):
1535 # Test whether fromtimestamp "rounds up" floats that are less
1536 # than one microsecond smaller than an integer.
1537 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1538 self.theclass.fromtimestamp(1))
1539
Tim Peters1b6f7a92004-06-20 02:50:16 +00001540 def test_insane_fromtimestamp(self):
1541 # It's possible that some platform maps time_t to double,
1542 # and that this test will fail there. This test should
1543 # exempt such platforms (provided they return reasonable
1544 # results!).
1545 for insane in -1e200, 1e200:
1546 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1547 insane)
1548
1549 def test_insane_utcfromtimestamp(self):
1550 # It's possible that some platform maps time_t to double,
1551 # and that this test will fail there. This test should
1552 # exempt such platforms (provided they return reasonable
1553 # results!).
1554 for insane in -1e200, 1e200:
1555 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1556 insane)
1557
Guido van Rossumd8faa362007-04-27 19:54:29 +00001558 def test_negative_float_fromtimestamp(self):
1559 # Windows doesn't accept negative timestamps
1560 if os.name == "nt":
1561 return
1562 # The result is tz-dependent; at least test that this doesn't
1563 # fail (like it did before bug 1646728 was fixed).
1564 self.theclass.fromtimestamp(-1.05)
1565
1566 def test_negative_float_utcfromtimestamp(self):
1567 # Windows doesn't accept negative timestamps
1568 if os.name == "nt":
1569 return
1570 d = self.theclass.utcfromtimestamp(-1.05)
1571 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1572
Tim Peters2a799bf2002-12-16 20:18:38 +00001573 def test_utcnow(self):
1574 import time
1575
1576 # Call it a success if utcnow() and utcfromtimestamp() are within
1577 # a second of each other.
1578 tolerance = timedelta(seconds=1)
1579 for dummy in range(3):
1580 from_now = self.theclass.utcnow()
1581 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1582 if abs(from_timestamp - from_now) <= tolerance:
1583 break
1584 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001585 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001586
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001587 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001588 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001589
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001590 string = '2004-12-01 13:02:47.197'
1591 format = '%Y-%m-%d %H:%M:%S.%f'
1592 result, frac = _strptime._strptime(string, format)
1593 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001594 got = self.theclass.strptime(string, format)
1595 self.assertEqual(expected, got)
1596
Tim Peters2a799bf2002-12-16 20:18:38 +00001597 def test_more_timetuple(self):
1598 # This tests fields beyond those tested by the TestDate.test_timetuple.
1599 t = self.theclass(2004, 12, 31, 6, 22, 33)
1600 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1601 self.assertEqual(t.timetuple(),
1602 (t.year, t.month, t.day,
1603 t.hour, t.minute, t.second,
1604 t.weekday(),
1605 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1606 -1))
1607 tt = t.timetuple()
1608 self.assertEqual(tt.tm_year, t.year)
1609 self.assertEqual(tt.tm_mon, t.month)
1610 self.assertEqual(tt.tm_mday, t.day)
1611 self.assertEqual(tt.tm_hour, t.hour)
1612 self.assertEqual(tt.tm_min, t.minute)
1613 self.assertEqual(tt.tm_sec, t.second)
1614 self.assertEqual(tt.tm_wday, t.weekday())
1615 self.assertEqual(tt.tm_yday, t.toordinal() -
1616 date(t.year, 1, 1).toordinal() + 1)
1617 self.assertEqual(tt.tm_isdst, -1)
1618
1619 def test_more_strftime(self):
1620 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001621 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1622 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1623 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001624
1625 def test_extract(self):
1626 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1627 self.assertEqual(dt.date(), date(2002, 3, 4))
1628 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1629
1630 def test_combine(self):
1631 d = date(2002, 3, 4)
1632 t = time(18, 45, 3, 1234)
1633 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1634 combine = self.theclass.combine
1635 dt = combine(d, t)
1636 self.assertEqual(dt, expected)
1637
1638 dt = combine(time=t, date=d)
1639 self.assertEqual(dt, expected)
1640
1641 self.assertEqual(d, dt.date())
1642 self.assertEqual(t, dt.time())
1643 self.assertEqual(dt, combine(dt.date(), dt.time()))
1644
1645 self.assertRaises(TypeError, combine) # need an arg
1646 self.assertRaises(TypeError, combine, d) # need two args
1647 self.assertRaises(TypeError, combine, t, d) # args reversed
1648 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1649 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1650
Tim Peters12bf3392002-12-24 05:41:27 +00001651 def test_replace(self):
1652 cls = self.theclass
1653 args = [1, 2, 3, 4, 5, 6, 7]
1654 base = cls(*args)
1655 self.assertEqual(base, base.replace())
1656
1657 i = 0
1658 for name, newval in (("year", 2),
1659 ("month", 3),
1660 ("day", 4),
1661 ("hour", 5),
1662 ("minute", 6),
1663 ("second", 7),
1664 ("microsecond", 8)):
1665 newargs = args[:]
1666 newargs[i] = newval
1667 expected = cls(*newargs)
1668 got = base.replace(**{name: newval})
1669 self.assertEqual(expected, got)
1670 i += 1
1671
1672 # Out of bounds.
1673 base = cls(2000, 2, 29)
1674 self.assertRaises(ValueError, base.replace, year=2001)
1675
Tim Peters80475bb2002-12-25 07:40:55 +00001676 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001677 # Pretty boring! The TZ test is more interesting here. astimezone()
1678 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001679 dt = self.theclass.now()
1680 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001681 self.assertRaises(TypeError, dt.astimezone) # not enough args
1682 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1683 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001684 self.assertRaises(ValueError, dt.astimezone, f) # naive
1685 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001686
Tim Peters52dcce22003-01-23 16:36:11 +00001687 class Bogus(tzinfo):
1688 def utcoffset(self, dt): return None
1689 def dst(self, dt): return timedelta(0)
1690 bog = Bogus()
1691 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1692
1693 class AlsoBogus(tzinfo):
1694 def utcoffset(self, dt): return timedelta(0)
1695 def dst(self, dt): return None
1696 alsobog = AlsoBogus()
1697 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001698
Tim Petersa98924a2003-05-17 05:55:19 +00001699 def test_subclass_datetime(self):
1700
1701 class C(self.theclass):
1702 theAnswer = 42
1703
1704 def __new__(cls, *args, **kws):
1705 temp = kws.copy()
1706 extra = temp.pop('extra')
1707 result = self.theclass.__new__(cls, *args, **temp)
1708 result.extra = extra
1709 return result
1710
1711 def newmeth(self, start):
1712 return start + self.year + self.month + self.second
1713
1714 args = 2003, 4, 14, 12, 13, 41
1715
1716 dt1 = self.theclass(*args)
1717 dt2 = C(*args, **{'extra': 7})
1718
1719 self.assertEqual(dt2.__class__, C)
1720 self.assertEqual(dt2.theAnswer, 42)
1721 self.assertEqual(dt2.extra, 7)
1722 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1723 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1724 dt1.second - 7)
1725
Tim Peters604c0132004-06-07 23:04:33 +00001726class SubclassTime(time):
1727 sub_var = 1
1728
Guido van Rossumd8faa362007-04-27 19:54:29 +00001729class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001730
1731 theclass = time
1732
1733 def test_basic_attributes(self):
1734 t = self.theclass(12, 0)
1735 self.assertEqual(t.hour, 12)
1736 self.assertEqual(t.minute, 0)
1737 self.assertEqual(t.second, 0)
1738 self.assertEqual(t.microsecond, 0)
1739
1740 def test_basic_attributes_nonzero(self):
1741 # Make sure all attributes are non-zero so bugs in
1742 # bit-shifting access show up.
1743 t = self.theclass(12, 59, 59, 8000)
1744 self.assertEqual(t.hour, 12)
1745 self.assertEqual(t.minute, 59)
1746 self.assertEqual(t.second, 59)
1747 self.assertEqual(t.microsecond, 8000)
1748
1749 def test_roundtrip(self):
1750 t = self.theclass(1, 2, 3, 4)
1751
1752 # Verify t -> string -> time identity.
1753 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001754 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001755 s = s[9:]
1756 t2 = eval(s)
1757 self.assertEqual(t, t2)
1758
1759 # Verify identity via reconstructing from pieces.
1760 t2 = self.theclass(t.hour, t.minute, t.second,
1761 t.microsecond)
1762 self.assertEqual(t, t2)
1763
1764 def test_comparing(self):
1765 args = [1, 2, 3, 4]
1766 t1 = self.theclass(*args)
1767 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001768 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001769 self.assertTrue(t1 <= t2)
1770 self.assertTrue(t1 >= t2)
1771 self.assertTrue(not t1 != t2)
1772 self.assertTrue(not t1 < t2)
1773 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001774
1775 for i in range(len(args)):
1776 newargs = args[:]
1777 newargs[i] = args[i] + 1
1778 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001779 self.assertTrue(t1 < t2)
1780 self.assertTrue(t2 > t1)
1781 self.assertTrue(t1 <= t2)
1782 self.assertTrue(t2 >= t1)
1783 self.assertTrue(t1 != t2)
1784 self.assertTrue(t2 != t1)
1785 self.assertTrue(not t1 == t2)
1786 self.assertTrue(not t2 == t1)
1787 self.assertTrue(not t1 > t2)
1788 self.assertTrue(not t2 < t1)
1789 self.assertTrue(not t1 >= t2)
1790 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001791
Tim Peters68124bb2003-02-08 03:46:31 +00001792 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001793 self.assertEqual(t1 == badarg, False)
1794 self.assertEqual(t1 != badarg, True)
1795 self.assertEqual(badarg == t1, False)
1796 self.assertEqual(badarg != t1, True)
1797
Tim Peters2a799bf2002-12-16 20:18:38 +00001798 self.assertRaises(TypeError, lambda: t1 <= badarg)
1799 self.assertRaises(TypeError, lambda: t1 < badarg)
1800 self.assertRaises(TypeError, lambda: t1 > badarg)
1801 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001802 self.assertRaises(TypeError, lambda: badarg <= t1)
1803 self.assertRaises(TypeError, lambda: badarg < t1)
1804 self.assertRaises(TypeError, lambda: badarg > t1)
1805 self.assertRaises(TypeError, lambda: badarg >= t1)
1806
1807 def test_bad_constructor_arguments(self):
1808 # bad hours
1809 self.theclass(0, 0) # no exception
1810 self.theclass(23, 0) # no exception
1811 self.assertRaises(ValueError, self.theclass, -1, 0)
1812 self.assertRaises(ValueError, self.theclass, 24, 0)
1813 # bad minutes
1814 self.theclass(23, 0) # no exception
1815 self.theclass(23, 59) # no exception
1816 self.assertRaises(ValueError, self.theclass, 23, -1)
1817 self.assertRaises(ValueError, self.theclass, 23, 60)
1818 # bad seconds
1819 self.theclass(23, 59, 0) # no exception
1820 self.theclass(23, 59, 59) # no exception
1821 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1822 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1823 # bad microseconds
1824 self.theclass(23, 59, 59, 0) # no exception
1825 self.theclass(23, 59, 59, 999999) # no exception
1826 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1827 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1828
1829 def test_hash_equality(self):
1830 d = self.theclass(23, 30, 17)
1831 e = self.theclass(23, 30, 17)
1832 self.assertEqual(d, e)
1833 self.assertEqual(hash(d), hash(e))
1834
1835 dic = {d: 1}
1836 dic[e] = 2
1837 self.assertEqual(len(dic), 1)
1838 self.assertEqual(dic[d], 2)
1839 self.assertEqual(dic[e], 2)
1840
1841 d = self.theclass(0, 5, 17)
1842 e = self.theclass(0, 5, 17)
1843 self.assertEqual(d, e)
1844 self.assertEqual(hash(d), hash(e))
1845
1846 dic = {d: 1}
1847 dic[e] = 2
1848 self.assertEqual(len(dic), 1)
1849 self.assertEqual(dic[d], 2)
1850 self.assertEqual(dic[e], 2)
1851
1852 def test_isoformat(self):
1853 t = self.theclass(4, 5, 1, 123)
1854 self.assertEqual(t.isoformat(), "04:05:01.000123")
1855 self.assertEqual(t.isoformat(), str(t))
1856
1857 t = self.theclass()
1858 self.assertEqual(t.isoformat(), "00:00:00")
1859 self.assertEqual(t.isoformat(), str(t))
1860
1861 t = self.theclass(microsecond=1)
1862 self.assertEqual(t.isoformat(), "00:00:00.000001")
1863 self.assertEqual(t.isoformat(), str(t))
1864
1865 t = self.theclass(microsecond=10)
1866 self.assertEqual(t.isoformat(), "00:00:00.000010")
1867 self.assertEqual(t.isoformat(), str(t))
1868
1869 t = self.theclass(microsecond=100)
1870 self.assertEqual(t.isoformat(), "00:00:00.000100")
1871 self.assertEqual(t.isoformat(), str(t))
1872
1873 t = self.theclass(microsecond=1000)
1874 self.assertEqual(t.isoformat(), "00:00:00.001000")
1875 self.assertEqual(t.isoformat(), str(t))
1876
1877 t = self.theclass(microsecond=10000)
1878 self.assertEqual(t.isoformat(), "00:00:00.010000")
1879 self.assertEqual(t.isoformat(), str(t))
1880
1881 t = self.theclass(microsecond=100000)
1882 self.assertEqual(t.isoformat(), "00:00:00.100000")
1883 self.assertEqual(t.isoformat(), str(t))
1884
Thomas Wouterscf297e42007-02-23 15:07:44 +00001885 def test_1653736(self):
1886 # verify it doesn't accept extra keyword arguments
1887 t = self.theclass(second=1)
1888 self.assertRaises(TypeError, t.isoformat, foo=3)
1889
Tim Peters2a799bf2002-12-16 20:18:38 +00001890 def test_strftime(self):
1891 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001892 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001893 # A naive object replaces %z and %Z with empty strings.
1894 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1895
Eric Smith1ba31142007-09-11 18:06:02 +00001896 def test_format(self):
1897 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001898 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001899
1900 # check that a derived class's __str__() gets called
1901 class A(self.theclass):
1902 def __str__(self):
1903 return 'A'
1904 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001905 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001906
1907 # check that a derived class's strftime gets called
1908 class B(self.theclass):
1909 def strftime(self, format_spec):
1910 return 'B'
1911 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001912 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001913
1914 for fmt in ['%H %M %S',
1915 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001916 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1917 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1918 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001919
Tim Peters2a799bf2002-12-16 20:18:38 +00001920 def test_str(self):
1921 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1922 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1923 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1924 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1925 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1926
1927 def test_repr(self):
1928 name = 'datetime.' + self.theclass.__name__
1929 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1930 "%s(1, 2, 3, 4)" % name)
1931 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1932 "%s(10, 2, 3, 4000)" % name)
1933 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1934 "%s(0, 2, 3, 400000)" % name)
1935 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1936 "%s(12, 2, 3)" % name)
1937 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1938 "%s(23, 15)" % name)
1939
1940 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00001941 self.assertIsInstance(self.theclass.min, self.theclass)
1942 self.assertIsInstance(self.theclass.max, self.theclass)
1943 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001944 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001945
1946 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001947 args = 20, 59, 16, 64**2
1948 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001949 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001950 green = pickler.dumps(orig, proto)
1951 derived = unpickler.loads(green)
1952 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001953
Tim Peters604c0132004-06-07 23:04:33 +00001954 def test_pickling_subclass_time(self):
1955 args = 20, 59, 16, 64**2
1956 orig = SubclassTime(*args)
1957 for pickler, unpickler, proto in pickle_choices:
1958 green = pickler.dumps(orig, proto)
1959 derived = unpickler.loads(green)
1960 self.assertEqual(orig, derived)
1961
Tim Peters2a799bf2002-12-16 20:18:38 +00001962 def test_bool(self):
1963 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001964 self.assertTrue(cls(1))
1965 self.assertTrue(cls(0, 1))
1966 self.assertTrue(cls(0, 0, 1))
1967 self.assertTrue(cls(0, 0, 0, 1))
1968 self.assertTrue(not cls(0))
1969 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001970
Tim Peters12bf3392002-12-24 05:41:27 +00001971 def test_replace(self):
1972 cls = self.theclass
1973 args = [1, 2, 3, 4]
1974 base = cls(*args)
1975 self.assertEqual(base, base.replace())
1976
1977 i = 0
1978 for name, newval in (("hour", 5),
1979 ("minute", 6),
1980 ("second", 7),
1981 ("microsecond", 8)):
1982 newargs = args[:]
1983 newargs[i] = newval
1984 expected = cls(*newargs)
1985 got = base.replace(**{name: newval})
1986 self.assertEqual(expected, got)
1987 i += 1
1988
1989 # Out of bounds.
1990 base = cls(1)
1991 self.assertRaises(ValueError, base.replace, hour=24)
1992 self.assertRaises(ValueError, base.replace, minute=-1)
1993 self.assertRaises(ValueError, base.replace, second=100)
1994 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1995
Tim Petersa98924a2003-05-17 05:55:19 +00001996 def test_subclass_time(self):
1997
1998 class C(self.theclass):
1999 theAnswer = 42
2000
2001 def __new__(cls, *args, **kws):
2002 temp = kws.copy()
2003 extra = temp.pop('extra')
2004 result = self.theclass.__new__(cls, *args, **temp)
2005 result.extra = extra
2006 return result
2007
2008 def newmeth(self, start):
2009 return start + self.hour + self.second
2010
2011 args = 4, 5, 6
2012
2013 dt1 = self.theclass(*args)
2014 dt2 = C(*args, **{'extra': 7})
2015
2016 self.assertEqual(dt2.__class__, C)
2017 self.assertEqual(dt2.theAnswer, 42)
2018 self.assertEqual(dt2.extra, 7)
2019 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2020 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2021
Armin Rigof4afb212005-11-07 07:15:48 +00002022 def test_backdoor_resistance(self):
2023 # see TestDate.test_backdoor_resistance().
2024 base = '2:59.0'
2025 for hour_byte in ' ', '9', chr(24), '\xff':
2026 self.assertRaises(TypeError, self.theclass,
2027 hour_byte + base[1:])
2028
Tim Peters855fe882002-12-22 03:43:39 +00002029# A mixin for classes with a tzinfo= argument. Subclasses must define
2030# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002031# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002032class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002033
Tim Petersbad8ff02002-12-30 20:52:32 +00002034 def test_argument_passing(self):
2035 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002036 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002037 class introspective(tzinfo):
2038 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002039 def utcoffset(self, dt):
2040 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002041 dst = utcoffset
2042
2043 obj = cls(1, 2, 3, tzinfo=introspective())
2044
Tim Peters0bf60bd2003-01-08 20:40:01 +00002045 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002046 self.assertEqual(obj.tzname(), expected)
2047
Tim Peters0bf60bd2003-01-08 20:40:01 +00002048 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002049 self.assertEqual(obj.utcoffset(), expected)
2050 self.assertEqual(obj.dst(), expected)
2051
Tim Peters855fe882002-12-22 03:43:39 +00002052 def test_bad_tzinfo_classes(self):
2053 cls = self.theclass
2054 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002055
Tim Peters855fe882002-12-22 03:43:39 +00002056 class NiceTry(object):
2057 def __init__(self): pass
2058 def utcoffset(self, dt): pass
2059 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2060
2061 class BetterTry(tzinfo):
2062 def __init__(self): pass
2063 def utcoffset(self, dt): pass
2064 b = BetterTry()
2065 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002066 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002067
2068 def test_utc_offset_out_of_bounds(self):
2069 class Edgy(tzinfo):
2070 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002071 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002072 def utcoffset(self, dt):
2073 return self.offset
2074
2075 cls = self.theclass
2076 for offset, legit in ((-1440, False),
2077 (-1439, True),
2078 (1439, True),
2079 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002080 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002081 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002082 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002083 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002084 else:
2085 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002086 if legit:
2087 aofs = abs(offset)
2088 h, m = divmod(aofs, 60)
2089 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002090 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002091 t = t.timetz()
2092 self.assertEqual(str(t), "01:02:03" + tag)
2093 else:
2094 self.assertRaises(ValueError, str, t)
2095
2096 def test_tzinfo_classes(self):
2097 cls = self.theclass
2098 class C1(tzinfo):
2099 def utcoffset(self, dt): return None
2100 def dst(self, dt): return None
2101 def tzname(self, dt): return None
2102 for t in (cls(1, 1, 1),
2103 cls(1, 1, 1, tzinfo=None),
2104 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002105 self.assertTrue(t.utcoffset() is None)
2106 self.assertTrue(t.dst() is None)
2107 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002108
Tim Peters855fe882002-12-22 03:43:39 +00002109 class C3(tzinfo):
2110 def utcoffset(self, dt): return timedelta(minutes=-1439)
2111 def dst(self, dt): return timedelta(minutes=1439)
2112 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002113 t = cls(1, 1, 1, tzinfo=C3())
2114 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2115 self.assertEqual(t.dst(), timedelta(minutes=1439))
2116 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002117
2118 # Wrong types.
2119 class C4(tzinfo):
2120 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002121 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002122 def tzname(self, dt): return 0
2123 t = cls(1, 1, 1, tzinfo=C4())
2124 self.assertRaises(TypeError, t.utcoffset)
2125 self.assertRaises(TypeError, t.dst)
2126 self.assertRaises(TypeError, t.tzname)
2127
2128 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002129 class C6(tzinfo):
2130 def utcoffset(self, dt): return timedelta(hours=-24)
2131 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002132 t = cls(1, 1, 1, tzinfo=C6())
2133 self.assertRaises(ValueError, t.utcoffset)
2134 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002135
2136 # Not a whole number of minutes.
2137 class C7(tzinfo):
2138 def utcoffset(self, dt): return timedelta(seconds=61)
2139 def dst(self, dt): return timedelta(microseconds=-81)
2140 t = cls(1, 1, 1, tzinfo=C7())
2141 self.assertRaises(ValueError, t.utcoffset)
2142 self.assertRaises(ValueError, t.dst)
2143
Tim Peters4c0db782002-12-26 05:01:19 +00002144 def test_aware_compare(self):
2145 cls = self.theclass
2146
Tim Peters60c76e42002-12-27 00:41:11 +00002147 # Ensure that utcoffset() gets ignored if the comparands have
2148 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002149 class OperandDependentOffset(tzinfo):
2150 def utcoffset(self, t):
2151 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002152 # d0 and d1 equal after adjustment
2153 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002154 else:
Tim Peters397301e2003-01-02 21:28:08 +00002155 # d2 off in the weeds
2156 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002157
2158 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2159 d0 = base.replace(minute=3)
2160 d1 = base.replace(minute=9)
2161 d2 = base.replace(minute=11)
2162 for x in d0, d1, d2:
2163 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002164 for op in lt, le, gt, ge, eq, ne:
2165 got = op(x, y)
2166 expected = op(x.minute, y.minute)
2167 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002168
2169 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002170 # Note that a time can't actually have an operand-depedent offset,
2171 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2172 # so skip this test for time.
2173 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002174 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2175 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2176 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2177 for x in d0, d1, d2:
2178 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002179 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002180 if (x is d0 or x is d1) and (y is d0 or y is d1):
2181 expected = 0
2182 elif x is y is d2:
2183 expected = 0
2184 elif x is d2:
2185 expected = -1
2186 else:
2187 assert y is d2
2188 expected = 1
2189 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002190
Tim Peters855fe882002-12-22 03:43:39 +00002191
Tim Peters0bf60bd2003-01-08 20:40:01 +00002192# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002193class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002194 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002195
2196 def test_empty(self):
2197 t = self.theclass()
2198 self.assertEqual(t.hour, 0)
2199 self.assertEqual(t.minute, 0)
2200 self.assertEqual(t.second, 0)
2201 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002202 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002203
Tim Peters2a799bf2002-12-16 20:18:38 +00002204 def test_zones(self):
2205 est = FixedOffset(-300, "EST", 1)
2206 utc = FixedOffset(0, "UTC", -2)
2207 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002208 t1 = time( 7, 47, tzinfo=est)
2209 t2 = time(12, 47, tzinfo=utc)
2210 t3 = time(13, 47, tzinfo=met)
2211 t4 = time(microsecond=40)
2212 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002213
2214 self.assertEqual(t1.tzinfo, est)
2215 self.assertEqual(t2.tzinfo, utc)
2216 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002217 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002218 self.assertEqual(t5.tzinfo, utc)
2219
Tim Peters855fe882002-12-22 03:43:39 +00002220 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2221 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2222 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002223 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002224 self.assertRaises(TypeError, t1.utcoffset, "no args")
2225
2226 self.assertEqual(t1.tzname(), "EST")
2227 self.assertEqual(t2.tzname(), "UTC")
2228 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002229 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002230 self.assertRaises(TypeError, t1.tzname, "no args")
2231
Tim Peters855fe882002-12-22 03:43:39 +00002232 self.assertEqual(t1.dst(), timedelta(minutes=1))
2233 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2234 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002235 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002236 self.assertRaises(TypeError, t1.dst, "no args")
2237
2238 self.assertEqual(hash(t1), hash(t2))
2239 self.assertEqual(hash(t1), hash(t3))
2240 self.assertEqual(hash(t2), hash(t3))
2241
2242 self.assertEqual(t1, t2)
2243 self.assertEqual(t1, t3)
2244 self.assertEqual(t2, t3)
2245 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2246 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2247 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2248
2249 self.assertEqual(str(t1), "07:47:00-05:00")
2250 self.assertEqual(str(t2), "12:47:00+00:00")
2251 self.assertEqual(str(t3), "13:47:00+01:00")
2252 self.assertEqual(str(t4), "00:00:00.000040")
2253 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2254
2255 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2256 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2257 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2258 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2259 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2260
Tim Peters0bf60bd2003-01-08 20:40:01 +00002261 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002262 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2263 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2264 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2265 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2266 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2267
2268 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2269 "07:47:00 %Z=EST %z=-0500")
2270 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2271 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2272
2273 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002274 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002275 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2276 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2277
Tim Petersb92bb712002-12-21 17:44:07 +00002278 # Check that an invalid tzname result raises an exception.
2279 class Badtzname(tzinfo):
2280 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002281 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002282 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2283 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002284
2285 def test_hash_edge_cases(self):
2286 # Offsets that overflow a basic time.
2287 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2288 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2289 self.assertEqual(hash(t1), hash(t2))
2290
2291 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2292 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2293 self.assertEqual(hash(t1), hash(t2))
2294
Tim Peters2a799bf2002-12-16 20:18:38 +00002295 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002296 # Try one without a tzinfo.
2297 args = 20, 59, 16, 64**2
2298 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002299 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002300 green = pickler.dumps(orig, proto)
2301 derived = unpickler.loads(green)
2302 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002303
2304 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002305 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002306 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002307 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002308 green = pickler.dumps(orig, proto)
2309 derived = unpickler.loads(green)
2310 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002311 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002312 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2313 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002314
2315 def test_more_bool(self):
2316 # Test cases with non-None tzinfo.
2317 cls = self.theclass
2318
2319 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002320 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002321
2322 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002323 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002324
2325 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002326 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002327
2328 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002329 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002330
2331 # Mostly ensuring this doesn't overflow internally.
2332 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002333 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002334
2335 # But this should yield a value error -- the utcoffset is bogus.
2336 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2337 self.assertRaises(ValueError, lambda: bool(t))
2338
2339 # Likewise.
2340 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2341 self.assertRaises(ValueError, lambda: bool(t))
2342
Tim Peters12bf3392002-12-24 05:41:27 +00002343 def test_replace(self):
2344 cls = self.theclass
2345 z100 = FixedOffset(100, "+100")
2346 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2347 args = [1, 2, 3, 4, z100]
2348 base = cls(*args)
2349 self.assertEqual(base, base.replace())
2350
2351 i = 0
2352 for name, newval in (("hour", 5),
2353 ("minute", 6),
2354 ("second", 7),
2355 ("microsecond", 8),
2356 ("tzinfo", zm200)):
2357 newargs = args[:]
2358 newargs[i] = newval
2359 expected = cls(*newargs)
2360 got = base.replace(**{name: newval})
2361 self.assertEqual(expected, got)
2362 i += 1
2363
2364 # Ensure we can get rid of a tzinfo.
2365 self.assertEqual(base.tzname(), "+100")
2366 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002367 self.assertTrue(base2.tzinfo is None)
2368 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002369
2370 # Ensure we can add one.
2371 base3 = base2.replace(tzinfo=z100)
2372 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002373 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002374
2375 # Out of bounds.
2376 base = cls(1)
2377 self.assertRaises(ValueError, base.replace, hour=24)
2378 self.assertRaises(ValueError, base.replace, minute=-1)
2379 self.assertRaises(ValueError, base.replace, second=100)
2380 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2381
Tim Peters60c76e42002-12-27 00:41:11 +00002382 def test_mixed_compare(self):
2383 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002384 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002385 self.assertEqual(t1, t2)
2386 t2 = t2.replace(tzinfo=None)
2387 self.assertEqual(t1, t2)
2388 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2389 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002390 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2391 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002392
Tim Peters0bf60bd2003-01-08 20:40:01 +00002393 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002394 class Varies(tzinfo):
2395 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002396 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002397 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002398 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002399 return self.offset
2400
2401 v = Varies()
2402 t1 = t2.replace(tzinfo=v)
2403 t2 = t2.replace(tzinfo=v)
2404 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2405 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2406 self.assertEqual(t1, t2)
2407
2408 # But if they're not identical, it isn't ignored.
2409 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002410 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002411
Tim Petersa98924a2003-05-17 05:55:19 +00002412 def test_subclass_timetz(self):
2413
2414 class C(self.theclass):
2415 theAnswer = 42
2416
2417 def __new__(cls, *args, **kws):
2418 temp = kws.copy()
2419 extra = temp.pop('extra')
2420 result = self.theclass.__new__(cls, *args, **temp)
2421 result.extra = extra
2422 return result
2423
2424 def newmeth(self, start):
2425 return start + self.hour + self.second
2426
2427 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2428
2429 dt1 = self.theclass(*args)
2430 dt2 = C(*args, **{'extra': 7})
2431
2432 self.assertEqual(dt2.__class__, C)
2433 self.assertEqual(dt2.theAnswer, 42)
2434 self.assertEqual(dt2.extra, 7)
2435 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2436 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2437
Tim Peters4c0db782002-12-26 05:01:19 +00002438
Tim Peters0bf60bd2003-01-08 20:40:01 +00002439# Testing datetime objects with a non-None tzinfo.
2440
Guido van Rossumd8faa362007-04-27 19:54:29 +00002441class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002442 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002443
2444 def test_trivial(self):
2445 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2446 self.assertEqual(dt.year, 1)
2447 self.assertEqual(dt.month, 2)
2448 self.assertEqual(dt.day, 3)
2449 self.assertEqual(dt.hour, 4)
2450 self.assertEqual(dt.minute, 5)
2451 self.assertEqual(dt.second, 6)
2452 self.assertEqual(dt.microsecond, 7)
2453 self.assertEqual(dt.tzinfo, None)
2454
2455 def test_even_more_compare(self):
2456 # The test_compare() and test_more_compare() inherited from TestDate
2457 # and TestDateTime covered non-tzinfo cases.
2458
2459 # Smallest possible after UTC adjustment.
2460 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2461 # Largest possible after UTC adjustment.
2462 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2463 tzinfo=FixedOffset(-1439, ""))
2464
2465 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002466 self.assertTrue(t1 < t2)
2467 self.assertTrue(t1 != t2)
2468 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002469
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002470 self.assertEqual(t1, t1)
2471 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002472
2473 # Equal afer adjustment.
2474 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2475 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2476 self.assertEqual(t1, t2)
2477
2478 # Change t1 not to subtract a minute, and t1 should be larger.
2479 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002480 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002481
2482 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2483 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002484 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002485
2486 # Back to the original t1, but make seconds resolve it.
2487 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2488 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002489 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002490
2491 # Likewise, but make microseconds resolve it.
2492 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2493 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002494 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002495
2496 # Make t2 naive and it should fail.
2497 t2 = self.theclass.min
2498 self.assertRaises(TypeError, lambda: t1 == t2)
2499 self.assertEqual(t2, t2)
2500
2501 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2502 class Naive(tzinfo):
2503 def utcoffset(self, dt): return None
2504 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2505 self.assertRaises(TypeError, lambda: t1 == t2)
2506 self.assertEqual(t2, t2)
2507
2508 # OTOH, it's OK to compare two of these mixing the two ways of being
2509 # naive.
2510 t1 = self.theclass(5, 6, 7)
2511 self.assertEqual(t1, t2)
2512
2513 # Try a bogus uctoffset.
2514 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002515 def utcoffset(self, dt):
2516 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002517 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2518 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002519 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002520
Tim Peters2a799bf2002-12-16 20:18:38 +00002521 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002522 # Try one without a tzinfo.
2523 args = 6, 7, 23, 20, 59, 1, 64**2
2524 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002525 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002526 green = pickler.dumps(orig, proto)
2527 derived = unpickler.loads(green)
2528 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002529
2530 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002531 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002532 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002533 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002534 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002535 green = pickler.dumps(orig, proto)
2536 derived = unpickler.loads(green)
2537 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002538 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002539 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2540 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002541
2542 def test_extreme_hashes(self):
2543 # If an attempt is made to hash these via subtracting the offset
2544 # then hashing a datetime object, OverflowError results. The
2545 # Python implementation used to blow up here.
2546 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2547 hash(t)
2548 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2549 tzinfo=FixedOffset(-1439, ""))
2550 hash(t)
2551
2552 # OTOH, an OOB offset should blow up.
2553 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2554 self.assertRaises(ValueError, hash, t)
2555
2556 def test_zones(self):
2557 est = FixedOffset(-300, "EST")
2558 utc = FixedOffset(0, "UTC")
2559 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002560 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2561 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2562 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002563 self.assertEqual(t1.tzinfo, est)
2564 self.assertEqual(t2.tzinfo, utc)
2565 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002566 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2567 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2568 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002569 self.assertEqual(t1.tzname(), "EST")
2570 self.assertEqual(t2.tzname(), "UTC")
2571 self.assertEqual(t3.tzname(), "MET")
2572 self.assertEqual(hash(t1), hash(t2))
2573 self.assertEqual(hash(t1), hash(t3))
2574 self.assertEqual(hash(t2), hash(t3))
2575 self.assertEqual(t1, t2)
2576 self.assertEqual(t1, t3)
2577 self.assertEqual(t2, t3)
2578 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2579 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2580 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002581 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002582 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2583 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2584 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2585
2586 def test_combine(self):
2587 met = FixedOffset(60, "MET")
2588 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002589 tz = time(18, 45, 3, 1234, tzinfo=met)
2590 dt = datetime.combine(d, tz)
2591 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002592 tzinfo=met))
2593
2594 def test_extract(self):
2595 met = FixedOffset(60, "MET")
2596 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2597 self.assertEqual(dt.date(), date(2002, 3, 4))
2598 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002599 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002600
2601 def test_tz_aware_arithmetic(self):
2602 import random
2603
2604 now = self.theclass.now()
2605 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002606 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002607 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002608 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002609 self.assertEqual(nowaware.timetz(), timeaware)
2610
2611 # Can't mix aware and non-aware.
2612 self.assertRaises(TypeError, lambda: now - nowaware)
2613 self.assertRaises(TypeError, lambda: nowaware - now)
2614
Tim Peters0bf60bd2003-01-08 20:40:01 +00002615 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002616 self.assertRaises(TypeError, lambda: now + nowaware)
2617 self.assertRaises(TypeError, lambda: nowaware + now)
2618 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2619
2620 # Subtracting should yield 0.
2621 self.assertEqual(now - now, timedelta(0))
2622 self.assertEqual(nowaware - nowaware, timedelta(0))
2623
2624 # Adding a delta should preserve tzinfo.
2625 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2626 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002627 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002628 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002629 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002630 self.assertEqual(nowawareplus, nowawareplus2)
2631
2632 # that - delta should be what we started with, and that - what we
2633 # started with should be delta.
2634 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002635 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002636 self.assertEqual(nowaware, diff)
2637 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2638 self.assertEqual(nowawareplus - nowaware, delta)
2639
2640 # Make up a random timezone.
2641 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002642 # Attach it to nowawareplus.
2643 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002644 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002645 # Make sure the difference takes the timezone adjustments into account.
2646 got = nowaware - nowawareplus
2647 # Expected: (nowaware base - nowaware offset) -
2648 # (nowawareplus base - nowawareplus offset) =
2649 # (nowaware base - nowawareplus base) +
2650 # (nowawareplus offset - nowaware offset) =
2651 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002652 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002653 self.assertEqual(got, expected)
2654
2655 # Try max possible difference.
2656 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2657 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2658 tzinfo=FixedOffset(-1439, "max"))
2659 maxdiff = max - min
2660 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2661 timedelta(minutes=2*1439))
2662
2663 def test_tzinfo_now(self):
2664 meth = self.theclass.now
2665 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2666 base = meth()
2667 # Try with and without naming the keyword.
2668 off42 = FixedOffset(42, "42")
2669 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002670 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002671 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002672 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002673 # Bad argument with and w/o naming the keyword.
2674 self.assertRaises(TypeError, meth, 16)
2675 self.assertRaises(TypeError, meth, tzinfo=16)
2676 # Bad keyword name.
2677 self.assertRaises(TypeError, meth, tinfo=off42)
2678 # Too many args.
2679 self.assertRaises(TypeError, meth, off42, off42)
2680
Tim Peters10cadce2003-01-23 19:58:02 +00002681 # We don't know which time zone we're in, and don't have a tzinfo
2682 # class to represent it, so seeing whether a tz argument actually
2683 # does a conversion is tricky.
2684 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2685 utc = FixedOffset(0, "utc", 0)
2686 for dummy in range(3):
2687 now = datetime.now(weirdtz)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002688 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002689 utcnow = datetime.utcnow().replace(tzinfo=utc)
2690 now2 = utcnow.astimezone(weirdtz)
2691 if abs(now - now2) < timedelta(seconds=30):
2692 break
2693 # Else the code is broken, or more than 30 seconds passed between
2694 # calls; assuming the latter, just try again.
2695 else:
2696 # Three strikes and we're out.
2697 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2698
Tim Peters2a799bf2002-12-16 20:18:38 +00002699 def test_tzinfo_fromtimestamp(self):
2700 import time
2701 meth = self.theclass.fromtimestamp
2702 ts = time.time()
2703 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2704 base = meth(ts)
2705 # Try with and without naming the keyword.
2706 off42 = FixedOffset(42, "42")
2707 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002708 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002709 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002710 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002711 # Bad argument with and w/o naming the keyword.
2712 self.assertRaises(TypeError, meth, ts, 16)
2713 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2714 # Bad keyword name.
2715 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2716 # Too many args.
2717 self.assertRaises(TypeError, meth, ts, off42, off42)
2718 # Too few args.
2719 self.assertRaises(TypeError, meth)
2720
Tim Peters2a44a8d2003-01-23 20:53:10 +00002721 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002722 timestamp = 1000000000
2723 utcdatetime = datetime.utcfromtimestamp(timestamp)
2724 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2725 # But on some flavor of Mac, it's nowhere near that. So we can't have
2726 # any idea here what time that actually is, we can only test that
2727 # relative changes match.
2728 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2729 tz = FixedOffset(utcoffset, "tz", 0)
2730 expected = utcdatetime + utcoffset
2731 got = datetime.fromtimestamp(timestamp, tz)
2732 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002733
Tim Peters2a799bf2002-12-16 20:18:38 +00002734 def test_tzinfo_utcnow(self):
2735 meth = self.theclass.utcnow
2736 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2737 base = meth()
2738 # Try with and without naming the keyword; for whatever reason,
2739 # utcnow() doesn't accept a tzinfo argument.
2740 off42 = FixedOffset(42, "42")
2741 self.assertRaises(TypeError, meth, off42)
2742 self.assertRaises(TypeError, meth, tzinfo=off42)
2743
2744 def test_tzinfo_utcfromtimestamp(self):
2745 import time
2746 meth = self.theclass.utcfromtimestamp
2747 ts = time.time()
2748 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2749 base = meth(ts)
2750 # Try with and without naming the keyword; for whatever reason,
2751 # utcfromtimestamp() doesn't accept a tzinfo argument.
2752 off42 = FixedOffset(42, "42")
2753 self.assertRaises(TypeError, meth, ts, off42)
2754 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2755
2756 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002757 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002758 # DST flag.
2759 class DST(tzinfo):
2760 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002761 if isinstance(dstvalue, int):
2762 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002763 self.dstvalue = dstvalue
2764 def dst(self, dt):
2765 return self.dstvalue
2766
2767 cls = self.theclass
2768 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2769 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2770 t = d.timetuple()
2771 self.assertEqual(1, t.tm_year)
2772 self.assertEqual(1, t.tm_mon)
2773 self.assertEqual(1, t.tm_mday)
2774 self.assertEqual(10, t.tm_hour)
2775 self.assertEqual(20, t.tm_min)
2776 self.assertEqual(30, t.tm_sec)
2777 self.assertEqual(0, t.tm_wday)
2778 self.assertEqual(1, t.tm_yday)
2779 self.assertEqual(flag, t.tm_isdst)
2780
2781 # dst() returns wrong type.
2782 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2783
2784 # dst() at the edge.
2785 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2786 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2787
2788 # dst() out of range.
2789 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2790 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2791
2792 def test_utctimetuple(self):
2793 class DST(tzinfo):
2794 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002795 if isinstance(dstvalue, int):
2796 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002797 self.dstvalue = dstvalue
2798 def dst(self, dt):
2799 return self.dstvalue
2800
2801 cls = self.theclass
2802 # This can't work: DST didn't implement utcoffset.
2803 self.assertRaises(NotImplementedError,
2804 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2805
2806 class UOFS(DST):
2807 def __init__(self, uofs, dofs=None):
2808 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002809 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002810 def utcoffset(self, dt):
2811 return self.uofs
2812
2813 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2814 # in effect for a UTC time.
2815 for dstvalue in -33, 33, 0, None:
2816 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2817 t = d.utctimetuple()
2818 self.assertEqual(d.year, t.tm_year)
2819 self.assertEqual(d.month, t.tm_mon)
2820 self.assertEqual(d.day, t.tm_mday)
2821 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2822 self.assertEqual(13, t.tm_min)
2823 self.assertEqual(d.second, t.tm_sec)
2824 self.assertEqual(d.weekday(), t.tm_wday)
2825 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2826 t.tm_yday)
2827 self.assertEqual(0, t.tm_isdst)
2828
2829 # At the edges, UTC adjustment can normalize into years out-of-range
2830 # for a datetime object. Ensure that a correct timetuple is
2831 # created anyway.
2832 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2833 # That goes back 1 minute less than a full day.
2834 t = tiny.utctimetuple()
2835 self.assertEqual(t.tm_year, MINYEAR-1)
2836 self.assertEqual(t.tm_mon, 12)
2837 self.assertEqual(t.tm_mday, 31)
2838 self.assertEqual(t.tm_hour, 0)
2839 self.assertEqual(t.tm_min, 1)
2840 self.assertEqual(t.tm_sec, 37)
2841 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2842 self.assertEqual(t.tm_isdst, 0)
2843
2844 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2845 # That goes forward 1 minute less than a full day.
2846 t = huge.utctimetuple()
2847 self.assertEqual(t.tm_year, MAXYEAR+1)
2848 self.assertEqual(t.tm_mon, 1)
2849 self.assertEqual(t.tm_mday, 1)
2850 self.assertEqual(t.tm_hour, 23)
2851 self.assertEqual(t.tm_min, 58)
2852 self.assertEqual(t.tm_sec, 37)
2853 self.assertEqual(t.tm_yday, 1)
2854 self.assertEqual(t.tm_isdst, 0)
2855
2856 def test_tzinfo_isoformat(self):
2857 zero = FixedOffset(0, "+00:00")
2858 plus = FixedOffset(220, "+03:40")
2859 minus = FixedOffset(-231, "-03:51")
2860 unknown = FixedOffset(None, "")
2861
2862 cls = self.theclass
2863 datestr = '0001-02-03'
2864 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002865 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002866 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2867 timestr = '04:05:59' + (us and '.987001' or '')
2868 ofsstr = ofs is not None and d.tzname() or ''
2869 tailstr = timestr + ofsstr
2870 iso = d.isoformat()
2871 self.assertEqual(iso, datestr + 'T' + tailstr)
2872 self.assertEqual(iso, d.isoformat('T'))
2873 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002874 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002875 self.assertEqual(str(d), datestr + ' ' + tailstr)
2876
Tim Peters12bf3392002-12-24 05:41:27 +00002877 def test_replace(self):
2878 cls = self.theclass
2879 z100 = FixedOffset(100, "+100")
2880 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2881 args = [1, 2, 3, 4, 5, 6, 7, z100]
2882 base = cls(*args)
2883 self.assertEqual(base, base.replace())
2884
2885 i = 0
2886 for name, newval in (("year", 2),
2887 ("month", 3),
2888 ("day", 4),
2889 ("hour", 5),
2890 ("minute", 6),
2891 ("second", 7),
2892 ("microsecond", 8),
2893 ("tzinfo", zm200)):
2894 newargs = args[:]
2895 newargs[i] = newval
2896 expected = cls(*newargs)
2897 got = base.replace(**{name: newval})
2898 self.assertEqual(expected, got)
2899 i += 1
2900
2901 # Ensure we can get rid of a tzinfo.
2902 self.assertEqual(base.tzname(), "+100")
2903 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002904 self.assertTrue(base2.tzinfo is None)
2905 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002906
2907 # Ensure we can add one.
2908 base3 = base2.replace(tzinfo=z100)
2909 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002910 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002911
2912 # Out of bounds.
2913 base = cls(2000, 2, 29)
2914 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002915
Tim Peters80475bb2002-12-25 07:40:55 +00002916 def test_more_astimezone(self):
2917 # The inherited test_astimezone covered some trivial and error cases.
2918 fnone = FixedOffset(None, "None")
2919 f44m = FixedOffset(44, "44")
2920 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2921
Tim Peters10cadce2003-01-23 19:58:02 +00002922 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002923 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002924 # Replacing with degenerate tzinfo raises an exception.
2925 self.assertRaises(ValueError, dt.astimezone, fnone)
2926 # Ditto with None tz.
2927 self.assertRaises(TypeError, dt.astimezone, None)
2928 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002929 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002930 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002931 self.assertEqual(x.date(), dt.date())
2932 self.assertEqual(x.time(), dt.time())
2933
2934 # Replacing with different tzinfo does adjust.
2935 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002936 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002937 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2938 expected = dt - dt.utcoffset() # in effect, convert to UTC
2939 expected += fm5h.utcoffset(dt) # and from there to local time
2940 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2941 self.assertEqual(got.date(), expected.date())
2942 self.assertEqual(got.time(), expected.time())
2943 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002944 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002945 self.assertEqual(got, expected)
2946
Tim Peters4c0db782002-12-26 05:01:19 +00002947 def test_aware_subtract(self):
2948 cls = self.theclass
2949
Tim Peters60c76e42002-12-27 00:41:11 +00002950 # Ensure that utcoffset() is ignored when the operands have the
2951 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002952 class OperandDependentOffset(tzinfo):
2953 def utcoffset(self, t):
2954 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002955 # d0 and d1 equal after adjustment
2956 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002957 else:
Tim Peters397301e2003-01-02 21:28:08 +00002958 # d2 off in the weeds
2959 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002960
2961 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2962 d0 = base.replace(minute=3)
2963 d1 = base.replace(minute=9)
2964 d2 = base.replace(minute=11)
2965 for x in d0, d1, d2:
2966 for y in d0, d1, d2:
2967 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002968 expected = timedelta(minutes=x.minute - y.minute)
2969 self.assertEqual(got, expected)
2970
2971 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2972 # ignored.
2973 base = cls(8, 9, 10, 11, 12, 13, 14)
2974 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2975 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2976 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2977 for x in d0, d1, d2:
2978 for y in d0, d1, d2:
2979 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002980 if (x is d0 or x is d1) and (y is d0 or y is d1):
2981 expected = timedelta(0)
2982 elif x is y is d2:
2983 expected = timedelta(0)
2984 elif x is d2:
2985 expected = timedelta(minutes=(11-59)-0)
2986 else:
2987 assert y is d2
2988 expected = timedelta(minutes=0-(11-59))
2989 self.assertEqual(got, expected)
2990
Tim Peters60c76e42002-12-27 00:41:11 +00002991 def test_mixed_compare(self):
2992 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002993 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002994 self.assertEqual(t1, t2)
2995 t2 = t2.replace(tzinfo=None)
2996 self.assertEqual(t1, t2)
2997 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2998 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002999 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3000 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003001
Tim Peters0bf60bd2003-01-08 20:40:01 +00003002 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003003 class Varies(tzinfo):
3004 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003005 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003006 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003007 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003008 return self.offset
3009
3010 v = Varies()
3011 t1 = t2.replace(tzinfo=v)
3012 t2 = t2.replace(tzinfo=v)
3013 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3014 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3015 self.assertEqual(t1, t2)
3016
3017 # But if they're not identical, it isn't ignored.
3018 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003019 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003020
Tim Petersa98924a2003-05-17 05:55:19 +00003021 def test_subclass_datetimetz(self):
3022
3023 class C(self.theclass):
3024 theAnswer = 42
3025
3026 def __new__(cls, *args, **kws):
3027 temp = kws.copy()
3028 extra = temp.pop('extra')
3029 result = self.theclass.__new__(cls, *args, **temp)
3030 result.extra = extra
3031 return result
3032
3033 def newmeth(self, start):
3034 return start + self.hour + self.year
3035
3036 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3037
3038 dt1 = self.theclass(*args)
3039 dt2 = C(*args, **{'extra': 7})
3040
3041 self.assertEqual(dt2.__class__, C)
3042 self.assertEqual(dt2.theAnswer, 42)
3043 self.assertEqual(dt2.extra, 7)
3044 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3045 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3046
Tim Peters621818b2002-12-29 23:44:49 +00003047# Pain to set up DST-aware tzinfo classes.
3048
3049def first_sunday_on_or_after(dt):
3050 days_to_go = 6 - dt.weekday()
3051 if days_to_go:
3052 dt += timedelta(days_to_go)
3053 return dt
3054
3055ZERO = timedelta(0)
3056HOUR = timedelta(hours=1)
3057DAY = timedelta(days=1)
3058# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3059DSTSTART = datetime(1, 4, 1, 2)
3060# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003061# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3062# being standard time on that day, there is no spelling in local time of
3063# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3064DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003065
3066class USTimeZone(tzinfo):
3067
3068 def __init__(self, hours, reprname, stdname, dstname):
3069 self.stdoffset = timedelta(hours=hours)
3070 self.reprname = reprname
3071 self.stdname = stdname
3072 self.dstname = dstname
3073
3074 def __repr__(self):
3075 return self.reprname
3076
3077 def tzname(self, dt):
3078 if self.dst(dt):
3079 return self.dstname
3080 else:
3081 return self.stdname
3082
3083 def utcoffset(self, dt):
3084 return self.stdoffset + self.dst(dt)
3085
3086 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003087 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003088 # An exception instead may be sensible here, in one or more of
3089 # the cases.
3090 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003091 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003092
3093 # Find first Sunday in April.
3094 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3095 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3096
3097 # Find last Sunday in October.
3098 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3099 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3100
Tim Peters621818b2002-12-29 23:44:49 +00003101 # Can't compare naive to aware objects, so strip the timezone from
3102 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003103 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003104 return HOUR
3105 else:
3106 return ZERO
3107
Tim Peters521fc152002-12-31 17:36:56 +00003108Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3109Central = USTimeZone(-6, "Central", "CST", "CDT")
3110Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3111Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003112utc_real = FixedOffset(0, "UTC", 0)
3113# For better test coverage, we want another flavor of UTC that's west of
3114# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003115utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003116
3117class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003118 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003119 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003120 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003121
Tim Peters0bf60bd2003-01-08 20:40:01 +00003122 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003123
Tim Peters521fc152002-12-31 17:36:56 +00003124 # Check a time that's inside DST.
3125 def checkinside(self, dt, tz, utc, dston, dstoff):
3126 self.assertEqual(dt.dst(), HOUR)
3127
3128 # Conversion to our own timezone is always an identity.
3129 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003130
3131 asutc = dt.astimezone(utc)
3132 there_and_back = asutc.astimezone(tz)
3133
3134 # Conversion to UTC and back isn't always an identity here,
3135 # because there are redundant spellings (in local time) of
3136 # UTC time when DST begins: the clock jumps from 1:59:59
3137 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3138 # make sense then. The classes above treat 2:MM:SS as
3139 # daylight time then (it's "after 2am"), really an alias
3140 # for 1:MM:SS standard time. The latter form is what
3141 # conversion back from UTC produces.
3142 if dt.date() == dston.date() and dt.hour == 2:
3143 # We're in the redundant hour, and coming back from
3144 # UTC gives the 1:MM:SS standard-time spelling.
3145 self.assertEqual(there_and_back + HOUR, dt)
3146 # Although during was considered to be in daylight
3147 # time, there_and_back is not.
3148 self.assertEqual(there_and_back.dst(), ZERO)
3149 # They're the same times in UTC.
3150 self.assertEqual(there_and_back.astimezone(utc),
3151 dt.astimezone(utc))
3152 else:
3153 # We're not in the redundant hour.
3154 self.assertEqual(dt, there_and_back)
3155
Tim Peters327098a2003-01-20 22:54:38 +00003156 # Because we have a redundant spelling when DST begins, there is
3157 # (unforunately) an hour when DST ends that can't be spelled at all in
3158 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3159 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3160 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3161 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3162 # expressed in local time. Nevertheless, we want conversion back
3163 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003164 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003165 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003166 if dt.date() == dstoff.date() and dt.hour == 0:
3167 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003168 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003169 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3170 nexthour_utc += HOUR
3171 nexthour_tz = nexthour_utc.astimezone(tz)
3172 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003173 else:
Tim Peters327098a2003-01-20 22:54:38 +00003174 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003175
3176 # Check a time that's outside DST.
3177 def checkoutside(self, dt, tz, utc):
3178 self.assertEqual(dt.dst(), ZERO)
3179
3180 # Conversion to our own timezone is always an identity.
3181 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003182
3183 # Converting to UTC and back is an identity too.
3184 asutc = dt.astimezone(utc)
3185 there_and_back = asutc.astimezone(tz)
3186 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003187
Tim Peters1024bf82002-12-30 17:09:40 +00003188 def convert_between_tz_and_utc(self, tz, utc):
3189 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003190 # Because 1:MM on the day DST ends is taken as being standard time,
3191 # there is no spelling in tz for the last hour of daylight time.
3192 # For purposes of the test, the last hour of DST is 0:MM, which is
3193 # taken as being daylight time (and 1:MM is taken as being standard
3194 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003195 dstoff = self.dstoff.replace(tzinfo=tz)
3196 for delta in (timedelta(weeks=13),
3197 DAY,
3198 HOUR,
3199 timedelta(minutes=1),
3200 timedelta(microseconds=1)):
3201
Tim Peters521fc152002-12-31 17:36:56 +00003202 self.checkinside(dston, tz, utc, dston, dstoff)
3203 for during in dston + delta, dstoff - delta:
3204 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003205
Tim Peters521fc152002-12-31 17:36:56 +00003206 self.checkoutside(dstoff, tz, utc)
3207 for outside in dston - delta, dstoff + delta:
3208 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003209
Tim Peters621818b2002-12-29 23:44:49 +00003210 def test_easy(self):
3211 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003212 self.convert_between_tz_and_utc(Eastern, utc_real)
3213 self.convert_between_tz_and_utc(Pacific, utc_real)
3214 self.convert_between_tz_and_utc(Eastern, utc_fake)
3215 self.convert_between_tz_and_utc(Pacific, utc_fake)
3216 # The next is really dancing near the edge. It works because
3217 # Pacific and Eastern are far enough apart that their "problem
3218 # hours" don't overlap.
3219 self.convert_between_tz_and_utc(Eastern, Pacific)
3220 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003221 # OTOH, these fail! Don't enable them. The difficulty is that
3222 # the edge case tests assume that every hour is representable in
3223 # the "utc" class. This is always true for a fixed-offset tzinfo
3224 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3225 # For these adjacent DST-aware time zones, the range of time offsets
3226 # tested ends up creating hours in the one that aren't representable
3227 # in the other. For the same reason, we would see failures in the
3228 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3229 # offset deltas in convert_between_tz_and_utc().
3230 #
3231 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3232 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003233
Tim Petersf3615152003-01-01 21:51:37 +00003234 def test_tricky(self):
3235 # 22:00 on day before daylight starts.
3236 fourback = self.dston - timedelta(hours=4)
3237 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003238 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003239 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3240 # 2", we should get the 3 spelling.
3241 # If we plug 22:00 the day before into Eastern, it "looks like std
3242 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3243 # to 22:00 lands on 2:00, which makes no sense in local time (the
3244 # local clock jumps from 1 to 3). The point here is to make sure we
3245 # get the 3 spelling.
3246 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003247 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003248 self.assertEqual(expected, got)
3249
3250 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3251 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003252 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003253 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3254 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3255 # spelling.
3256 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003257 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003258 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003259
Tim Petersadf64202003-01-04 06:03:15 +00003260 # Now on the day DST ends, we want "repeat an hour" behavior.
3261 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3262 # EST 23:MM 0:MM 1:MM 2:MM
3263 # EDT 0:MM 1:MM 2:MM 3:MM
3264 # wall 0:MM 1:MM 1:MM 2:MM against these
3265 for utc in utc_real, utc_fake:
3266 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003267 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003268 # Convert that to UTC.
3269 first_std_hour -= tz.utcoffset(None)
3270 # Adjust for possibly fake UTC.
3271 asutc = first_std_hour + utc.utcoffset(None)
3272 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3273 # tz=Eastern.
3274 asutcbase = asutc.replace(tzinfo=utc)
3275 for tzhour in (0, 1, 1, 2):
3276 expectedbase = self.dstoff.replace(hour=tzhour)
3277 for minute in 0, 30, 59:
3278 expected = expectedbase.replace(minute=minute)
3279 asutc = asutcbase.replace(minute=minute)
3280 astz = asutc.astimezone(tz)
3281 self.assertEqual(astz.replace(tzinfo=None), expected)
3282 asutcbase += HOUR
3283
3284
Tim Peters710fb152003-01-02 19:35:54 +00003285 def test_bogus_dst(self):
3286 class ok(tzinfo):
3287 def utcoffset(self, dt): return HOUR
3288 def dst(self, dt): return HOUR
3289
3290 now = self.theclass.now().replace(tzinfo=utc_real)
3291 # Doesn't blow up.
3292 now.astimezone(ok())
3293
3294 # Does blow up.
3295 class notok(ok):
3296 def dst(self, dt): return None
3297 self.assertRaises(ValueError, now.astimezone, notok())
3298
Tim Peters52dcce22003-01-23 16:36:11 +00003299 def test_fromutc(self):
3300 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3301 now = datetime.utcnow().replace(tzinfo=utc_real)
3302 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3303 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3304 enow = Eastern.fromutc(now) # doesn't blow up
3305 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3306 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3307 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3308
3309 # Always converts UTC to standard time.
3310 class FauxUSTimeZone(USTimeZone):
3311 def fromutc(self, dt):
3312 return dt + self.stdoffset
3313 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3314
3315 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3316 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3317 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3318
3319 # Check around DST start.
3320 start = self.dston.replace(hour=4, tzinfo=Eastern)
3321 fstart = start.replace(tzinfo=FEastern)
3322 for wall in 23, 0, 1, 3, 4, 5:
3323 expected = start.replace(hour=wall)
3324 if wall == 23:
3325 expected -= timedelta(days=1)
3326 got = Eastern.fromutc(start)
3327 self.assertEqual(expected, got)
3328
3329 expected = fstart + FEastern.stdoffset
3330 got = FEastern.fromutc(fstart)
3331 self.assertEqual(expected, got)
3332
3333 # Ensure astimezone() calls fromutc() too.
3334 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3335 self.assertEqual(expected, got)
3336
3337 start += HOUR
3338 fstart += HOUR
3339
3340 # Check around DST end.
3341 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3342 fstart = start.replace(tzinfo=FEastern)
3343 for wall in 0, 1, 1, 2, 3, 4:
3344 expected = start.replace(hour=wall)
3345 got = Eastern.fromutc(start)
3346 self.assertEqual(expected, got)
3347
3348 expected = fstart + FEastern.stdoffset
3349 got = FEastern.fromutc(fstart)
3350 self.assertEqual(expected, got)
3351
3352 # Ensure astimezone() calls fromutc() too.
3353 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3354 self.assertEqual(expected, got)
3355
3356 start += HOUR
3357 fstart += HOUR
3358
Tim Peters710fb152003-01-02 19:35:54 +00003359
Tim Peters528ca532004-09-16 01:30:50 +00003360#############################################################################
3361# oddballs
3362
3363class Oddballs(unittest.TestCase):
3364
3365 def test_bug_1028306(self):
3366 # Trying to compare a date to a datetime should act like a mixed-
3367 # type comparison, despite that datetime is a subclass of date.
3368 as_date = date.today()
3369 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003370 self.assertTrue(as_date != as_datetime)
3371 self.assertTrue(as_datetime != as_date)
3372 self.assertTrue(not as_date == as_datetime)
3373 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003374 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3375 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3376 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3377 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3378 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3379 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3380 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3381 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3382
3383 # Neverthelss, comparison should work with the base-class (date)
3384 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003385 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003386 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003387 as_different = as_datetime.replace(day= different_day)
3388 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003389
3390 # And date should compare with other subclasses of date. If a
3391 # subclass wants to stop this, it's up to the subclass to do so.
3392 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3393 self.assertEqual(as_date, date_sc)
3394 self.assertEqual(date_sc, as_date)
3395
3396 # Ditto for datetimes.
3397 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3398 as_date.day, 0, 0, 0)
3399 self.assertEqual(as_datetime, datetime_sc)
3400 self.assertEqual(datetime_sc, as_datetime)
3401
Tim Peters2a799bf2002-12-16 20:18:38 +00003402def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003403 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003404
3405if __name__ == "__main__":
3406 test_main()