blob: 7c5fa9f255863f6e56ca8588ae4f3560c5aa3e15 [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
Alexander Belopolskyd5442cd2010-05-26 20:00:12 +00006import sys
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
Alexander Belopolsky1790bc42010-05-31 17:33:47 +000028# XXX Copied from test_float.
29INF = float("inf")
30NAN = float("nan")
31
32# decorator for skipping tests on non-IEEE 754 platforms
33requires_IEEE_754 = unittest.skipUnless(
34 float.__getformat__("double").startswith("IEEE"),
35 "test requires IEEE 754 doubles")
36
37
Tim Peters2a799bf2002-12-16 20:18:38 +000038#############################################################################
39# module tests
40
41class TestModule(unittest.TestCase):
42
43 def test_constants(self):
44 import datetime
45 self.assertEqual(datetime.MINYEAR, 1)
46 self.assertEqual(datetime.MAXYEAR, 9999)
47
48#############################################################################
49# tzinfo tests
50
51class FixedOffset(tzinfo):
52 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000053 if isinstance(offset, int):
54 offset = timedelta(minutes=offset)
55 if isinstance(dstoffset, int):
56 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000057 self.__offset = offset
58 self.__name = name
59 self.__dstoffset = dstoffset
60 def __repr__(self):
61 return self.__name.lower()
62 def utcoffset(self, dt):
63 return self.__offset
64 def tzname(self, dt):
65 return self.__name
66 def dst(self, dt):
67 return self.__dstoffset
68
Tim Petersfb8472c2002-12-21 05:04:42 +000069class PicklableFixedOffset(FixedOffset):
70 def __init__(self, offset=None, name=None, dstoffset=None):
71 FixedOffset.__init__(self, offset, name, dstoffset)
72
Tim Peters2a799bf2002-12-16 20:18:38 +000073class TestTZInfo(unittest.TestCase):
74
75 def test_non_abstractness(self):
76 # In order to allow subclasses to get pickled, the C implementation
77 # wasn't able to get away with having __init__ raise
78 # NotImplementedError.
79 useless = tzinfo()
80 dt = datetime.max
81 self.assertRaises(NotImplementedError, useless.tzname, dt)
82 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
83 self.assertRaises(NotImplementedError, useless.dst, dt)
84
85 def test_subclass_must_override(self):
86 class NotEnough(tzinfo):
87 def __init__(self, offset, name):
88 self.__offset = offset
89 self.__name = name
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000090 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000091 ne = NotEnough(3, "NotByALongShot")
Ezio Melottie9615932010-01-24 19:26:24 +000092 self.assertIsInstance(ne, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000093
94 dt = datetime.now()
95 self.assertRaises(NotImplementedError, ne.tzname, dt)
96 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
97 self.assertRaises(NotImplementedError, ne.dst, dt)
98
99 def test_normal(self):
100 fo = FixedOffset(3, "Three")
Ezio Melottie9615932010-01-24 19:26:24 +0000101 self.assertIsInstance(fo, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000102 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000103 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000104 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000105 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000106
107 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000108 # There's no point to pickling tzinfo objects on their own (they
109 # carry no data), but they need to be picklable anyway else
110 # concrete subclasses can't be pickled.
111 orig = tzinfo.__new__(tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000112 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000113 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000114 green = pickler.dumps(orig, proto)
115 derived = unpickler.loads(green)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000116 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000117
118 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000119 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000120 offset = timedelta(minutes=-300)
121 orig = PicklableFixedOffset(offset, 'cookie')
Ezio Melottie9615932010-01-24 19:26:24 +0000122 self.assertIsInstance(orig, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000123 self.assertTrue(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000124 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000125 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000126 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000127 green = pickler.dumps(orig, proto)
128 derived = unpickler.loads(green)
Ezio Melottie9615932010-01-24 19:26:24 +0000129 self.assertIsInstance(derived, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000130 self.assertTrue(type(derived) is PicklableFixedOffset)
Tim Petersf2715e02003-02-19 02:35:07 +0000131 self.assertEqual(derived.utcoffset(None), offset)
132 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000133
134#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000135# Base clase for testing a particular aspect of timedelta, time, date and
136# datetime comparisons.
137
Guido van Rossumd8faa362007-04-27 19:54:29 +0000138class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000139 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
140
141 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
142 # legit constructor.
143
144 def test_harmless_mixed_comparison(self):
145 me = self.theclass(1, 1, 1)
146
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000147 self.assertFalse(me == ())
148 self.assertTrue(me != ())
149 self.assertFalse(() == me)
150 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000151
Benjamin Peterson577473f2010-01-19 00:09:57 +0000152 self.assertIn(me, [1, 20, [], me])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000153 self.assertIn([], [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000154
155 def test_harmful_mixed_comparison(self):
156 me = self.theclass(1, 1, 1)
157
158 self.assertRaises(TypeError, lambda: me < ())
159 self.assertRaises(TypeError, lambda: me <= ())
160 self.assertRaises(TypeError, lambda: me > ())
161 self.assertRaises(TypeError, lambda: me >= ())
162
163 self.assertRaises(TypeError, lambda: () < me)
164 self.assertRaises(TypeError, lambda: () <= me)
165 self.assertRaises(TypeError, lambda: () > me)
166 self.assertRaises(TypeError, lambda: () >= me)
167
Tim Peters07534a62003-02-07 22:50:28 +0000168#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000169# timedelta tests
170
Guido van Rossumd8faa362007-04-27 19:54:29 +0000171class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000172
173 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000174
175 def test_constructor(self):
176 eq = self.assertEqual
177 td = timedelta
178
179 # Check keyword args to constructor
180 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
181 milliseconds=0, microseconds=0))
182 eq(td(1), td(days=1))
183 eq(td(0, 1), td(seconds=1))
184 eq(td(0, 0, 1), td(microseconds=1))
185 eq(td(weeks=1), td(days=7))
186 eq(td(days=1), td(hours=24))
187 eq(td(hours=1), td(minutes=60))
188 eq(td(minutes=1), td(seconds=60))
189 eq(td(seconds=1), td(milliseconds=1000))
190 eq(td(milliseconds=1), td(microseconds=1000))
191
192 # Check float args to constructor
193 eq(td(weeks=1.0/7), td(days=1))
194 eq(td(days=1.0/24), td(hours=1))
195 eq(td(hours=1.0/60), td(minutes=1))
196 eq(td(minutes=1.0/60), td(seconds=1))
197 eq(td(seconds=0.001), td(milliseconds=1))
198 eq(td(milliseconds=0.001), td(microseconds=1))
199
200 def test_computations(self):
201 eq = self.assertEqual
202 td = timedelta
203
204 a = td(7) # One week
205 b = td(0, 60) # One minute
206 c = td(0, 0, 1000) # One millisecond
207 eq(a+b+c, td(7, 60, 1000))
208 eq(a-b, td(6, 24*3600 - 60))
209 eq(-a, td(-7))
210 eq(+a, td(7))
211 eq(-b, td(-1, 24*3600 - 60))
212 eq(-c, td(-1, 24*3600 - 1, 999000))
213 eq(abs(a), a)
214 eq(abs(-a), a)
215 eq(td(6, 24*3600), a)
216 eq(td(0, 0, 60*1000000), b)
217 eq(a*10, td(70))
218 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000219 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000220 eq(b*10, td(0, 600))
221 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000222 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000223 eq(c*10, td(0, 0, 10000))
224 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000225 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000226 eq(a*-1, -a)
227 eq(b*-2, -b-b)
228 eq(c*-2, -c+-c)
229 eq(b*(60*24), (b*60)*24)
230 eq(b*(60*24), (60*b)*24)
231 eq(c*1000, td(0, 1))
232 eq(1000*c, td(0, 1))
233 eq(a//7, td(1))
234 eq(b//10, td(0, 6))
235 eq(c//1000, td(0, 0, 1))
236 eq(a//10, td(0, 7*24*360))
237 eq(a//3600000, td(0, 0, 7*24*1000))
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000238 eq(a/0.5, td(14))
239 eq(b/0.5, td(0, 120))
240 eq(a/7, td(1))
241 eq(b/10, td(0, 6))
242 eq(c/1000, td(0, 0, 1))
243 eq(a/10, td(0, 7*24*360))
244 eq(a/3600000, td(0, 0, 7*24*1000))
245
246 # Multiplication by float
247 us = td(microseconds=1)
248 eq((3*us) * 0.5, 2*us)
249 eq((5*us) * 0.5, 2*us)
250 eq(0.5 * (3*us), 2*us)
251 eq(0.5 * (5*us), 2*us)
252 eq((-3*us) * 0.5, -2*us)
253 eq((-5*us) * 0.5, -2*us)
254
255 # Division by int and float
256 eq((3*us) / 2, 2*us)
257 eq((5*us) / 2, 2*us)
258 eq((-3*us) / 2.0, -2*us)
259 eq((-5*us) / 2.0, -2*us)
260 eq((3*us) / -2, -2*us)
261 eq((5*us) / -2, -2*us)
262 eq((3*us) / -2.0, -2*us)
263 eq((5*us) / -2.0, -2*us)
264 for i in range(-10, 10):
265 eq((i*us/3)//us, round(i/3))
266 for i in range(-10, 10):
267 eq((i*us/-3)//us, round(i/-3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000268
269 def test_disallowed_computations(self):
270 a = timedelta(42)
271
Mark Dickinson5c2db372009-12-05 20:28:34 +0000272 # Add/sub ints or floats should be illegal
273 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000274 self.assertRaises(TypeError, lambda: a+i)
275 self.assertRaises(TypeError, lambda: a-i)
276 self.assertRaises(TypeError, lambda: i+a)
277 self.assertRaises(TypeError, lambda: i-a)
278
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000279 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000280 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000281 zero = 0
282 self.assertRaises(TypeError, lambda: zero // a)
283 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000284 self.assertRaises(ZeroDivisionError, lambda: a / zero)
285 self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
286
287 @requires_IEEE_754
288 def test_disallowed_special(self):
289 a = timedelta(42)
290 self.assertRaises(ValueError, a.__mul__, NAN)
291 self.assertRaises(ValueError, a.__truediv__, NAN)
Tim Peters2a799bf2002-12-16 20:18:38 +0000292
293 def test_basic_attributes(self):
294 days, seconds, us = 1, 7, 31
295 td = timedelta(days, seconds, us)
296 self.assertEqual(td.days, days)
297 self.assertEqual(td.seconds, seconds)
298 self.assertEqual(td.microseconds, us)
299
Antoine Pitroube6859d2009-11-25 23:02:32 +0000300 def test_total_seconds(self):
301 td = timedelta(days=365)
302 self.assertEqual(td.total_seconds(), 31536000.0)
303 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
304 td = timedelta(seconds=total_seconds)
305 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson0381e3f2010-05-08 14:35:02 +0000306 # Issue8644: Test that td.total_seconds() has the same
307 # accuracy as td / timedelta(seconds=1).
308 for ms in [-1, -2, -123]:
309 td = timedelta(microseconds=ms)
310 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
Antoine Pitroube6859d2009-11-25 23:02:32 +0000311
Tim Peters2a799bf2002-12-16 20:18:38 +0000312 def test_carries(self):
313 t1 = timedelta(days=100,
314 weeks=-7,
315 hours=-24*(100-49),
316 minutes=-3,
317 seconds=12,
318 microseconds=(3*60 - 12) * 1e6 + 1)
319 t2 = timedelta(microseconds=1)
320 self.assertEqual(t1, t2)
321
322 def test_hash_equality(self):
323 t1 = timedelta(days=100,
324 weeks=-7,
325 hours=-24*(100-49),
326 minutes=-3,
327 seconds=12,
328 microseconds=(3*60 - 12) * 1000000)
329 t2 = timedelta()
330 self.assertEqual(hash(t1), hash(t2))
331
332 t1 += timedelta(weeks=7)
333 t2 += timedelta(days=7*7)
334 self.assertEqual(t1, t2)
335 self.assertEqual(hash(t1), hash(t2))
336
337 d = {t1: 1}
338 d[t2] = 2
339 self.assertEqual(len(d), 1)
340 self.assertEqual(d[t1], 2)
341
342 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000343 args = 12, 34, 56
344 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000345 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000346 green = pickler.dumps(orig, proto)
347 derived = unpickler.loads(green)
348 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000349
350 def test_compare(self):
351 t1 = timedelta(2, 3, 4)
352 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000353 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000354 self.assertTrue(t1 <= t2)
355 self.assertTrue(t1 >= t2)
356 self.assertTrue(not t1 != t2)
357 self.assertTrue(not t1 < t2)
358 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000359
360 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
361 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000362 self.assertTrue(t1 < t2)
363 self.assertTrue(t2 > t1)
364 self.assertTrue(t1 <= t2)
365 self.assertTrue(t2 >= t1)
366 self.assertTrue(t1 != t2)
367 self.assertTrue(t2 != t1)
368 self.assertTrue(not t1 == t2)
369 self.assertTrue(not t2 == t1)
370 self.assertTrue(not t1 > t2)
371 self.assertTrue(not t2 < t1)
372 self.assertTrue(not t1 >= t2)
373 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000374
Tim Peters68124bb2003-02-08 03:46:31 +0000375 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000376 self.assertEqual(t1 == badarg, False)
377 self.assertEqual(t1 != badarg, True)
378 self.assertEqual(badarg == t1, False)
379 self.assertEqual(badarg != t1, True)
380
Tim Peters2a799bf2002-12-16 20:18:38 +0000381 self.assertRaises(TypeError, lambda: t1 <= badarg)
382 self.assertRaises(TypeError, lambda: t1 < badarg)
383 self.assertRaises(TypeError, lambda: t1 > badarg)
384 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000385 self.assertRaises(TypeError, lambda: badarg <= t1)
386 self.assertRaises(TypeError, lambda: badarg < t1)
387 self.assertRaises(TypeError, lambda: badarg > t1)
388 self.assertRaises(TypeError, lambda: badarg >= t1)
389
390 def test_str(self):
391 td = timedelta
392 eq = self.assertEqual
393
394 eq(str(td(1)), "1 day, 0:00:00")
395 eq(str(td(-1)), "-1 day, 0:00:00")
396 eq(str(td(2)), "2 days, 0:00:00")
397 eq(str(td(-2)), "-2 days, 0:00:00")
398
399 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
400 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
401 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
402 "-210 days, 23:12:34")
403
404 eq(str(td(milliseconds=1)), "0:00:00.001000")
405 eq(str(td(microseconds=3)), "0:00:00.000003")
406
407 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
408 microseconds=999999)),
409 "999999999 days, 23:59:59.999999")
410
411 def test_roundtrip(self):
412 for td in (timedelta(days=999999999, hours=23, minutes=59,
413 seconds=59, microseconds=999999),
414 timedelta(days=-999999999),
415 timedelta(days=1, seconds=2, microseconds=3)):
416
417 # Verify td -> string -> td identity.
418 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000419 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000420 s = s[9:]
421 td2 = eval(s)
422 self.assertEqual(td, td2)
423
424 # Verify identity via reconstructing from pieces.
425 td2 = timedelta(td.days, td.seconds, td.microseconds)
426 self.assertEqual(td, td2)
427
428 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000429 self.assertIsInstance(timedelta.min, timedelta)
430 self.assertIsInstance(timedelta.max, timedelta)
431 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000432 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000433 self.assertEqual(timedelta.min, timedelta(-999999999))
434 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
435 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
436
437 def test_overflow(self):
438 tiny = timedelta.resolution
439
440 td = timedelta.min + tiny
441 td -= tiny # no problem
442 self.assertRaises(OverflowError, td.__sub__, tiny)
443 self.assertRaises(OverflowError, td.__add__, -tiny)
444
445 td = timedelta.max - tiny
446 td += tiny # no problem
447 self.assertRaises(OverflowError, td.__add__, tiny)
448 self.assertRaises(OverflowError, td.__sub__, -tiny)
449
450 self.assertRaises(OverflowError, lambda: -timedelta.max)
451
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000452 day = timedelta(1)
453 self.assertRaises(OverflowError, day.__mul__, 10**9)
454 self.assertRaises(OverflowError, day.__mul__, 1e9)
455 self.assertRaises(OverflowError, day.__truediv__, 1e-20)
456 self.assertRaises(OverflowError, day.__truediv__, 1e-10)
457 self.assertRaises(OverflowError, day.__truediv__, 9e-10)
458
459 @requires_IEEE_754
460 def _test_overflow_special(self):
461 day = timedelta(1)
462 self.assertRaises(OverflowError, day.__mul__, INF)
463 self.assertRaises(OverflowError, day.__mul__, -INF)
464
Tim Peters2a799bf2002-12-16 20:18:38 +0000465 def test_microsecond_rounding(self):
466 td = timedelta
467 eq = self.assertEqual
468
469 # Single-field rounding.
470 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
471 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
472 eq(td(milliseconds=0.6/1000), td(microseconds=1))
473 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
474
475 # Rounding due to contributions from more than one field.
476 us_per_hour = 3600e6
477 us_per_day = us_per_hour * 24
478 eq(td(days=.4/us_per_day), td(0))
479 eq(td(hours=.2/us_per_hour), td(0))
480 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
481
482 eq(td(days=-.4/us_per_day), td(0))
483 eq(td(hours=-.2/us_per_hour), td(0))
484 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
485
486 def test_massive_normalization(self):
487 td = timedelta(microseconds=-1)
488 self.assertEqual((td.days, td.seconds, td.microseconds),
489 (-1, 24*3600-1, 999999))
490
491 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000492 self.assertTrue(timedelta(1))
493 self.assertTrue(timedelta(0, 1))
494 self.assertTrue(timedelta(0, 0, 1))
495 self.assertTrue(timedelta(microseconds=1))
496 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000497
Tim Petersb0c854d2003-05-17 15:57:00 +0000498 def test_subclass_timedelta(self):
499
500 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000501 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000502 def from_td(td):
503 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000504
505 def as_hours(self):
506 sum = (self.days * 24 +
507 self.seconds / 3600.0 +
508 self.microseconds / 3600e6)
509 return round(sum)
510
511 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000512 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000513 self.assertEqual(t1.as_hours(), 24)
514
515 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000516 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000517 self.assertEqual(t2.as_hours(), -25)
518
519 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000520 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000521 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000522 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000523 self.assertEqual(t3.days, t4.days)
524 self.assertEqual(t3.seconds, t4.seconds)
525 self.assertEqual(t3.microseconds, t4.microseconds)
526 self.assertEqual(str(t3), str(t4))
527 self.assertEqual(t4.as_hours(), -1)
528
Mark Dickinson7c186e22010-04-20 22:32:49 +0000529 def test_division(self):
530 t = timedelta(hours=1, minutes=24, seconds=19)
531 second = timedelta(seconds=1)
532 self.assertEqual(t / second, 5059.0)
533 self.assertEqual(t // second, 5059)
534
535 t = timedelta(minutes=2, seconds=30)
536 minute = timedelta(minutes=1)
537 self.assertEqual(t / minute, 2.5)
538 self.assertEqual(t // minute, 2)
539
540 zerotd = timedelta(0)
541 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
542 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
543
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000544 # self.assertRaises(TypeError, truediv, t, 2)
Mark Dickinson7c186e22010-04-20 22:32:49 +0000545 # note: floor division of a timedelta by an integer *is*
546 # currently permitted.
547
548 def test_remainder(self):
549 t = timedelta(minutes=2, seconds=30)
550 minute = timedelta(minutes=1)
551 r = t % minute
552 self.assertEqual(r, timedelta(seconds=30))
553
554 t = timedelta(minutes=-2, seconds=30)
555 r = t % minute
556 self.assertEqual(r, timedelta(seconds=30))
557
558 zerotd = timedelta(0)
559 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
560
561 self.assertRaises(TypeError, mod, t, 10)
562
563 def test_divmod(self):
564 t = timedelta(minutes=2, seconds=30)
565 minute = timedelta(minutes=1)
566 q, r = divmod(t, minute)
567 self.assertEqual(q, 2)
568 self.assertEqual(r, timedelta(seconds=30))
569
570 t = timedelta(minutes=-2, seconds=30)
571 q, r = divmod(t, minute)
572 self.assertEqual(q, -2)
573 self.assertEqual(r, timedelta(seconds=30))
574
575 zerotd = timedelta(0)
576 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
577
578 self.assertRaises(TypeError, divmod, t, 10)
579
580
Tim Peters2a799bf2002-12-16 20:18:38 +0000581#############################################################################
582# date tests
583
584class TestDateOnly(unittest.TestCase):
585 # Tests here won't pass if also run on datetime objects, so don't
586 # subclass this to test datetimes too.
587
588 def test_delta_non_days_ignored(self):
589 dt = date(2000, 1, 2)
590 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
591 microseconds=5)
592 days = timedelta(delta.days)
593 self.assertEqual(days, timedelta(1))
594
595 dt2 = dt + delta
596 self.assertEqual(dt2, dt + days)
597
598 dt2 = delta + dt
599 self.assertEqual(dt2, dt + days)
600
601 dt2 = dt - delta
602 self.assertEqual(dt2, dt - days)
603
604 delta = -delta
605 days = timedelta(delta.days)
606 self.assertEqual(days, timedelta(-2))
607
608 dt2 = dt + delta
609 self.assertEqual(dt2, dt + days)
610
611 dt2 = delta + dt
612 self.assertEqual(dt2, dt + days)
613
614 dt2 = dt - delta
615 self.assertEqual(dt2, dt - days)
616
Tim Peters604c0132004-06-07 23:04:33 +0000617class SubclassDate(date):
618 sub_var = 1
619
Guido van Rossumd8faa362007-04-27 19:54:29 +0000620class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000621 # Tests here should pass for both dates and datetimes, except for a
622 # few tests that TestDateTime overrides.
623
624 theclass = date
625
626 def test_basic_attributes(self):
627 dt = self.theclass(2002, 3, 1)
628 self.assertEqual(dt.year, 2002)
629 self.assertEqual(dt.month, 3)
630 self.assertEqual(dt.day, 1)
631
632 def test_roundtrip(self):
633 for dt in (self.theclass(1, 2, 3),
634 self.theclass.today()):
635 # Verify dt -> string -> date identity.
636 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000637 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000638 s = s[9:]
639 dt2 = eval(s)
640 self.assertEqual(dt, dt2)
641
642 # Verify identity via reconstructing from pieces.
643 dt2 = self.theclass(dt.year, dt.month, dt.day)
644 self.assertEqual(dt, dt2)
645
646 def test_ordinal_conversions(self):
647 # Check some fixed values.
648 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
649 (1, 12, 31, 365),
650 (2, 1, 1, 366),
651 # first example from "Calendrical Calculations"
652 (1945, 11, 12, 710347)]:
653 d = self.theclass(y, m, d)
654 self.assertEqual(n, d.toordinal())
655 fromord = self.theclass.fromordinal(n)
656 self.assertEqual(d, fromord)
657 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000658 # if we're checking something fancier than a date, verify
659 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000660 self.assertEqual(fromord.hour, 0)
661 self.assertEqual(fromord.minute, 0)
662 self.assertEqual(fromord.second, 0)
663 self.assertEqual(fromord.microsecond, 0)
664
Tim Peters0bf60bd2003-01-08 20:40:01 +0000665 # Check first and last days of year spottily across the whole
666 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000667 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000668 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
669 d = self.theclass(year, 1, 1)
670 n = d.toordinal()
671 d2 = self.theclass.fromordinal(n)
672 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000673 # Verify that moving back a day gets to the end of year-1.
674 if year > 1:
675 d = self.theclass.fromordinal(n-1)
676 d2 = self.theclass(year-1, 12, 31)
677 self.assertEqual(d, d2)
678 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000679
680 # Test every day in a leap-year and a non-leap year.
681 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
682 for year, isleap in (2000, True), (2002, False):
683 n = self.theclass(year, 1, 1).toordinal()
684 for month, maxday in zip(range(1, 13), dim):
685 if month == 2 and isleap:
686 maxday += 1
687 for day in range(1, maxday+1):
688 d = self.theclass(year, month, day)
689 self.assertEqual(d.toordinal(), n)
690 self.assertEqual(d, self.theclass.fromordinal(n))
691 n += 1
692
693 def test_extreme_ordinals(self):
694 a = self.theclass.min
695 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
696 aord = a.toordinal()
697 b = a.fromordinal(aord)
698 self.assertEqual(a, b)
699
700 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
701
702 b = a + timedelta(days=1)
703 self.assertEqual(b.toordinal(), aord + 1)
704 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
705
706 a = self.theclass.max
707 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
708 aord = a.toordinal()
709 b = a.fromordinal(aord)
710 self.assertEqual(a, b)
711
712 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
713
714 b = a - timedelta(days=1)
715 self.assertEqual(b.toordinal(), aord - 1)
716 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
717
718 def test_bad_constructor_arguments(self):
719 # bad years
720 self.theclass(MINYEAR, 1, 1) # no exception
721 self.theclass(MAXYEAR, 1, 1) # no exception
722 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
723 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
724 # bad months
725 self.theclass(2000, 1, 1) # no exception
726 self.theclass(2000, 12, 1) # no exception
727 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
728 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
729 # bad days
730 self.theclass(2000, 2, 29) # no exception
731 self.theclass(2004, 2, 29) # no exception
732 self.theclass(2400, 2, 29) # no exception
733 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
734 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
735 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
736 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
737 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
738 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
739
740 def test_hash_equality(self):
741 d = self.theclass(2000, 12, 31)
742 # same thing
743 e = self.theclass(2000, 12, 31)
744 self.assertEqual(d, e)
745 self.assertEqual(hash(d), hash(e))
746
747 dic = {d: 1}
748 dic[e] = 2
749 self.assertEqual(len(dic), 1)
750 self.assertEqual(dic[d], 2)
751 self.assertEqual(dic[e], 2)
752
753 d = self.theclass(2001, 1, 1)
754 # same thing
755 e = self.theclass(2001, 1, 1)
756 self.assertEqual(d, e)
757 self.assertEqual(hash(d), hash(e))
758
759 dic = {d: 1}
760 dic[e] = 2
761 self.assertEqual(len(dic), 1)
762 self.assertEqual(dic[d], 2)
763 self.assertEqual(dic[e], 2)
764
765 def test_computations(self):
766 a = self.theclass(2002, 1, 31)
767 b = self.theclass(1956, 1, 31)
768
769 diff = a-b
770 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
771 self.assertEqual(diff.seconds, 0)
772 self.assertEqual(diff.microseconds, 0)
773
774 day = timedelta(1)
775 week = timedelta(7)
776 a = self.theclass(2002, 3, 2)
777 self.assertEqual(a + day, self.theclass(2002, 3, 3))
778 self.assertEqual(day + a, self.theclass(2002, 3, 3))
779 self.assertEqual(a - day, self.theclass(2002, 3, 1))
780 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
781 self.assertEqual(a + week, self.theclass(2002, 3, 9))
782 self.assertEqual(a - week, self.theclass(2002, 2, 23))
783 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
784 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
785 self.assertEqual((a + week) - a, week)
786 self.assertEqual((a + day) - a, day)
787 self.assertEqual((a - week) - a, -week)
788 self.assertEqual((a - day) - a, -day)
789 self.assertEqual(a - (a + week), -week)
790 self.assertEqual(a - (a + day), -day)
791 self.assertEqual(a - (a - week), week)
792 self.assertEqual(a - (a - day), day)
793
Mark Dickinson5c2db372009-12-05 20:28:34 +0000794 # Add/sub ints or floats should be illegal
795 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000796 self.assertRaises(TypeError, lambda: a+i)
797 self.assertRaises(TypeError, lambda: a-i)
798 self.assertRaises(TypeError, lambda: i+a)
799 self.assertRaises(TypeError, lambda: i-a)
800
801 # delta - date is senseless.
802 self.assertRaises(TypeError, lambda: day - a)
803 # mixing date and (delta or date) via * or // is senseless
804 self.assertRaises(TypeError, lambda: day * a)
805 self.assertRaises(TypeError, lambda: a * day)
806 self.assertRaises(TypeError, lambda: day // a)
807 self.assertRaises(TypeError, lambda: a // day)
808 self.assertRaises(TypeError, lambda: a * a)
809 self.assertRaises(TypeError, lambda: a // a)
810 # date + date is senseless
811 self.assertRaises(TypeError, lambda: a + a)
812
813 def test_overflow(self):
814 tiny = self.theclass.resolution
815
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000816 for delta in [tiny, timedelta(1), timedelta(2)]:
817 dt = self.theclass.min + delta
818 dt -= delta # no problem
819 self.assertRaises(OverflowError, dt.__sub__, delta)
820 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000821
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000822 dt = self.theclass.max - delta
823 dt += delta # no problem
824 self.assertRaises(OverflowError, dt.__add__, delta)
825 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000826
827 def test_fromtimestamp(self):
828 import time
829
830 # Try an arbitrary fixed value.
831 year, month, day = 1999, 9, 19
832 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
833 d = self.theclass.fromtimestamp(ts)
834 self.assertEqual(d.year, year)
835 self.assertEqual(d.month, month)
836 self.assertEqual(d.day, day)
837
Tim Peters1b6f7a92004-06-20 02:50:16 +0000838 def test_insane_fromtimestamp(self):
839 # It's possible that some platform maps time_t to double,
840 # and that this test will fail there. This test should
841 # exempt such platforms (provided they return reasonable
842 # results!).
843 for insane in -1e200, 1e200:
844 self.assertRaises(ValueError, self.theclass.fromtimestamp,
845 insane)
846
Tim Peters2a799bf2002-12-16 20:18:38 +0000847 def test_today(self):
848 import time
849
850 # We claim that today() is like fromtimestamp(time.time()), so
851 # prove it.
852 for dummy in range(3):
853 today = self.theclass.today()
854 ts = time.time()
855 todayagain = self.theclass.fromtimestamp(ts)
856 if today == todayagain:
857 break
858 # There are several legit reasons that could fail:
859 # 1. It recently became midnight, between the today() and the
860 # time() calls.
861 # 2. The platform time() has such fine resolution that we'll
862 # never get the same value twice.
863 # 3. The platform time() has poor resolution, and we just
864 # happened to call today() right before a resolution quantum
865 # boundary.
866 # 4. The system clock got fiddled between calls.
867 # In any case, wait a little while and try again.
868 time.sleep(0.1)
869
870 # It worked or it didn't. If it didn't, assume it's reason #2, and
871 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000872 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000873 abs(todayagain - today) < timedelta(seconds=0.5))
874
875 def test_weekday(self):
876 for i in range(7):
877 # March 4, 2002 is a Monday
878 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
879 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
880 # January 2, 1956 is a Monday
881 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
882 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
883
884 def test_isocalendar(self):
885 # Check examples from
886 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
887 for i in range(7):
888 d = self.theclass(2003, 12, 22+i)
889 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
890 d = self.theclass(2003, 12, 29) + timedelta(i)
891 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
892 d = self.theclass(2004, 1, 5+i)
893 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
894 d = self.theclass(2009, 12, 21+i)
895 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
896 d = self.theclass(2009, 12, 28) + timedelta(i)
897 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
898 d = self.theclass(2010, 1, 4+i)
899 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
900
901 def test_iso_long_years(self):
902 # Calculate long ISO years and compare to table from
903 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
904 ISO_LONG_YEARS_TABLE = """
905 4 32 60 88
906 9 37 65 93
907 15 43 71 99
908 20 48 76
909 26 54 82
910
911 105 133 161 189
912 111 139 167 195
913 116 144 172
914 122 150 178
915 128 156 184
916
917 201 229 257 285
918 207 235 263 291
919 212 240 268 296
920 218 246 274
921 224 252 280
922
923 303 331 359 387
924 308 336 364 392
925 314 342 370 398
926 320 348 376
927 325 353 381
928 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000929 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000930 L = []
931 for i in range(400):
932 d = self.theclass(2000+i, 12, 31)
933 d1 = self.theclass(1600+i, 12, 31)
934 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
935 if d.isocalendar()[1] == 53:
936 L.append(i)
937 self.assertEqual(L, iso_long_years)
938
939 def test_isoformat(self):
940 t = self.theclass(2, 3, 2)
941 self.assertEqual(t.isoformat(), "0002-03-02")
942
943 def test_ctime(self):
944 t = self.theclass(2002, 3, 2)
945 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
946
947 def test_strftime(self):
948 t = self.theclass(2005, 3, 2)
949 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000950 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000951 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000952
953 self.assertRaises(TypeError, t.strftime) # needs an arg
954 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
955 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
956
Georg Brandlf78e02b2008-06-10 17:40:04 +0000957 # test that unicode input is allowed (issue 2782)
958 self.assertEqual(t.strftime("%m"), "03")
959
Tim Peters2a799bf2002-12-16 20:18:38 +0000960 # A naive object replaces %z and %Z w/ empty strings.
961 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
962
Benjamin Petersone1cdfd72009-01-18 21:02:37 +0000963 #make sure that invalid format specifiers are handled correctly
964 #self.assertRaises(ValueError, t.strftime, "%e")
965 #self.assertRaises(ValueError, t.strftime, "%")
966 #self.assertRaises(ValueError, t.strftime, "%#")
967
968 #oh well, some systems just ignore those invalid ones.
969 #at least, excercise them to make sure that no crashes
970 #are generated
971 for f in ["%e", "%", "%#"]:
972 try:
973 t.strftime(f)
974 except ValueError:
975 pass
976
977 #check that this standard extension works
978 t.strftime("%f")
979
Georg Brandlf78e02b2008-06-10 17:40:04 +0000980
Eric Smith1ba31142007-09-11 18:06:02 +0000981 def test_format(self):
982 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000983 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000984
985 # check that a derived class's __str__() gets called
986 class A(self.theclass):
987 def __str__(self):
988 return 'A'
989 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000990 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000991
992 # check that a derived class's strftime gets called
993 class B(self.theclass):
994 def strftime(self, format_spec):
995 return 'B'
996 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000997 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000998
999 for fmt in ["m:%m d:%d y:%y",
1000 "m:%m d:%d y:%y H:%H M:%M S:%S",
1001 "%z %Z",
1002 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001003 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1004 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1005 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001006
Tim Peters2a799bf2002-12-16 20:18:38 +00001007 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00001008 self.assertIsInstance(self.theclass.min, self.theclass)
1009 self.assertIsInstance(self.theclass.max, self.theclass)
1010 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001011 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001012
1013 def test_extreme_timedelta(self):
1014 big = self.theclass.max - self.theclass.min
1015 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1016 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1017 # n == 315537897599999999 ~= 2**58.13
1018 justasbig = timedelta(0, 0, n)
1019 self.assertEqual(big, justasbig)
1020 self.assertEqual(self.theclass.min + big, self.theclass.max)
1021 self.assertEqual(self.theclass.max - big, self.theclass.min)
1022
1023 def test_timetuple(self):
1024 for i in range(7):
1025 # January 2, 1956 is a Monday (0)
1026 d = self.theclass(1956, 1, 2+i)
1027 t = d.timetuple()
1028 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1029 # February 1, 1956 is a Wednesday (2)
1030 d = self.theclass(1956, 2, 1+i)
1031 t = d.timetuple()
1032 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1033 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1034 # of the year.
1035 d = self.theclass(1956, 3, 1+i)
1036 t = d.timetuple()
1037 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1038 self.assertEqual(t.tm_year, 1956)
1039 self.assertEqual(t.tm_mon, 3)
1040 self.assertEqual(t.tm_mday, 1+i)
1041 self.assertEqual(t.tm_hour, 0)
1042 self.assertEqual(t.tm_min, 0)
1043 self.assertEqual(t.tm_sec, 0)
1044 self.assertEqual(t.tm_wday, (3+i)%7)
1045 self.assertEqual(t.tm_yday, 61+i)
1046 self.assertEqual(t.tm_isdst, -1)
1047
1048 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001049 args = 6, 7, 23
1050 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001051 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001052 green = pickler.dumps(orig, proto)
1053 derived = unpickler.loads(green)
1054 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001055
1056 def test_compare(self):
1057 t1 = self.theclass(2, 3, 4)
1058 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001059 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001060 self.assertTrue(t1 <= t2)
1061 self.assertTrue(t1 >= t2)
1062 self.assertTrue(not t1 != t2)
1063 self.assertTrue(not t1 < t2)
1064 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001065
1066 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1067 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001068 self.assertTrue(t1 < t2)
1069 self.assertTrue(t2 > t1)
1070 self.assertTrue(t1 <= t2)
1071 self.assertTrue(t2 >= t1)
1072 self.assertTrue(t1 != t2)
1073 self.assertTrue(t2 != t1)
1074 self.assertTrue(not t1 == t2)
1075 self.assertTrue(not t2 == t1)
1076 self.assertTrue(not t1 > t2)
1077 self.assertTrue(not t2 < t1)
1078 self.assertTrue(not t1 >= t2)
1079 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001080
Tim Peters68124bb2003-02-08 03:46:31 +00001081 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001082 self.assertEqual(t1 == badarg, False)
1083 self.assertEqual(t1 != badarg, True)
1084 self.assertEqual(badarg == t1, False)
1085 self.assertEqual(badarg != t1, True)
1086
Tim Peters2a799bf2002-12-16 20:18:38 +00001087 self.assertRaises(TypeError, lambda: t1 < badarg)
1088 self.assertRaises(TypeError, lambda: t1 > badarg)
1089 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001090 self.assertRaises(TypeError, lambda: badarg <= t1)
1091 self.assertRaises(TypeError, lambda: badarg < t1)
1092 self.assertRaises(TypeError, lambda: badarg > t1)
1093 self.assertRaises(TypeError, lambda: badarg >= t1)
1094
Tim Peters8d81a012003-01-24 22:36:34 +00001095 def test_mixed_compare(self):
1096 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001097
1098 # Our class can be compared for equality to other classes
1099 self.assertEqual(our == 1, False)
1100 self.assertEqual(1 == our, False)
1101 self.assertEqual(our != 1, True)
1102 self.assertEqual(1 != our, True)
1103
1104 # But the ordering is undefined
1105 self.assertRaises(TypeError, lambda: our < 1)
1106 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001107
Guido van Rossum19960592006-08-24 17:29:38 +00001108 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001109
Guido van Rossum19960592006-08-24 17:29:38 +00001110 class SomeClass:
1111 pass
1112
1113 their = SomeClass()
1114 self.assertEqual(our == their, False)
1115 self.assertEqual(their == our, False)
1116 self.assertEqual(our != their, True)
1117 self.assertEqual(their != our, True)
1118 self.assertRaises(TypeError, lambda: our < their)
1119 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001120
Guido van Rossum19960592006-08-24 17:29:38 +00001121 # However, if the other class explicitly defines ordering
1122 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001123
Guido van Rossum19960592006-08-24 17:29:38 +00001124 class LargerThanAnything:
1125 def __lt__(self, other):
1126 return False
1127 def __le__(self, other):
1128 return isinstance(other, LargerThanAnything)
1129 def __eq__(self, other):
1130 return isinstance(other, LargerThanAnything)
1131 def __ne__(self, other):
1132 return not isinstance(other, LargerThanAnything)
1133 def __gt__(self, other):
1134 return not isinstance(other, LargerThanAnything)
1135 def __ge__(self, other):
1136 return True
1137
1138 their = LargerThanAnything()
1139 self.assertEqual(our == their, False)
1140 self.assertEqual(their == our, False)
1141 self.assertEqual(our != their, True)
1142 self.assertEqual(their != our, True)
1143 self.assertEqual(our < their, True)
1144 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001145
Tim Peters2a799bf2002-12-16 20:18:38 +00001146 def test_bool(self):
1147 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001148 self.assertTrue(self.theclass.min)
1149 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001150
Guido van Rossum04110fb2007-08-24 16:32:05 +00001151 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001152 # For nasty technical reasons, we can't handle years before 1900.
1153 cls = self.theclass
1154 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1155 for y in 1, 49, 51, 99, 100, 1000, 1899:
1156 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001157
1158 def test_replace(self):
1159 cls = self.theclass
1160 args = [1, 2, 3]
1161 base = cls(*args)
1162 self.assertEqual(base, base.replace())
1163
1164 i = 0
1165 for name, newval in (("year", 2),
1166 ("month", 3),
1167 ("day", 4)):
1168 newargs = args[:]
1169 newargs[i] = newval
1170 expected = cls(*newargs)
1171 got = base.replace(**{name: newval})
1172 self.assertEqual(expected, got)
1173 i += 1
1174
1175 # Out of bounds.
1176 base = cls(2000, 2, 29)
1177 self.assertRaises(ValueError, base.replace, year=2001)
1178
Tim Petersa98924a2003-05-17 05:55:19 +00001179 def test_subclass_date(self):
1180
1181 class C(self.theclass):
1182 theAnswer = 42
1183
1184 def __new__(cls, *args, **kws):
1185 temp = kws.copy()
1186 extra = temp.pop('extra')
1187 result = self.theclass.__new__(cls, *args, **temp)
1188 result.extra = extra
1189 return result
1190
1191 def newmeth(self, start):
1192 return start + self.year + self.month
1193
1194 args = 2003, 4, 14
1195
1196 dt1 = self.theclass(*args)
1197 dt2 = C(*args, **{'extra': 7})
1198
1199 self.assertEqual(dt2.__class__, C)
1200 self.assertEqual(dt2.theAnswer, 42)
1201 self.assertEqual(dt2.extra, 7)
1202 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1203 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1204
Tim Peters604c0132004-06-07 23:04:33 +00001205 def test_pickling_subclass_date(self):
1206
1207 args = 6, 7, 23
1208 orig = SubclassDate(*args)
1209 for pickler, unpickler, proto in pickle_choices:
1210 green = pickler.dumps(orig, proto)
1211 derived = unpickler.loads(green)
1212 self.assertEqual(orig, derived)
1213
Tim Peters3f606292004-03-21 23:38:41 +00001214 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001215 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001216 # This is a low-overhead backdoor. A user can (by intent or
1217 # mistake) pass a string directly, which (if it's the right length)
1218 # will get treated like a pickle, and bypass the normal sanity
1219 # checks in the constructor. This can create insane objects.
1220 # The constructor doesn't want to burn the time to validate all
1221 # fields, but does check the month field. This stops, e.g.,
1222 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001223 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001224 if not issubclass(self.theclass, datetime):
1225 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001226 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001227 self.assertRaises(TypeError, self.theclass,
1228 base[:2] + month_byte + base[3:])
1229 for ord_byte in range(1, 13):
1230 # This shouldn't blow up because of the month byte alone. If
1231 # the implementation changes to do more-careful checking, it may
1232 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001233 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001234
Tim Peters2a799bf2002-12-16 20:18:38 +00001235#############################################################################
1236# datetime tests
1237
Tim Peters604c0132004-06-07 23:04:33 +00001238class SubclassDatetime(datetime):
1239 sub_var = 1
1240
Tim Peters2a799bf2002-12-16 20:18:38 +00001241class TestDateTime(TestDate):
1242
1243 theclass = datetime
1244
1245 def test_basic_attributes(self):
1246 dt = self.theclass(2002, 3, 1, 12, 0)
1247 self.assertEqual(dt.year, 2002)
1248 self.assertEqual(dt.month, 3)
1249 self.assertEqual(dt.day, 1)
1250 self.assertEqual(dt.hour, 12)
1251 self.assertEqual(dt.minute, 0)
1252 self.assertEqual(dt.second, 0)
1253 self.assertEqual(dt.microsecond, 0)
1254
1255 def test_basic_attributes_nonzero(self):
1256 # Make sure all attributes are non-zero so bugs in
1257 # bit-shifting access show up.
1258 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1259 self.assertEqual(dt.year, 2002)
1260 self.assertEqual(dt.month, 3)
1261 self.assertEqual(dt.day, 1)
1262 self.assertEqual(dt.hour, 12)
1263 self.assertEqual(dt.minute, 59)
1264 self.assertEqual(dt.second, 59)
1265 self.assertEqual(dt.microsecond, 8000)
1266
1267 def test_roundtrip(self):
1268 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1269 self.theclass.now()):
1270 # Verify dt -> string -> datetime identity.
1271 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001272 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001273 s = s[9:]
1274 dt2 = eval(s)
1275 self.assertEqual(dt, dt2)
1276
1277 # Verify identity via reconstructing from pieces.
1278 dt2 = self.theclass(dt.year, dt.month, dt.day,
1279 dt.hour, dt.minute, dt.second,
1280 dt.microsecond)
1281 self.assertEqual(dt, dt2)
1282
1283 def test_isoformat(self):
1284 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1285 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1286 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1287 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001288 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001289 # str is ISO format with the separator forced to a blank.
1290 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1291
1292 t = self.theclass(2, 3, 2)
1293 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1294 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1295 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1296 # str is ISO format with the separator forced to a blank.
1297 self.assertEqual(str(t), "0002-03-02 00:00:00")
1298
Eric Smith1ba31142007-09-11 18:06:02 +00001299 def test_format(self):
1300 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001301 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001302
1303 # check that a derived class's __str__() gets called
1304 class A(self.theclass):
1305 def __str__(self):
1306 return 'A'
1307 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001308 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001309
1310 # check that a derived class's strftime gets called
1311 class B(self.theclass):
1312 def strftime(self, format_spec):
1313 return 'B'
1314 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001315 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001316
1317 for fmt in ["m:%m d:%d y:%y",
1318 "m:%m d:%d y:%y H:%H M:%M S:%S",
1319 "%z %Z",
1320 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001321 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1322 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1323 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001324
Tim Peters2a799bf2002-12-16 20:18:38 +00001325 def test_more_ctime(self):
1326 # Test fields that TestDate doesn't touch.
1327 import time
1328
1329 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1330 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1331 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1332 # out. The difference is that t.ctime() produces " 2" for the day,
1333 # but platform ctime() produces "02" for the day. According to
1334 # C99, t.ctime() is correct here.
1335 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1336
1337 # So test a case where that difference doesn't matter.
1338 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1339 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1340
1341 def test_tz_independent_comparing(self):
1342 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1343 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1344 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1345 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001346 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001347
1348 # Make sure comparison doesn't forget microseconds, and isn't done
1349 # via comparing a float timestamp (an IEEE double doesn't have enough
1350 # precision to span microsecond resolution across years 1 thru 9999,
1351 # so comparing via timestamp necessarily calls some distinct values
1352 # equal).
1353 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1354 us = timedelta(microseconds=1)
1355 dt2 = dt1 + us
1356 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001357 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001358
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001359 def test_strftime_with_bad_tzname_replace(self):
1360 # verify ok if tzinfo.tzname().replace() returns a non-string
1361 class MyTzInfo(FixedOffset):
1362 def tzname(self, dt):
1363 class MyStr(str):
1364 def replace(self, *args):
1365 return None
1366 return MyStr('name')
1367 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1368 self.assertRaises(TypeError, t.strftime, '%Z')
1369
Tim Peters2a799bf2002-12-16 20:18:38 +00001370 def test_bad_constructor_arguments(self):
1371 # bad years
1372 self.theclass(MINYEAR, 1, 1) # no exception
1373 self.theclass(MAXYEAR, 1, 1) # no exception
1374 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1375 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1376 # bad months
1377 self.theclass(2000, 1, 1) # no exception
1378 self.theclass(2000, 12, 1) # no exception
1379 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1380 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1381 # bad days
1382 self.theclass(2000, 2, 29) # no exception
1383 self.theclass(2004, 2, 29) # no exception
1384 self.theclass(2400, 2, 29) # no exception
1385 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1386 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1387 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1388 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1389 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1390 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1391 # bad hours
1392 self.theclass(2000, 1, 31, 0) # no exception
1393 self.theclass(2000, 1, 31, 23) # no exception
1394 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1395 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1396 # bad minutes
1397 self.theclass(2000, 1, 31, 23, 0) # no exception
1398 self.theclass(2000, 1, 31, 23, 59) # no exception
1399 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1400 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1401 # bad seconds
1402 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1403 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1404 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1405 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1406 # bad microseconds
1407 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1408 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1409 self.assertRaises(ValueError, self.theclass,
1410 2000, 1, 31, 23, 59, 59, -1)
1411 self.assertRaises(ValueError, self.theclass,
1412 2000, 1, 31, 23, 59, 59,
1413 1000000)
1414
1415 def test_hash_equality(self):
1416 d = self.theclass(2000, 12, 31, 23, 30, 17)
1417 e = self.theclass(2000, 12, 31, 23, 30, 17)
1418 self.assertEqual(d, e)
1419 self.assertEqual(hash(d), hash(e))
1420
1421 dic = {d: 1}
1422 dic[e] = 2
1423 self.assertEqual(len(dic), 1)
1424 self.assertEqual(dic[d], 2)
1425 self.assertEqual(dic[e], 2)
1426
1427 d = self.theclass(2001, 1, 1, 0, 5, 17)
1428 e = self.theclass(2001, 1, 1, 0, 5, 17)
1429 self.assertEqual(d, e)
1430 self.assertEqual(hash(d), hash(e))
1431
1432 dic = {d: 1}
1433 dic[e] = 2
1434 self.assertEqual(len(dic), 1)
1435 self.assertEqual(dic[d], 2)
1436 self.assertEqual(dic[e], 2)
1437
1438 def test_computations(self):
1439 a = self.theclass(2002, 1, 31)
1440 b = self.theclass(1956, 1, 31)
1441 diff = a-b
1442 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1443 self.assertEqual(diff.seconds, 0)
1444 self.assertEqual(diff.microseconds, 0)
1445 a = self.theclass(2002, 3, 2, 17, 6)
1446 millisec = timedelta(0, 0, 1000)
1447 hour = timedelta(0, 3600)
1448 day = timedelta(1)
1449 week = timedelta(7)
1450 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1451 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1452 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1453 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1454 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1455 self.assertEqual(a - hour, a + -hour)
1456 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1457 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1458 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1459 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1460 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1461 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1462 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1463 self.assertEqual((a + week) - a, week)
1464 self.assertEqual((a + day) - a, day)
1465 self.assertEqual((a + hour) - a, hour)
1466 self.assertEqual((a + millisec) - a, millisec)
1467 self.assertEqual((a - week) - a, -week)
1468 self.assertEqual((a - day) - a, -day)
1469 self.assertEqual((a - hour) - a, -hour)
1470 self.assertEqual((a - millisec) - a, -millisec)
1471 self.assertEqual(a - (a + week), -week)
1472 self.assertEqual(a - (a + day), -day)
1473 self.assertEqual(a - (a + hour), -hour)
1474 self.assertEqual(a - (a + millisec), -millisec)
1475 self.assertEqual(a - (a - week), week)
1476 self.assertEqual(a - (a - day), day)
1477 self.assertEqual(a - (a - hour), hour)
1478 self.assertEqual(a - (a - millisec), millisec)
1479 self.assertEqual(a + (week + day + hour + millisec),
1480 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1481 self.assertEqual(a + (week + day + hour + millisec),
1482 (((a + week) + day) + hour) + millisec)
1483 self.assertEqual(a - (week + day + hour + millisec),
1484 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1485 self.assertEqual(a - (week + day + hour + millisec),
1486 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001487 # Add/sub ints or floats should be illegal
1488 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001489 self.assertRaises(TypeError, lambda: a+i)
1490 self.assertRaises(TypeError, lambda: a-i)
1491 self.assertRaises(TypeError, lambda: i+a)
1492 self.assertRaises(TypeError, lambda: i-a)
1493
1494 # delta - datetime is senseless.
1495 self.assertRaises(TypeError, lambda: day - a)
1496 # mixing datetime and (delta or datetime) via * or // is senseless
1497 self.assertRaises(TypeError, lambda: day * a)
1498 self.assertRaises(TypeError, lambda: a * day)
1499 self.assertRaises(TypeError, lambda: day // a)
1500 self.assertRaises(TypeError, lambda: a // day)
1501 self.assertRaises(TypeError, lambda: a * a)
1502 self.assertRaises(TypeError, lambda: a // a)
1503 # datetime + datetime is senseless
1504 self.assertRaises(TypeError, lambda: a + a)
1505
1506 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001507 args = 6, 7, 23, 20, 59, 1, 64**2
1508 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001509 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001510 green = pickler.dumps(orig, proto)
1511 derived = unpickler.loads(green)
1512 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001513
Guido van Rossum275666f2003-02-07 21:49:01 +00001514 def test_more_pickling(self):
1515 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1516 s = pickle.dumps(a)
1517 b = pickle.loads(s)
1518 self.assertEqual(b.year, 2003)
1519 self.assertEqual(b.month, 2)
1520 self.assertEqual(b.day, 7)
1521
Tim Peters604c0132004-06-07 23:04:33 +00001522 def test_pickling_subclass_datetime(self):
1523 args = 6, 7, 23, 20, 59, 1, 64**2
1524 orig = SubclassDatetime(*args)
1525 for pickler, unpickler, proto in pickle_choices:
1526 green = pickler.dumps(orig, proto)
1527 derived = unpickler.loads(green)
1528 self.assertEqual(orig, derived)
1529
Tim Peters2a799bf2002-12-16 20:18:38 +00001530 def test_more_compare(self):
1531 # The test_compare() inherited from TestDate covers the error cases.
1532 # We just want to test lexicographic ordering on the members datetime
1533 # has that date lacks.
1534 args = [2000, 11, 29, 20, 58, 16, 999998]
1535 t1 = self.theclass(*args)
1536 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001537 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001538 self.assertTrue(t1 <= t2)
1539 self.assertTrue(t1 >= t2)
1540 self.assertTrue(not t1 != t2)
1541 self.assertTrue(not t1 < t2)
1542 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001543
1544 for i in range(len(args)):
1545 newargs = args[:]
1546 newargs[i] = args[i] + 1
1547 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001548 self.assertTrue(t1 < t2)
1549 self.assertTrue(t2 > t1)
1550 self.assertTrue(t1 <= t2)
1551 self.assertTrue(t2 >= t1)
1552 self.assertTrue(t1 != t2)
1553 self.assertTrue(t2 != t1)
1554 self.assertTrue(not t1 == t2)
1555 self.assertTrue(not t2 == t1)
1556 self.assertTrue(not t1 > t2)
1557 self.assertTrue(not t2 < t1)
1558 self.assertTrue(not t1 >= t2)
1559 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001560
1561
1562 # A helper for timestamp constructor tests.
1563 def verify_field_equality(self, expected, got):
1564 self.assertEqual(expected.tm_year, got.year)
1565 self.assertEqual(expected.tm_mon, got.month)
1566 self.assertEqual(expected.tm_mday, got.day)
1567 self.assertEqual(expected.tm_hour, got.hour)
1568 self.assertEqual(expected.tm_min, got.minute)
1569 self.assertEqual(expected.tm_sec, got.second)
1570
1571 def test_fromtimestamp(self):
1572 import time
1573
1574 ts = time.time()
1575 expected = time.localtime(ts)
1576 got = self.theclass.fromtimestamp(ts)
1577 self.verify_field_equality(expected, got)
1578
1579 def test_utcfromtimestamp(self):
1580 import time
1581
1582 ts = time.time()
1583 expected = time.gmtime(ts)
1584 got = self.theclass.utcfromtimestamp(ts)
1585 self.verify_field_equality(expected, got)
1586
Thomas Wouters477c8d52006-05-27 19:21:47 +00001587 def test_microsecond_rounding(self):
1588 # Test whether fromtimestamp "rounds up" floats that are less
1589 # than one microsecond smaller than an integer.
1590 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1591 self.theclass.fromtimestamp(1))
1592
Tim Peters1b6f7a92004-06-20 02:50:16 +00001593 def test_insane_fromtimestamp(self):
1594 # It's possible that some platform maps time_t to double,
1595 # and that this test will fail there. This test should
1596 # exempt such platforms (provided they return reasonable
1597 # results!).
1598 for insane in -1e200, 1e200:
1599 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1600 insane)
1601
1602 def test_insane_utcfromtimestamp(self):
1603 # It's possible that some platform maps time_t to double,
1604 # and that this test will fail there. This test should
1605 # exempt such platforms (provided they return reasonable
1606 # results!).
1607 for insane in -1e200, 1e200:
1608 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1609 insane)
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001610 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001611 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001612 # The result is tz-dependent; at least test that this doesn't
1613 # fail (like it did before bug 1646728 was fixed).
1614 self.theclass.fromtimestamp(-1.05)
1615
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001616 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001617 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001618 d = self.theclass.utcfromtimestamp(-1.05)
1619 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1620
Tim Peters2a799bf2002-12-16 20:18:38 +00001621 def test_utcnow(self):
1622 import time
1623
1624 # Call it a success if utcnow() and utcfromtimestamp() are within
1625 # a second of each other.
1626 tolerance = timedelta(seconds=1)
1627 for dummy in range(3):
1628 from_now = self.theclass.utcnow()
1629 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1630 if abs(from_timestamp - from_now) <= tolerance:
1631 break
1632 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001633 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001634
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001635 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001636 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001637
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001638 string = '2004-12-01 13:02:47.197'
1639 format = '%Y-%m-%d %H:%M:%S.%f'
1640 result, frac = _strptime._strptime(string, format)
1641 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001642 got = self.theclass.strptime(string, format)
1643 self.assertEqual(expected, got)
1644
Tim Peters2a799bf2002-12-16 20:18:38 +00001645 def test_more_timetuple(self):
1646 # This tests fields beyond those tested by the TestDate.test_timetuple.
1647 t = self.theclass(2004, 12, 31, 6, 22, 33)
1648 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1649 self.assertEqual(t.timetuple(),
1650 (t.year, t.month, t.day,
1651 t.hour, t.minute, t.second,
1652 t.weekday(),
1653 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1654 -1))
1655 tt = t.timetuple()
1656 self.assertEqual(tt.tm_year, t.year)
1657 self.assertEqual(tt.tm_mon, t.month)
1658 self.assertEqual(tt.tm_mday, t.day)
1659 self.assertEqual(tt.tm_hour, t.hour)
1660 self.assertEqual(tt.tm_min, t.minute)
1661 self.assertEqual(tt.tm_sec, t.second)
1662 self.assertEqual(tt.tm_wday, t.weekday())
1663 self.assertEqual(tt.tm_yday, t.toordinal() -
1664 date(t.year, 1, 1).toordinal() + 1)
1665 self.assertEqual(tt.tm_isdst, -1)
1666
1667 def test_more_strftime(self):
1668 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001669 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1670 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1671 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001672
1673 def test_extract(self):
1674 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1675 self.assertEqual(dt.date(), date(2002, 3, 4))
1676 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1677
1678 def test_combine(self):
1679 d = date(2002, 3, 4)
1680 t = time(18, 45, 3, 1234)
1681 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1682 combine = self.theclass.combine
1683 dt = combine(d, t)
1684 self.assertEqual(dt, expected)
1685
1686 dt = combine(time=t, date=d)
1687 self.assertEqual(dt, expected)
1688
1689 self.assertEqual(d, dt.date())
1690 self.assertEqual(t, dt.time())
1691 self.assertEqual(dt, combine(dt.date(), dt.time()))
1692
1693 self.assertRaises(TypeError, combine) # need an arg
1694 self.assertRaises(TypeError, combine, d) # need two args
1695 self.assertRaises(TypeError, combine, t, d) # args reversed
1696 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1697 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1698
Tim Peters12bf3392002-12-24 05:41:27 +00001699 def test_replace(self):
1700 cls = self.theclass
1701 args = [1, 2, 3, 4, 5, 6, 7]
1702 base = cls(*args)
1703 self.assertEqual(base, base.replace())
1704
1705 i = 0
1706 for name, newval in (("year", 2),
1707 ("month", 3),
1708 ("day", 4),
1709 ("hour", 5),
1710 ("minute", 6),
1711 ("second", 7),
1712 ("microsecond", 8)):
1713 newargs = args[:]
1714 newargs[i] = newval
1715 expected = cls(*newargs)
1716 got = base.replace(**{name: newval})
1717 self.assertEqual(expected, got)
1718 i += 1
1719
1720 # Out of bounds.
1721 base = cls(2000, 2, 29)
1722 self.assertRaises(ValueError, base.replace, year=2001)
1723
Tim Peters80475bb2002-12-25 07:40:55 +00001724 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001725 # Pretty boring! The TZ test is more interesting here. astimezone()
1726 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001727 dt = self.theclass.now()
1728 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001729 self.assertRaises(TypeError, dt.astimezone) # not enough args
1730 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1731 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001732 self.assertRaises(ValueError, dt.astimezone, f) # naive
1733 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001734
Tim Peters52dcce22003-01-23 16:36:11 +00001735 class Bogus(tzinfo):
1736 def utcoffset(self, dt): return None
1737 def dst(self, dt): return timedelta(0)
1738 bog = Bogus()
1739 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1740
1741 class AlsoBogus(tzinfo):
1742 def utcoffset(self, dt): return timedelta(0)
1743 def dst(self, dt): return None
1744 alsobog = AlsoBogus()
1745 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001746
Tim Petersa98924a2003-05-17 05:55:19 +00001747 def test_subclass_datetime(self):
1748
1749 class C(self.theclass):
1750 theAnswer = 42
1751
1752 def __new__(cls, *args, **kws):
1753 temp = kws.copy()
1754 extra = temp.pop('extra')
1755 result = self.theclass.__new__(cls, *args, **temp)
1756 result.extra = extra
1757 return result
1758
1759 def newmeth(self, start):
1760 return start + self.year + self.month + self.second
1761
1762 args = 2003, 4, 14, 12, 13, 41
1763
1764 dt1 = self.theclass(*args)
1765 dt2 = C(*args, **{'extra': 7})
1766
1767 self.assertEqual(dt2.__class__, C)
1768 self.assertEqual(dt2.theAnswer, 42)
1769 self.assertEqual(dt2.extra, 7)
1770 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1771 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1772 dt1.second - 7)
1773
Tim Peters604c0132004-06-07 23:04:33 +00001774class SubclassTime(time):
1775 sub_var = 1
1776
Guido van Rossumd8faa362007-04-27 19:54:29 +00001777class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001778
1779 theclass = time
1780
1781 def test_basic_attributes(self):
1782 t = self.theclass(12, 0)
1783 self.assertEqual(t.hour, 12)
1784 self.assertEqual(t.minute, 0)
1785 self.assertEqual(t.second, 0)
1786 self.assertEqual(t.microsecond, 0)
1787
1788 def test_basic_attributes_nonzero(self):
1789 # Make sure all attributes are non-zero so bugs in
1790 # bit-shifting access show up.
1791 t = self.theclass(12, 59, 59, 8000)
1792 self.assertEqual(t.hour, 12)
1793 self.assertEqual(t.minute, 59)
1794 self.assertEqual(t.second, 59)
1795 self.assertEqual(t.microsecond, 8000)
1796
1797 def test_roundtrip(self):
1798 t = self.theclass(1, 2, 3, 4)
1799
1800 # Verify t -> string -> time identity.
1801 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001802 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001803 s = s[9:]
1804 t2 = eval(s)
1805 self.assertEqual(t, t2)
1806
1807 # Verify identity via reconstructing from pieces.
1808 t2 = self.theclass(t.hour, t.minute, t.second,
1809 t.microsecond)
1810 self.assertEqual(t, t2)
1811
1812 def test_comparing(self):
1813 args = [1, 2, 3, 4]
1814 t1 = self.theclass(*args)
1815 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001816 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001817 self.assertTrue(t1 <= t2)
1818 self.assertTrue(t1 >= t2)
1819 self.assertTrue(not t1 != t2)
1820 self.assertTrue(not t1 < t2)
1821 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001822
1823 for i in range(len(args)):
1824 newargs = args[:]
1825 newargs[i] = args[i] + 1
1826 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001827 self.assertTrue(t1 < t2)
1828 self.assertTrue(t2 > t1)
1829 self.assertTrue(t1 <= t2)
1830 self.assertTrue(t2 >= t1)
1831 self.assertTrue(t1 != t2)
1832 self.assertTrue(t2 != t1)
1833 self.assertTrue(not t1 == t2)
1834 self.assertTrue(not t2 == t1)
1835 self.assertTrue(not t1 > t2)
1836 self.assertTrue(not t2 < t1)
1837 self.assertTrue(not t1 >= t2)
1838 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001839
Tim Peters68124bb2003-02-08 03:46:31 +00001840 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001841 self.assertEqual(t1 == badarg, False)
1842 self.assertEqual(t1 != badarg, True)
1843 self.assertEqual(badarg == t1, False)
1844 self.assertEqual(badarg != t1, True)
1845
Tim Peters2a799bf2002-12-16 20:18:38 +00001846 self.assertRaises(TypeError, lambda: t1 <= badarg)
1847 self.assertRaises(TypeError, lambda: t1 < badarg)
1848 self.assertRaises(TypeError, lambda: t1 > badarg)
1849 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001850 self.assertRaises(TypeError, lambda: badarg <= t1)
1851 self.assertRaises(TypeError, lambda: badarg < t1)
1852 self.assertRaises(TypeError, lambda: badarg > t1)
1853 self.assertRaises(TypeError, lambda: badarg >= t1)
1854
1855 def test_bad_constructor_arguments(self):
1856 # bad hours
1857 self.theclass(0, 0) # no exception
1858 self.theclass(23, 0) # no exception
1859 self.assertRaises(ValueError, self.theclass, -1, 0)
1860 self.assertRaises(ValueError, self.theclass, 24, 0)
1861 # bad minutes
1862 self.theclass(23, 0) # no exception
1863 self.theclass(23, 59) # no exception
1864 self.assertRaises(ValueError, self.theclass, 23, -1)
1865 self.assertRaises(ValueError, self.theclass, 23, 60)
1866 # bad seconds
1867 self.theclass(23, 59, 0) # no exception
1868 self.theclass(23, 59, 59) # no exception
1869 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1870 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1871 # bad microseconds
1872 self.theclass(23, 59, 59, 0) # no exception
1873 self.theclass(23, 59, 59, 999999) # no exception
1874 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1875 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1876
1877 def test_hash_equality(self):
1878 d = self.theclass(23, 30, 17)
1879 e = self.theclass(23, 30, 17)
1880 self.assertEqual(d, e)
1881 self.assertEqual(hash(d), hash(e))
1882
1883 dic = {d: 1}
1884 dic[e] = 2
1885 self.assertEqual(len(dic), 1)
1886 self.assertEqual(dic[d], 2)
1887 self.assertEqual(dic[e], 2)
1888
1889 d = self.theclass(0, 5, 17)
1890 e = self.theclass(0, 5, 17)
1891 self.assertEqual(d, e)
1892 self.assertEqual(hash(d), hash(e))
1893
1894 dic = {d: 1}
1895 dic[e] = 2
1896 self.assertEqual(len(dic), 1)
1897 self.assertEqual(dic[d], 2)
1898 self.assertEqual(dic[e], 2)
1899
1900 def test_isoformat(self):
1901 t = self.theclass(4, 5, 1, 123)
1902 self.assertEqual(t.isoformat(), "04:05:01.000123")
1903 self.assertEqual(t.isoformat(), str(t))
1904
1905 t = self.theclass()
1906 self.assertEqual(t.isoformat(), "00:00:00")
1907 self.assertEqual(t.isoformat(), str(t))
1908
1909 t = self.theclass(microsecond=1)
1910 self.assertEqual(t.isoformat(), "00:00:00.000001")
1911 self.assertEqual(t.isoformat(), str(t))
1912
1913 t = self.theclass(microsecond=10)
1914 self.assertEqual(t.isoformat(), "00:00:00.000010")
1915 self.assertEqual(t.isoformat(), str(t))
1916
1917 t = self.theclass(microsecond=100)
1918 self.assertEqual(t.isoformat(), "00:00:00.000100")
1919 self.assertEqual(t.isoformat(), str(t))
1920
1921 t = self.theclass(microsecond=1000)
1922 self.assertEqual(t.isoformat(), "00:00:00.001000")
1923 self.assertEqual(t.isoformat(), str(t))
1924
1925 t = self.theclass(microsecond=10000)
1926 self.assertEqual(t.isoformat(), "00:00:00.010000")
1927 self.assertEqual(t.isoformat(), str(t))
1928
1929 t = self.theclass(microsecond=100000)
1930 self.assertEqual(t.isoformat(), "00:00:00.100000")
1931 self.assertEqual(t.isoformat(), str(t))
1932
Thomas Wouterscf297e42007-02-23 15:07:44 +00001933 def test_1653736(self):
1934 # verify it doesn't accept extra keyword arguments
1935 t = self.theclass(second=1)
1936 self.assertRaises(TypeError, t.isoformat, foo=3)
1937
Tim Peters2a799bf2002-12-16 20:18:38 +00001938 def test_strftime(self):
1939 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001940 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001941 # A naive object replaces %z and %Z with empty strings.
1942 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1943
Eric Smith1ba31142007-09-11 18:06:02 +00001944 def test_format(self):
1945 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001946 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001947
1948 # check that a derived class's __str__() gets called
1949 class A(self.theclass):
1950 def __str__(self):
1951 return 'A'
1952 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001953 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001954
1955 # check that a derived class's strftime gets called
1956 class B(self.theclass):
1957 def strftime(self, format_spec):
1958 return 'B'
1959 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001960 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001961
1962 for fmt in ['%H %M %S',
1963 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001964 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1965 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1966 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001967
Tim Peters2a799bf2002-12-16 20:18:38 +00001968 def test_str(self):
1969 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1970 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1971 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1972 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1973 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1974
1975 def test_repr(self):
1976 name = 'datetime.' + self.theclass.__name__
1977 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1978 "%s(1, 2, 3, 4)" % name)
1979 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1980 "%s(10, 2, 3, 4000)" % name)
1981 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1982 "%s(0, 2, 3, 400000)" % name)
1983 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1984 "%s(12, 2, 3)" % name)
1985 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1986 "%s(23, 15)" % name)
1987
1988 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00001989 self.assertIsInstance(self.theclass.min, self.theclass)
1990 self.assertIsInstance(self.theclass.max, self.theclass)
1991 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001992 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001993
1994 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001995 args = 20, 59, 16, 64**2
1996 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001997 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001998 green = pickler.dumps(orig, proto)
1999 derived = unpickler.loads(green)
2000 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002001
Tim Peters604c0132004-06-07 23:04:33 +00002002 def test_pickling_subclass_time(self):
2003 args = 20, 59, 16, 64**2
2004 orig = SubclassTime(*args)
2005 for pickler, unpickler, proto in pickle_choices:
2006 green = pickler.dumps(orig, proto)
2007 derived = unpickler.loads(green)
2008 self.assertEqual(orig, derived)
2009
Tim Peters2a799bf2002-12-16 20:18:38 +00002010 def test_bool(self):
2011 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002012 self.assertTrue(cls(1))
2013 self.assertTrue(cls(0, 1))
2014 self.assertTrue(cls(0, 0, 1))
2015 self.assertTrue(cls(0, 0, 0, 1))
2016 self.assertTrue(not cls(0))
2017 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00002018
Tim Peters12bf3392002-12-24 05:41:27 +00002019 def test_replace(self):
2020 cls = self.theclass
2021 args = [1, 2, 3, 4]
2022 base = cls(*args)
2023 self.assertEqual(base, base.replace())
2024
2025 i = 0
2026 for name, newval in (("hour", 5),
2027 ("minute", 6),
2028 ("second", 7),
2029 ("microsecond", 8)):
2030 newargs = args[:]
2031 newargs[i] = newval
2032 expected = cls(*newargs)
2033 got = base.replace(**{name: newval})
2034 self.assertEqual(expected, got)
2035 i += 1
2036
2037 # Out of bounds.
2038 base = cls(1)
2039 self.assertRaises(ValueError, base.replace, hour=24)
2040 self.assertRaises(ValueError, base.replace, minute=-1)
2041 self.assertRaises(ValueError, base.replace, second=100)
2042 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2043
Tim Petersa98924a2003-05-17 05:55:19 +00002044 def test_subclass_time(self):
2045
2046 class C(self.theclass):
2047 theAnswer = 42
2048
2049 def __new__(cls, *args, **kws):
2050 temp = kws.copy()
2051 extra = temp.pop('extra')
2052 result = self.theclass.__new__(cls, *args, **temp)
2053 result.extra = extra
2054 return result
2055
2056 def newmeth(self, start):
2057 return start + self.hour + self.second
2058
2059 args = 4, 5, 6
2060
2061 dt1 = self.theclass(*args)
2062 dt2 = C(*args, **{'extra': 7})
2063
2064 self.assertEqual(dt2.__class__, C)
2065 self.assertEqual(dt2.theAnswer, 42)
2066 self.assertEqual(dt2.extra, 7)
2067 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2068 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2069
Armin Rigof4afb212005-11-07 07:15:48 +00002070 def test_backdoor_resistance(self):
2071 # see TestDate.test_backdoor_resistance().
2072 base = '2:59.0'
2073 for hour_byte in ' ', '9', chr(24), '\xff':
2074 self.assertRaises(TypeError, self.theclass,
2075 hour_byte + base[1:])
2076
Tim Peters855fe882002-12-22 03:43:39 +00002077# A mixin for classes with a tzinfo= argument. Subclasses must define
2078# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002079# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002080class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002081
Tim Petersbad8ff02002-12-30 20:52:32 +00002082 def test_argument_passing(self):
2083 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002084 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002085 class introspective(tzinfo):
2086 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002087 def utcoffset(self, dt):
2088 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002089 dst = utcoffset
2090
2091 obj = cls(1, 2, 3, tzinfo=introspective())
2092
Tim Peters0bf60bd2003-01-08 20:40:01 +00002093 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002094 self.assertEqual(obj.tzname(), expected)
2095
Tim Peters0bf60bd2003-01-08 20:40:01 +00002096 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002097 self.assertEqual(obj.utcoffset(), expected)
2098 self.assertEqual(obj.dst(), expected)
2099
Tim Peters855fe882002-12-22 03:43:39 +00002100 def test_bad_tzinfo_classes(self):
2101 cls = self.theclass
2102 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002103
Tim Peters855fe882002-12-22 03:43:39 +00002104 class NiceTry(object):
2105 def __init__(self): pass
2106 def utcoffset(self, dt): pass
2107 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2108
2109 class BetterTry(tzinfo):
2110 def __init__(self): pass
2111 def utcoffset(self, dt): pass
2112 b = BetterTry()
2113 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002114 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002115
2116 def test_utc_offset_out_of_bounds(self):
2117 class Edgy(tzinfo):
2118 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002119 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002120 def utcoffset(self, dt):
2121 return self.offset
2122
2123 cls = self.theclass
2124 for offset, legit in ((-1440, False),
2125 (-1439, True),
2126 (1439, True),
2127 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002128 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002129 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002130 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002131 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002132 else:
2133 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002134 if legit:
2135 aofs = abs(offset)
2136 h, m = divmod(aofs, 60)
2137 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002138 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002139 t = t.timetz()
2140 self.assertEqual(str(t), "01:02:03" + tag)
2141 else:
2142 self.assertRaises(ValueError, str, t)
2143
2144 def test_tzinfo_classes(self):
2145 cls = self.theclass
2146 class C1(tzinfo):
2147 def utcoffset(self, dt): return None
2148 def dst(self, dt): return None
2149 def tzname(self, dt): return None
2150 for t in (cls(1, 1, 1),
2151 cls(1, 1, 1, tzinfo=None),
2152 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002153 self.assertTrue(t.utcoffset() is None)
2154 self.assertTrue(t.dst() is None)
2155 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002156
Tim Peters855fe882002-12-22 03:43:39 +00002157 class C3(tzinfo):
2158 def utcoffset(self, dt): return timedelta(minutes=-1439)
2159 def dst(self, dt): return timedelta(minutes=1439)
2160 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002161 t = cls(1, 1, 1, tzinfo=C3())
2162 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2163 self.assertEqual(t.dst(), timedelta(minutes=1439))
2164 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002165
2166 # Wrong types.
2167 class C4(tzinfo):
2168 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002169 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002170 def tzname(self, dt): return 0
2171 t = cls(1, 1, 1, tzinfo=C4())
2172 self.assertRaises(TypeError, t.utcoffset)
2173 self.assertRaises(TypeError, t.dst)
2174 self.assertRaises(TypeError, t.tzname)
2175
2176 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002177 class C6(tzinfo):
2178 def utcoffset(self, dt): return timedelta(hours=-24)
2179 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002180 t = cls(1, 1, 1, tzinfo=C6())
2181 self.assertRaises(ValueError, t.utcoffset)
2182 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002183
2184 # Not a whole number of minutes.
2185 class C7(tzinfo):
2186 def utcoffset(self, dt): return timedelta(seconds=61)
2187 def dst(self, dt): return timedelta(microseconds=-81)
2188 t = cls(1, 1, 1, tzinfo=C7())
2189 self.assertRaises(ValueError, t.utcoffset)
2190 self.assertRaises(ValueError, t.dst)
2191
Tim Peters4c0db782002-12-26 05:01:19 +00002192 def test_aware_compare(self):
2193 cls = self.theclass
2194
Tim Peters60c76e42002-12-27 00:41:11 +00002195 # Ensure that utcoffset() gets ignored if the comparands have
2196 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002197 class OperandDependentOffset(tzinfo):
2198 def utcoffset(self, t):
2199 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002200 # d0 and d1 equal after adjustment
2201 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002202 else:
Tim Peters397301e2003-01-02 21:28:08 +00002203 # d2 off in the weeds
2204 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002205
2206 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2207 d0 = base.replace(minute=3)
2208 d1 = base.replace(minute=9)
2209 d2 = base.replace(minute=11)
2210 for x in d0, d1, d2:
2211 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002212 for op in lt, le, gt, ge, eq, ne:
2213 got = op(x, y)
2214 expected = op(x.minute, y.minute)
2215 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002216
2217 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002218 # Note that a time can't actually have an operand-depedent offset,
2219 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2220 # so skip this test for time.
2221 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002222 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2223 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2224 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2225 for x in d0, d1, d2:
2226 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002227 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002228 if (x is d0 or x is d1) and (y is d0 or y is d1):
2229 expected = 0
2230 elif x is y is d2:
2231 expected = 0
2232 elif x is d2:
2233 expected = -1
2234 else:
2235 assert y is d2
2236 expected = 1
2237 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002238
Tim Peters855fe882002-12-22 03:43:39 +00002239
Tim Peters0bf60bd2003-01-08 20:40:01 +00002240# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002241class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002242 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002243
2244 def test_empty(self):
2245 t = self.theclass()
2246 self.assertEqual(t.hour, 0)
2247 self.assertEqual(t.minute, 0)
2248 self.assertEqual(t.second, 0)
2249 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002250 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002251
Tim Peters2a799bf2002-12-16 20:18:38 +00002252 def test_zones(self):
2253 est = FixedOffset(-300, "EST", 1)
2254 utc = FixedOffset(0, "UTC", -2)
2255 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002256 t1 = time( 7, 47, tzinfo=est)
2257 t2 = time(12, 47, tzinfo=utc)
2258 t3 = time(13, 47, tzinfo=met)
2259 t4 = time(microsecond=40)
2260 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002261
2262 self.assertEqual(t1.tzinfo, est)
2263 self.assertEqual(t2.tzinfo, utc)
2264 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002265 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002266 self.assertEqual(t5.tzinfo, utc)
2267
Tim Peters855fe882002-12-22 03:43:39 +00002268 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2269 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2270 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002271 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002272 self.assertRaises(TypeError, t1.utcoffset, "no args")
2273
2274 self.assertEqual(t1.tzname(), "EST")
2275 self.assertEqual(t2.tzname(), "UTC")
2276 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002277 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002278 self.assertRaises(TypeError, t1.tzname, "no args")
2279
Tim Peters855fe882002-12-22 03:43:39 +00002280 self.assertEqual(t1.dst(), timedelta(minutes=1))
2281 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2282 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002283 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002284 self.assertRaises(TypeError, t1.dst, "no args")
2285
2286 self.assertEqual(hash(t1), hash(t2))
2287 self.assertEqual(hash(t1), hash(t3))
2288 self.assertEqual(hash(t2), hash(t3))
2289
2290 self.assertEqual(t1, t2)
2291 self.assertEqual(t1, t3)
2292 self.assertEqual(t2, t3)
2293 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2294 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2295 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2296
2297 self.assertEqual(str(t1), "07:47:00-05:00")
2298 self.assertEqual(str(t2), "12:47:00+00:00")
2299 self.assertEqual(str(t3), "13:47:00+01:00")
2300 self.assertEqual(str(t4), "00:00:00.000040")
2301 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2302
2303 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2304 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2305 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2306 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2307 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2308
Tim Peters0bf60bd2003-01-08 20:40:01 +00002309 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002310 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2311 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2312 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2313 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2314 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2315
2316 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2317 "07:47:00 %Z=EST %z=-0500")
2318 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2319 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2320
2321 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002322 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002323 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2324 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2325
Tim Petersb92bb712002-12-21 17:44:07 +00002326 # Check that an invalid tzname result raises an exception.
2327 class Badtzname(tzinfo):
2328 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002329 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002330 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2331 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002332
2333 def test_hash_edge_cases(self):
2334 # Offsets that overflow a basic time.
2335 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2336 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2337 self.assertEqual(hash(t1), hash(t2))
2338
2339 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2340 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2341 self.assertEqual(hash(t1), hash(t2))
2342
Tim Peters2a799bf2002-12-16 20:18:38 +00002343 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002344 # Try one without a tzinfo.
2345 args = 20, 59, 16, 64**2
2346 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002347 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002348 green = pickler.dumps(orig, proto)
2349 derived = unpickler.loads(green)
2350 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002351
2352 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002353 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002354 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002355 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002356 green = pickler.dumps(orig, proto)
2357 derived = unpickler.loads(green)
2358 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002359 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002360 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2361 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002362
2363 def test_more_bool(self):
2364 # Test cases with non-None tzinfo.
2365 cls = self.theclass
2366
2367 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002368 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002369
2370 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002371 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002372
2373 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002374 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002375
2376 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002377 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002378
2379 # Mostly ensuring this doesn't overflow internally.
2380 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002381 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002382
2383 # But this should yield a value error -- the utcoffset is bogus.
2384 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2385 self.assertRaises(ValueError, lambda: bool(t))
2386
2387 # Likewise.
2388 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2389 self.assertRaises(ValueError, lambda: bool(t))
2390
Tim Peters12bf3392002-12-24 05:41:27 +00002391 def test_replace(self):
2392 cls = self.theclass
2393 z100 = FixedOffset(100, "+100")
2394 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2395 args = [1, 2, 3, 4, z100]
2396 base = cls(*args)
2397 self.assertEqual(base, base.replace())
2398
2399 i = 0
2400 for name, newval in (("hour", 5),
2401 ("minute", 6),
2402 ("second", 7),
2403 ("microsecond", 8),
2404 ("tzinfo", zm200)):
2405 newargs = args[:]
2406 newargs[i] = newval
2407 expected = cls(*newargs)
2408 got = base.replace(**{name: newval})
2409 self.assertEqual(expected, got)
2410 i += 1
2411
2412 # Ensure we can get rid of a tzinfo.
2413 self.assertEqual(base.tzname(), "+100")
2414 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002415 self.assertTrue(base2.tzinfo is None)
2416 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002417
2418 # Ensure we can add one.
2419 base3 = base2.replace(tzinfo=z100)
2420 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002421 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002422
2423 # Out of bounds.
2424 base = cls(1)
2425 self.assertRaises(ValueError, base.replace, hour=24)
2426 self.assertRaises(ValueError, base.replace, minute=-1)
2427 self.assertRaises(ValueError, base.replace, second=100)
2428 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2429
Tim Peters60c76e42002-12-27 00:41:11 +00002430 def test_mixed_compare(self):
2431 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002432 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002433 self.assertEqual(t1, t2)
2434 t2 = t2.replace(tzinfo=None)
2435 self.assertEqual(t1, t2)
2436 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2437 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002438 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2439 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002440
Tim Peters0bf60bd2003-01-08 20:40:01 +00002441 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002442 class Varies(tzinfo):
2443 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002444 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002445 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002446 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002447 return self.offset
2448
2449 v = Varies()
2450 t1 = t2.replace(tzinfo=v)
2451 t2 = t2.replace(tzinfo=v)
2452 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2453 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2454 self.assertEqual(t1, t2)
2455
2456 # But if they're not identical, it isn't ignored.
2457 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002458 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002459
Tim Petersa98924a2003-05-17 05:55:19 +00002460 def test_subclass_timetz(self):
2461
2462 class C(self.theclass):
2463 theAnswer = 42
2464
2465 def __new__(cls, *args, **kws):
2466 temp = kws.copy()
2467 extra = temp.pop('extra')
2468 result = self.theclass.__new__(cls, *args, **temp)
2469 result.extra = extra
2470 return result
2471
2472 def newmeth(self, start):
2473 return start + self.hour + self.second
2474
2475 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2476
2477 dt1 = self.theclass(*args)
2478 dt2 = C(*args, **{'extra': 7})
2479
2480 self.assertEqual(dt2.__class__, C)
2481 self.assertEqual(dt2.theAnswer, 42)
2482 self.assertEqual(dt2.extra, 7)
2483 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2484 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2485
Tim Peters4c0db782002-12-26 05:01:19 +00002486
Tim Peters0bf60bd2003-01-08 20:40:01 +00002487# Testing datetime objects with a non-None tzinfo.
2488
Guido van Rossumd8faa362007-04-27 19:54:29 +00002489class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002490 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002491
2492 def test_trivial(self):
2493 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2494 self.assertEqual(dt.year, 1)
2495 self.assertEqual(dt.month, 2)
2496 self.assertEqual(dt.day, 3)
2497 self.assertEqual(dt.hour, 4)
2498 self.assertEqual(dt.minute, 5)
2499 self.assertEqual(dt.second, 6)
2500 self.assertEqual(dt.microsecond, 7)
2501 self.assertEqual(dt.tzinfo, None)
2502
2503 def test_even_more_compare(self):
2504 # The test_compare() and test_more_compare() inherited from TestDate
2505 # and TestDateTime covered non-tzinfo cases.
2506
2507 # Smallest possible after UTC adjustment.
2508 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2509 # Largest possible after UTC adjustment.
2510 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2511 tzinfo=FixedOffset(-1439, ""))
2512
2513 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002514 self.assertTrue(t1 < t2)
2515 self.assertTrue(t1 != t2)
2516 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002517
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002518 self.assertEqual(t1, t1)
2519 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002520
2521 # Equal afer adjustment.
2522 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2523 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2524 self.assertEqual(t1, t2)
2525
2526 # Change t1 not to subtract a minute, and t1 should be larger.
2527 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002528 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002529
2530 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2531 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002532 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002533
2534 # Back to the original t1, but make seconds resolve it.
2535 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2536 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002537 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002538
2539 # Likewise, but make microseconds resolve it.
2540 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2541 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002542 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002543
2544 # Make t2 naive and it should fail.
2545 t2 = self.theclass.min
2546 self.assertRaises(TypeError, lambda: t1 == t2)
2547 self.assertEqual(t2, t2)
2548
2549 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2550 class Naive(tzinfo):
2551 def utcoffset(self, dt): return None
2552 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2553 self.assertRaises(TypeError, lambda: t1 == t2)
2554 self.assertEqual(t2, t2)
2555
2556 # OTOH, it's OK to compare two of these mixing the two ways of being
2557 # naive.
2558 t1 = self.theclass(5, 6, 7)
2559 self.assertEqual(t1, t2)
2560
2561 # Try a bogus uctoffset.
2562 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002563 def utcoffset(self, dt):
2564 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002565 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2566 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002567 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002568
Tim Peters2a799bf2002-12-16 20:18:38 +00002569 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002570 # Try one without a tzinfo.
2571 args = 6, 7, 23, 20, 59, 1, 64**2
2572 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002573 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002574 green = pickler.dumps(orig, proto)
2575 derived = unpickler.loads(green)
2576 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002577
2578 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002579 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002580 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002581 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002582 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002583 green = pickler.dumps(orig, proto)
2584 derived = unpickler.loads(green)
2585 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002586 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002587 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2588 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002589
2590 def test_extreme_hashes(self):
2591 # If an attempt is made to hash these via subtracting the offset
2592 # then hashing a datetime object, OverflowError results. The
2593 # Python implementation used to blow up here.
2594 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2595 hash(t)
2596 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2597 tzinfo=FixedOffset(-1439, ""))
2598 hash(t)
2599
2600 # OTOH, an OOB offset should blow up.
2601 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2602 self.assertRaises(ValueError, hash, t)
2603
2604 def test_zones(self):
2605 est = FixedOffset(-300, "EST")
2606 utc = FixedOffset(0, "UTC")
2607 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002608 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2609 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2610 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002611 self.assertEqual(t1.tzinfo, est)
2612 self.assertEqual(t2.tzinfo, utc)
2613 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002614 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2615 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2616 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002617 self.assertEqual(t1.tzname(), "EST")
2618 self.assertEqual(t2.tzname(), "UTC")
2619 self.assertEqual(t3.tzname(), "MET")
2620 self.assertEqual(hash(t1), hash(t2))
2621 self.assertEqual(hash(t1), hash(t3))
2622 self.assertEqual(hash(t2), hash(t3))
2623 self.assertEqual(t1, t2)
2624 self.assertEqual(t1, t3)
2625 self.assertEqual(t2, t3)
2626 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2627 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2628 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002629 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002630 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2631 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2632 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2633
2634 def test_combine(self):
2635 met = FixedOffset(60, "MET")
2636 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002637 tz = time(18, 45, 3, 1234, tzinfo=met)
2638 dt = datetime.combine(d, tz)
2639 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002640 tzinfo=met))
2641
2642 def test_extract(self):
2643 met = FixedOffset(60, "MET")
2644 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2645 self.assertEqual(dt.date(), date(2002, 3, 4))
2646 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002647 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002648
2649 def test_tz_aware_arithmetic(self):
2650 import random
2651
2652 now = self.theclass.now()
2653 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002654 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002655 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002656 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002657 self.assertEqual(nowaware.timetz(), timeaware)
2658
2659 # Can't mix aware and non-aware.
2660 self.assertRaises(TypeError, lambda: now - nowaware)
2661 self.assertRaises(TypeError, lambda: nowaware - now)
2662
Tim Peters0bf60bd2003-01-08 20:40:01 +00002663 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002664 self.assertRaises(TypeError, lambda: now + nowaware)
2665 self.assertRaises(TypeError, lambda: nowaware + now)
2666 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2667
2668 # Subtracting should yield 0.
2669 self.assertEqual(now - now, timedelta(0))
2670 self.assertEqual(nowaware - nowaware, timedelta(0))
2671
2672 # Adding a delta should preserve tzinfo.
2673 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2674 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002675 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002676 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002677 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002678 self.assertEqual(nowawareplus, nowawareplus2)
2679
2680 # that - delta should be what we started with, and that - what we
2681 # started with should be delta.
2682 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002683 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002684 self.assertEqual(nowaware, diff)
2685 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2686 self.assertEqual(nowawareplus - nowaware, delta)
2687
2688 # Make up a random timezone.
2689 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002690 # Attach it to nowawareplus.
2691 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002692 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002693 # Make sure the difference takes the timezone adjustments into account.
2694 got = nowaware - nowawareplus
2695 # Expected: (nowaware base - nowaware offset) -
2696 # (nowawareplus base - nowawareplus offset) =
2697 # (nowaware base - nowawareplus base) +
2698 # (nowawareplus offset - nowaware offset) =
2699 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002700 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002701 self.assertEqual(got, expected)
2702
2703 # Try max possible difference.
2704 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2705 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2706 tzinfo=FixedOffset(-1439, "max"))
2707 maxdiff = max - min
2708 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2709 timedelta(minutes=2*1439))
2710
2711 def test_tzinfo_now(self):
2712 meth = self.theclass.now
2713 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2714 base = meth()
2715 # Try with and without naming the keyword.
2716 off42 = FixedOffset(42, "42")
2717 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002718 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002719 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002720 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002721 # Bad argument with and w/o naming the keyword.
2722 self.assertRaises(TypeError, meth, 16)
2723 self.assertRaises(TypeError, meth, tzinfo=16)
2724 # Bad keyword name.
2725 self.assertRaises(TypeError, meth, tinfo=off42)
2726 # Too many args.
2727 self.assertRaises(TypeError, meth, off42, off42)
2728
Tim Peters10cadce2003-01-23 19:58:02 +00002729 # We don't know which time zone we're in, and don't have a tzinfo
2730 # class to represent it, so seeing whether a tz argument actually
2731 # does a conversion is tricky.
2732 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2733 utc = FixedOffset(0, "utc", 0)
2734 for dummy in range(3):
2735 now = datetime.now(weirdtz)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002736 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002737 utcnow = datetime.utcnow().replace(tzinfo=utc)
2738 now2 = utcnow.astimezone(weirdtz)
2739 if abs(now - now2) < timedelta(seconds=30):
2740 break
2741 # Else the code is broken, or more than 30 seconds passed between
2742 # calls; assuming the latter, just try again.
2743 else:
2744 # Three strikes and we're out.
2745 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2746
Tim Peters2a799bf2002-12-16 20:18:38 +00002747 def test_tzinfo_fromtimestamp(self):
2748 import time
2749 meth = self.theclass.fromtimestamp
2750 ts = time.time()
2751 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2752 base = meth(ts)
2753 # Try with and without naming the keyword.
2754 off42 = FixedOffset(42, "42")
2755 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002756 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002757 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002758 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002759 # Bad argument with and w/o naming the keyword.
2760 self.assertRaises(TypeError, meth, ts, 16)
2761 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2762 # Bad keyword name.
2763 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2764 # Too many args.
2765 self.assertRaises(TypeError, meth, ts, off42, off42)
2766 # Too few args.
2767 self.assertRaises(TypeError, meth)
2768
Tim Peters2a44a8d2003-01-23 20:53:10 +00002769 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002770 timestamp = 1000000000
2771 utcdatetime = datetime.utcfromtimestamp(timestamp)
2772 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2773 # But on some flavor of Mac, it's nowhere near that. So we can't have
2774 # any idea here what time that actually is, we can only test that
2775 # relative changes match.
2776 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2777 tz = FixedOffset(utcoffset, "tz", 0)
2778 expected = utcdatetime + utcoffset
2779 got = datetime.fromtimestamp(timestamp, tz)
2780 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002781
Tim Peters2a799bf2002-12-16 20:18:38 +00002782 def test_tzinfo_utcnow(self):
2783 meth = self.theclass.utcnow
2784 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2785 base = meth()
2786 # Try with and without naming the keyword; for whatever reason,
2787 # utcnow() doesn't accept a tzinfo argument.
2788 off42 = FixedOffset(42, "42")
2789 self.assertRaises(TypeError, meth, off42)
2790 self.assertRaises(TypeError, meth, tzinfo=off42)
2791
2792 def test_tzinfo_utcfromtimestamp(self):
2793 import time
2794 meth = self.theclass.utcfromtimestamp
2795 ts = time.time()
2796 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2797 base = meth(ts)
2798 # Try with and without naming the keyword; for whatever reason,
2799 # utcfromtimestamp() doesn't accept a tzinfo argument.
2800 off42 = FixedOffset(42, "42")
2801 self.assertRaises(TypeError, meth, ts, off42)
2802 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2803
2804 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002805 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002806 # DST flag.
2807 class DST(tzinfo):
2808 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002809 if isinstance(dstvalue, int):
2810 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002811 self.dstvalue = dstvalue
2812 def dst(self, dt):
2813 return self.dstvalue
2814
2815 cls = self.theclass
2816 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2817 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2818 t = d.timetuple()
2819 self.assertEqual(1, t.tm_year)
2820 self.assertEqual(1, t.tm_mon)
2821 self.assertEqual(1, t.tm_mday)
2822 self.assertEqual(10, t.tm_hour)
2823 self.assertEqual(20, t.tm_min)
2824 self.assertEqual(30, t.tm_sec)
2825 self.assertEqual(0, t.tm_wday)
2826 self.assertEqual(1, t.tm_yday)
2827 self.assertEqual(flag, t.tm_isdst)
2828
2829 # dst() returns wrong type.
2830 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2831
2832 # dst() at the edge.
2833 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2834 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2835
2836 # dst() out of range.
2837 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2838 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2839
2840 def test_utctimetuple(self):
2841 class DST(tzinfo):
2842 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002843 if isinstance(dstvalue, int):
2844 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002845 self.dstvalue = dstvalue
2846 def dst(self, dt):
2847 return self.dstvalue
2848
2849 cls = self.theclass
2850 # This can't work: DST didn't implement utcoffset.
2851 self.assertRaises(NotImplementedError,
2852 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2853
2854 class UOFS(DST):
2855 def __init__(self, uofs, dofs=None):
2856 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002857 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002858 def utcoffset(self, dt):
2859 return self.uofs
2860
2861 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2862 # in effect for a UTC time.
2863 for dstvalue in -33, 33, 0, None:
2864 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2865 t = d.utctimetuple()
2866 self.assertEqual(d.year, t.tm_year)
2867 self.assertEqual(d.month, t.tm_mon)
2868 self.assertEqual(d.day, t.tm_mday)
2869 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2870 self.assertEqual(13, t.tm_min)
2871 self.assertEqual(d.second, t.tm_sec)
2872 self.assertEqual(d.weekday(), t.tm_wday)
2873 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2874 t.tm_yday)
2875 self.assertEqual(0, t.tm_isdst)
2876
2877 # At the edges, UTC adjustment can normalize into years out-of-range
2878 # for a datetime object. Ensure that a correct timetuple is
2879 # created anyway.
2880 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2881 # That goes back 1 minute less than a full day.
2882 t = tiny.utctimetuple()
2883 self.assertEqual(t.tm_year, MINYEAR-1)
2884 self.assertEqual(t.tm_mon, 12)
2885 self.assertEqual(t.tm_mday, 31)
2886 self.assertEqual(t.tm_hour, 0)
2887 self.assertEqual(t.tm_min, 1)
2888 self.assertEqual(t.tm_sec, 37)
2889 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2890 self.assertEqual(t.tm_isdst, 0)
2891
2892 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2893 # That goes forward 1 minute less than a full day.
2894 t = huge.utctimetuple()
2895 self.assertEqual(t.tm_year, MAXYEAR+1)
2896 self.assertEqual(t.tm_mon, 1)
2897 self.assertEqual(t.tm_mday, 1)
2898 self.assertEqual(t.tm_hour, 23)
2899 self.assertEqual(t.tm_min, 58)
2900 self.assertEqual(t.tm_sec, 37)
2901 self.assertEqual(t.tm_yday, 1)
2902 self.assertEqual(t.tm_isdst, 0)
2903
2904 def test_tzinfo_isoformat(self):
2905 zero = FixedOffset(0, "+00:00")
2906 plus = FixedOffset(220, "+03:40")
2907 minus = FixedOffset(-231, "-03:51")
2908 unknown = FixedOffset(None, "")
2909
2910 cls = self.theclass
2911 datestr = '0001-02-03'
2912 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002913 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002914 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2915 timestr = '04:05:59' + (us and '.987001' or '')
2916 ofsstr = ofs is not None and d.tzname() or ''
2917 tailstr = timestr + ofsstr
2918 iso = d.isoformat()
2919 self.assertEqual(iso, datestr + 'T' + tailstr)
2920 self.assertEqual(iso, d.isoformat('T'))
2921 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002922 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002923 self.assertEqual(str(d), datestr + ' ' + tailstr)
2924
Tim Peters12bf3392002-12-24 05:41:27 +00002925 def test_replace(self):
2926 cls = self.theclass
2927 z100 = FixedOffset(100, "+100")
2928 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2929 args = [1, 2, 3, 4, 5, 6, 7, z100]
2930 base = cls(*args)
2931 self.assertEqual(base, base.replace())
2932
2933 i = 0
2934 for name, newval in (("year", 2),
2935 ("month", 3),
2936 ("day", 4),
2937 ("hour", 5),
2938 ("minute", 6),
2939 ("second", 7),
2940 ("microsecond", 8),
2941 ("tzinfo", zm200)):
2942 newargs = args[:]
2943 newargs[i] = newval
2944 expected = cls(*newargs)
2945 got = base.replace(**{name: newval})
2946 self.assertEqual(expected, got)
2947 i += 1
2948
2949 # Ensure we can get rid of a tzinfo.
2950 self.assertEqual(base.tzname(), "+100")
2951 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002952 self.assertTrue(base2.tzinfo is None)
2953 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002954
2955 # Ensure we can add one.
2956 base3 = base2.replace(tzinfo=z100)
2957 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002958 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002959
2960 # Out of bounds.
2961 base = cls(2000, 2, 29)
2962 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002963
Tim Peters80475bb2002-12-25 07:40:55 +00002964 def test_more_astimezone(self):
2965 # The inherited test_astimezone covered some trivial and error cases.
2966 fnone = FixedOffset(None, "None")
2967 f44m = FixedOffset(44, "44")
2968 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2969
Tim Peters10cadce2003-01-23 19:58:02 +00002970 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002971 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002972 # Replacing with degenerate tzinfo raises an exception.
2973 self.assertRaises(ValueError, dt.astimezone, fnone)
2974 # Ditto with None tz.
2975 self.assertRaises(TypeError, dt.astimezone, None)
2976 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002977 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002978 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002979 self.assertEqual(x.date(), dt.date())
2980 self.assertEqual(x.time(), dt.time())
2981
2982 # Replacing with different tzinfo does adjust.
2983 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002984 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002985 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2986 expected = dt - dt.utcoffset() # in effect, convert to UTC
2987 expected += fm5h.utcoffset(dt) # and from there to local time
2988 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2989 self.assertEqual(got.date(), expected.date())
2990 self.assertEqual(got.time(), expected.time())
2991 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002992 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002993 self.assertEqual(got, expected)
2994
Tim Peters4c0db782002-12-26 05:01:19 +00002995 def test_aware_subtract(self):
2996 cls = self.theclass
2997
Tim Peters60c76e42002-12-27 00:41:11 +00002998 # Ensure that utcoffset() is ignored when the operands have the
2999 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00003000 class OperandDependentOffset(tzinfo):
3001 def utcoffset(self, t):
3002 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00003003 # d0 and d1 equal after adjustment
3004 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00003005 else:
Tim Peters397301e2003-01-02 21:28:08 +00003006 # d2 off in the weeds
3007 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00003008
3009 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3010 d0 = base.replace(minute=3)
3011 d1 = base.replace(minute=9)
3012 d2 = base.replace(minute=11)
3013 for x in d0, d1, d2:
3014 for y in d0, d1, d2:
3015 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00003016 expected = timedelta(minutes=x.minute - y.minute)
3017 self.assertEqual(got, expected)
3018
3019 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3020 # ignored.
3021 base = cls(8, 9, 10, 11, 12, 13, 14)
3022 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3023 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3024 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3025 for x in d0, d1, d2:
3026 for y in d0, d1, d2:
3027 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00003028 if (x is d0 or x is d1) and (y is d0 or y is d1):
3029 expected = timedelta(0)
3030 elif x is y is d2:
3031 expected = timedelta(0)
3032 elif x is d2:
3033 expected = timedelta(minutes=(11-59)-0)
3034 else:
3035 assert y is d2
3036 expected = timedelta(minutes=0-(11-59))
3037 self.assertEqual(got, expected)
3038
Tim Peters60c76e42002-12-27 00:41:11 +00003039 def test_mixed_compare(self):
3040 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00003041 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00003042 self.assertEqual(t1, t2)
3043 t2 = t2.replace(tzinfo=None)
3044 self.assertEqual(t1, t2)
3045 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3046 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003047 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3048 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003049
Tim Peters0bf60bd2003-01-08 20:40:01 +00003050 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003051 class Varies(tzinfo):
3052 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003053 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003054 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003055 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003056 return self.offset
3057
3058 v = Varies()
3059 t1 = t2.replace(tzinfo=v)
3060 t2 = t2.replace(tzinfo=v)
3061 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3062 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3063 self.assertEqual(t1, t2)
3064
3065 # But if they're not identical, it isn't ignored.
3066 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003067 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003068
Tim Petersa98924a2003-05-17 05:55:19 +00003069 def test_subclass_datetimetz(self):
3070
3071 class C(self.theclass):
3072 theAnswer = 42
3073
3074 def __new__(cls, *args, **kws):
3075 temp = kws.copy()
3076 extra = temp.pop('extra')
3077 result = self.theclass.__new__(cls, *args, **temp)
3078 result.extra = extra
3079 return result
3080
3081 def newmeth(self, start):
3082 return start + self.hour + self.year
3083
3084 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3085
3086 dt1 = self.theclass(*args)
3087 dt2 = C(*args, **{'extra': 7})
3088
3089 self.assertEqual(dt2.__class__, C)
3090 self.assertEqual(dt2.theAnswer, 42)
3091 self.assertEqual(dt2.extra, 7)
3092 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3093 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3094
Tim Peters621818b2002-12-29 23:44:49 +00003095# Pain to set up DST-aware tzinfo classes.
3096
3097def first_sunday_on_or_after(dt):
3098 days_to_go = 6 - dt.weekday()
3099 if days_to_go:
3100 dt += timedelta(days_to_go)
3101 return dt
3102
3103ZERO = timedelta(0)
3104HOUR = timedelta(hours=1)
3105DAY = timedelta(days=1)
3106# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3107DSTSTART = datetime(1, 4, 1, 2)
3108# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003109# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3110# being standard time on that day, there is no spelling in local time of
3111# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3112DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003113
3114class USTimeZone(tzinfo):
3115
3116 def __init__(self, hours, reprname, stdname, dstname):
3117 self.stdoffset = timedelta(hours=hours)
3118 self.reprname = reprname
3119 self.stdname = stdname
3120 self.dstname = dstname
3121
3122 def __repr__(self):
3123 return self.reprname
3124
3125 def tzname(self, dt):
3126 if self.dst(dt):
3127 return self.dstname
3128 else:
3129 return self.stdname
3130
3131 def utcoffset(self, dt):
3132 return self.stdoffset + self.dst(dt)
3133
3134 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003135 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003136 # An exception instead may be sensible here, in one or more of
3137 # the cases.
3138 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003139 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003140
3141 # Find first Sunday in April.
3142 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3143 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3144
3145 # Find last Sunday in October.
3146 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3147 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3148
Tim Peters621818b2002-12-29 23:44:49 +00003149 # Can't compare naive to aware objects, so strip the timezone from
3150 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003151 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003152 return HOUR
3153 else:
3154 return ZERO
3155
Tim Peters521fc152002-12-31 17:36:56 +00003156Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3157Central = USTimeZone(-6, "Central", "CST", "CDT")
3158Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3159Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003160utc_real = FixedOffset(0, "UTC", 0)
3161# For better test coverage, we want another flavor of UTC that's west of
3162# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003163utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003164
3165class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003166 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003167 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003168 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003169
Tim Peters0bf60bd2003-01-08 20:40:01 +00003170 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003171
Tim Peters521fc152002-12-31 17:36:56 +00003172 # Check a time that's inside DST.
3173 def checkinside(self, dt, tz, utc, dston, dstoff):
3174 self.assertEqual(dt.dst(), HOUR)
3175
3176 # Conversion to our own timezone is always an identity.
3177 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003178
3179 asutc = dt.astimezone(utc)
3180 there_and_back = asutc.astimezone(tz)
3181
3182 # Conversion to UTC and back isn't always an identity here,
3183 # because there are redundant spellings (in local time) of
3184 # UTC time when DST begins: the clock jumps from 1:59:59
3185 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3186 # make sense then. The classes above treat 2:MM:SS as
3187 # daylight time then (it's "after 2am"), really an alias
3188 # for 1:MM:SS standard time. The latter form is what
3189 # conversion back from UTC produces.
3190 if dt.date() == dston.date() and dt.hour == 2:
3191 # We're in the redundant hour, and coming back from
3192 # UTC gives the 1:MM:SS standard-time spelling.
3193 self.assertEqual(there_and_back + HOUR, dt)
3194 # Although during was considered to be in daylight
3195 # time, there_and_back is not.
3196 self.assertEqual(there_and_back.dst(), ZERO)
3197 # They're the same times in UTC.
3198 self.assertEqual(there_and_back.astimezone(utc),
3199 dt.astimezone(utc))
3200 else:
3201 # We're not in the redundant hour.
3202 self.assertEqual(dt, there_and_back)
3203
Tim Peters327098a2003-01-20 22:54:38 +00003204 # Because we have a redundant spelling when DST begins, there is
3205 # (unforunately) an hour when DST ends that can't be spelled at all in
3206 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3207 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3208 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3209 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3210 # expressed in local time. Nevertheless, we want conversion back
3211 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003212 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003213 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003214 if dt.date() == dstoff.date() and dt.hour == 0:
3215 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003216 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003217 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3218 nexthour_utc += HOUR
3219 nexthour_tz = nexthour_utc.astimezone(tz)
3220 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003221 else:
Tim Peters327098a2003-01-20 22:54:38 +00003222 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003223
3224 # Check a time that's outside DST.
3225 def checkoutside(self, dt, tz, utc):
3226 self.assertEqual(dt.dst(), ZERO)
3227
3228 # Conversion to our own timezone is always an identity.
3229 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003230
3231 # Converting to UTC and back is an identity too.
3232 asutc = dt.astimezone(utc)
3233 there_and_back = asutc.astimezone(tz)
3234 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003235
Tim Peters1024bf82002-12-30 17:09:40 +00003236 def convert_between_tz_and_utc(self, tz, utc):
3237 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003238 # Because 1:MM on the day DST ends is taken as being standard time,
3239 # there is no spelling in tz for the last hour of daylight time.
3240 # For purposes of the test, the last hour of DST is 0:MM, which is
3241 # taken as being daylight time (and 1:MM is taken as being standard
3242 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003243 dstoff = self.dstoff.replace(tzinfo=tz)
3244 for delta in (timedelta(weeks=13),
3245 DAY,
3246 HOUR,
3247 timedelta(minutes=1),
3248 timedelta(microseconds=1)):
3249
Tim Peters521fc152002-12-31 17:36:56 +00003250 self.checkinside(dston, tz, utc, dston, dstoff)
3251 for during in dston + delta, dstoff - delta:
3252 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003253
Tim Peters521fc152002-12-31 17:36:56 +00003254 self.checkoutside(dstoff, tz, utc)
3255 for outside in dston - delta, dstoff + delta:
3256 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003257
Tim Peters621818b2002-12-29 23:44:49 +00003258 def test_easy(self):
3259 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003260 self.convert_between_tz_and_utc(Eastern, utc_real)
3261 self.convert_between_tz_and_utc(Pacific, utc_real)
3262 self.convert_between_tz_and_utc(Eastern, utc_fake)
3263 self.convert_between_tz_and_utc(Pacific, utc_fake)
3264 # The next is really dancing near the edge. It works because
3265 # Pacific and Eastern are far enough apart that their "problem
3266 # hours" don't overlap.
3267 self.convert_between_tz_and_utc(Eastern, Pacific)
3268 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003269 # OTOH, these fail! Don't enable them. The difficulty is that
3270 # the edge case tests assume that every hour is representable in
3271 # the "utc" class. This is always true for a fixed-offset tzinfo
3272 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3273 # For these adjacent DST-aware time zones, the range of time offsets
3274 # tested ends up creating hours in the one that aren't representable
3275 # in the other. For the same reason, we would see failures in the
3276 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3277 # offset deltas in convert_between_tz_and_utc().
3278 #
3279 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3280 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003281
Tim Petersf3615152003-01-01 21:51:37 +00003282 def test_tricky(self):
3283 # 22:00 on day before daylight starts.
3284 fourback = self.dston - timedelta(hours=4)
3285 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003286 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003287 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3288 # 2", we should get the 3 spelling.
3289 # If we plug 22:00 the day before into Eastern, it "looks like std
3290 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3291 # to 22:00 lands on 2:00, which makes no sense in local time (the
3292 # local clock jumps from 1 to 3). The point here is to make sure we
3293 # get the 3 spelling.
3294 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003295 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003296 self.assertEqual(expected, got)
3297
3298 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3299 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003300 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003301 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3302 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3303 # spelling.
3304 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003305 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003306 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003307
Tim Petersadf64202003-01-04 06:03:15 +00003308 # Now on the day DST ends, we want "repeat an hour" behavior.
3309 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3310 # EST 23:MM 0:MM 1:MM 2:MM
3311 # EDT 0:MM 1:MM 2:MM 3:MM
3312 # wall 0:MM 1:MM 1:MM 2:MM against these
3313 for utc in utc_real, utc_fake:
3314 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003315 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003316 # Convert that to UTC.
3317 first_std_hour -= tz.utcoffset(None)
3318 # Adjust for possibly fake UTC.
3319 asutc = first_std_hour + utc.utcoffset(None)
3320 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3321 # tz=Eastern.
3322 asutcbase = asutc.replace(tzinfo=utc)
3323 for tzhour in (0, 1, 1, 2):
3324 expectedbase = self.dstoff.replace(hour=tzhour)
3325 for minute in 0, 30, 59:
3326 expected = expectedbase.replace(minute=minute)
3327 asutc = asutcbase.replace(minute=minute)
3328 astz = asutc.astimezone(tz)
3329 self.assertEqual(astz.replace(tzinfo=None), expected)
3330 asutcbase += HOUR
3331
3332
Tim Peters710fb152003-01-02 19:35:54 +00003333 def test_bogus_dst(self):
3334 class ok(tzinfo):
3335 def utcoffset(self, dt): return HOUR
3336 def dst(self, dt): return HOUR
3337
3338 now = self.theclass.now().replace(tzinfo=utc_real)
3339 # Doesn't blow up.
3340 now.astimezone(ok())
3341
3342 # Does blow up.
3343 class notok(ok):
3344 def dst(self, dt): return None
3345 self.assertRaises(ValueError, now.astimezone, notok())
3346
Tim Peters52dcce22003-01-23 16:36:11 +00003347 def test_fromutc(self):
3348 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3349 now = datetime.utcnow().replace(tzinfo=utc_real)
3350 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3351 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3352 enow = Eastern.fromutc(now) # doesn't blow up
3353 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3354 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3355 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3356
3357 # Always converts UTC to standard time.
3358 class FauxUSTimeZone(USTimeZone):
3359 def fromutc(self, dt):
3360 return dt + self.stdoffset
3361 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3362
3363 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3364 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3365 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3366
3367 # Check around DST start.
3368 start = self.dston.replace(hour=4, tzinfo=Eastern)
3369 fstart = start.replace(tzinfo=FEastern)
3370 for wall in 23, 0, 1, 3, 4, 5:
3371 expected = start.replace(hour=wall)
3372 if wall == 23:
3373 expected -= timedelta(days=1)
3374 got = Eastern.fromutc(start)
3375 self.assertEqual(expected, got)
3376
3377 expected = fstart + FEastern.stdoffset
3378 got = FEastern.fromutc(fstart)
3379 self.assertEqual(expected, got)
3380
3381 # Ensure astimezone() calls fromutc() too.
3382 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3383 self.assertEqual(expected, got)
3384
3385 start += HOUR
3386 fstart += HOUR
3387
3388 # Check around DST end.
3389 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3390 fstart = start.replace(tzinfo=FEastern)
3391 for wall in 0, 1, 1, 2, 3, 4:
3392 expected = start.replace(hour=wall)
3393 got = Eastern.fromutc(start)
3394 self.assertEqual(expected, got)
3395
3396 expected = fstart + FEastern.stdoffset
3397 got = FEastern.fromutc(fstart)
3398 self.assertEqual(expected, got)
3399
3400 # Ensure astimezone() calls fromutc() too.
3401 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3402 self.assertEqual(expected, got)
3403
3404 start += HOUR
3405 fstart += HOUR
3406
Tim Peters710fb152003-01-02 19:35:54 +00003407
Tim Peters528ca532004-09-16 01:30:50 +00003408#############################################################################
3409# oddballs
3410
3411class Oddballs(unittest.TestCase):
3412
3413 def test_bug_1028306(self):
3414 # Trying to compare a date to a datetime should act like a mixed-
3415 # type comparison, despite that datetime is a subclass of date.
3416 as_date = date.today()
3417 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003418 self.assertTrue(as_date != as_datetime)
3419 self.assertTrue(as_datetime != as_date)
3420 self.assertTrue(not as_date == as_datetime)
3421 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003422 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3423 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3424 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3425 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3426 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3427 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3428 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3429 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3430
3431 # Neverthelss, comparison should work with the base-class (date)
3432 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003433 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003434 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003435 as_different = as_datetime.replace(day= different_day)
3436 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003437
3438 # And date should compare with other subclasses of date. If a
3439 # subclass wants to stop this, it's up to the subclass to do so.
3440 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3441 self.assertEqual(as_date, date_sc)
3442 self.assertEqual(date_sc, as_date)
3443
3444 # Ditto for datetimes.
3445 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3446 as_date.day, 0, 0, 0)
3447 self.assertEqual(as_datetime, datetime_sc)
3448 self.assertEqual(datetime_sc, as_datetime)
3449
Tim Peters2a799bf2002-12-16 20:18:38 +00003450def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003451 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003452
3453if __name__ == "__main__":
3454 test_main()