blob: b7518204653f76efee10d64d8e791880bbaef6c6 [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
28#############################################################################
29# module tests
30
31class TestModule(unittest.TestCase):
32
33 def test_constants(self):
34 import datetime
35 self.assertEqual(datetime.MINYEAR, 1)
36 self.assertEqual(datetime.MAXYEAR, 9999)
37
38#############################################################################
39# tzinfo tests
40
41class FixedOffset(tzinfo):
42 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000043 if isinstance(offset, int):
44 offset = timedelta(minutes=offset)
45 if isinstance(dstoffset, int):
46 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000047 self.__offset = offset
48 self.__name = name
49 self.__dstoffset = dstoffset
50 def __repr__(self):
51 return self.__name.lower()
52 def utcoffset(self, dt):
53 return self.__offset
54 def tzname(self, dt):
55 return self.__name
56 def dst(self, dt):
57 return self.__dstoffset
58
Tim Petersfb8472c2002-12-21 05:04:42 +000059class PicklableFixedOffset(FixedOffset):
60 def __init__(self, offset=None, name=None, dstoffset=None):
61 FixedOffset.__init__(self, offset, name, dstoffset)
62
Tim Peters2a799bf2002-12-16 20:18:38 +000063class TestTZInfo(unittest.TestCase):
64
65 def test_non_abstractness(self):
66 # In order to allow subclasses to get pickled, the C implementation
67 # wasn't able to get away with having __init__ raise
68 # NotImplementedError.
69 useless = tzinfo()
70 dt = datetime.max
71 self.assertRaises(NotImplementedError, useless.tzname, dt)
72 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
73 self.assertRaises(NotImplementedError, useless.dst, dt)
74
75 def test_subclass_must_override(self):
76 class NotEnough(tzinfo):
77 def __init__(self, offset, name):
78 self.__offset = offset
79 self.__name = name
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000080 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000081 ne = NotEnough(3, "NotByALongShot")
Ezio Melottie9615932010-01-24 19:26:24 +000082 self.assertIsInstance(ne, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000083
84 dt = datetime.now()
85 self.assertRaises(NotImplementedError, ne.tzname, dt)
86 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
87 self.assertRaises(NotImplementedError, ne.dst, dt)
88
89 def test_normal(self):
90 fo = FixedOffset(3, "Three")
Ezio Melottie9615932010-01-24 19:26:24 +000091 self.assertIsInstance(fo, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000092 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +000093 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +000094 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +000095 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +000096
97 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +000098 # There's no point to pickling tzinfo objects on their own (they
99 # carry no data), but they need to be picklable anyway else
100 # concrete subclasses can't be pickled.
101 orig = tzinfo.__new__(tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000102 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000103 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000104 green = pickler.dumps(orig, proto)
105 derived = unpickler.loads(green)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000106 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000107
108 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000109 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000110 offset = timedelta(minutes=-300)
111 orig = PicklableFixedOffset(offset, 'cookie')
Ezio Melottie9615932010-01-24 19:26:24 +0000112 self.assertIsInstance(orig, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000113 self.assertTrue(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000114 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000115 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000116 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000117 green = pickler.dumps(orig, proto)
118 derived = unpickler.loads(green)
Ezio Melottie9615932010-01-24 19:26:24 +0000119 self.assertIsInstance(derived, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000120 self.assertTrue(type(derived) is PicklableFixedOffset)
Tim Petersf2715e02003-02-19 02:35:07 +0000121 self.assertEqual(derived.utcoffset(None), offset)
122 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000123
124#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000125# Base clase for testing a particular aspect of timedelta, time, date and
126# datetime comparisons.
127
Guido van Rossumd8faa362007-04-27 19:54:29 +0000128class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000129 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
130
131 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
132 # legit constructor.
133
134 def test_harmless_mixed_comparison(self):
135 me = self.theclass(1, 1, 1)
136
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000137 self.assertFalse(me == ())
138 self.assertTrue(me != ())
139 self.assertFalse(() == me)
140 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000141
Benjamin Peterson577473f2010-01-19 00:09:57 +0000142 self.assertIn(me, [1, 20, [], me])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000143 self.assertIn([], [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000144
145 def test_harmful_mixed_comparison(self):
146 me = self.theclass(1, 1, 1)
147
148 self.assertRaises(TypeError, lambda: me < ())
149 self.assertRaises(TypeError, lambda: me <= ())
150 self.assertRaises(TypeError, lambda: me > ())
151 self.assertRaises(TypeError, lambda: me >= ())
152
153 self.assertRaises(TypeError, lambda: () < me)
154 self.assertRaises(TypeError, lambda: () <= me)
155 self.assertRaises(TypeError, lambda: () > me)
156 self.assertRaises(TypeError, lambda: () >= me)
157
Tim Peters07534a62003-02-07 22:50:28 +0000158#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000159# timedelta tests
160
Guido van Rossumd8faa362007-04-27 19:54:29 +0000161class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000162
163 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000164
165 def test_constructor(self):
166 eq = self.assertEqual
167 td = timedelta
168
169 # Check keyword args to constructor
170 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
171 milliseconds=0, microseconds=0))
172 eq(td(1), td(days=1))
173 eq(td(0, 1), td(seconds=1))
174 eq(td(0, 0, 1), td(microseconds=1))
175 eq(td(weeks=1), td(days=7))
176 eq(td(days=1), td(hours=24))
177 eq(td(hours=1), td(minutes=60))
178 eq(td(minutes=1), td(seconds=60))
179 eq(td(seconds=1), td(milliseconds=1000))
180 eq(td(milliseconds=1), td(microseconds=1000))
181
182 # Check float args to constructor
183 eq(td(weeks=1.0/7), td(days=1))
184 eq(td(days=1.0/24), td(hours=1))
185 eq(td(hours=1.0/60), td(minutes=1))
186 eq(td(minutes=1.0/60), td(seconds=1))
187 eq(td(seconds=0.001), td(milliseconds=1))
188 eq(td(milliseconds=0.001), td(microseconds=1))
189
190 def test_computations(self):
191 eq = self.assertEqual
192 td = timedelta
193
194 a = td(7) # One week
195 b = td(0, 60) # One minute
196 c = td(0, 0, 1000) # One millisecond
197 eq(a+b+c, td(7, 60, 1000))
198 eq(a-b, td(6, 24*3600 - 60))
199 eq(-a, td(-7))
200 eq(+a, td(7))
201 eq(-b, td(-1, 24*3600 - 60))
202 eq(-c, td(-1, 24*3600 - 1, 999000))
203 eq(abs(a), a)
204 eq(abs(-a), a)
205 eq(td(6, 24*3600), a)
206 eq(td(0, 0, 60*1000000), b)
207 eq(a*10, td(70))
208 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000209 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000210 eq(b*10, td(0, 600))
211 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000212 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000213 eq(c*10, td(0, 0, 10000))
214 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000215 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000216 eq(a*-1, -a)
217 eq(b*-2, -b-b)
218 eq(c*-2, -c+-c)
219 eq(b*(60*24), (b*60)*24)
220 eq(b*(60*24), (60*b)*24)
221 eq(c*1000, td(0, 1))
222 eq(1000*c, td(0, 1))
223 eq(a//7, td(1))
224 eq(b//10, td(0, 6))
225 eq(c//1000, td(0, 0, 1))
226 eq(a//10, td(0, 7*24*360))
227 eq(a//3600000, td(0, 0, 7*24*1000))
228
229 def test_disallowed_computations(self):
230 a = timedelta(42)
231
Mark Dickinson5c2db372009-12-05 20:28:34 +0000232 # Add/sub ints or floats should be illegal
233 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000234 self.assertRaises(TypeError, lambda: a+i)
235 self.assertRaises(TypeError, lambda: a-i)
236 self.assertRaises(TypeError, lambda: i+a)
237 self.assertRaises(TypeError, lambda: i-a)
238
239 # Mul/div by float isn't supported.
240 x = 2.3
241 self.assertRaises(TypeError, lambda: a*x)
242 self.assertRaises(TypeError, lambda: x*a)
243 self.assertRaises(TypeError, lambda: a/x)
244 self.assertRaises(TypeError, lambda: x/a)
245 self.assertRaises(TypeError, lambda: a // x)
246 self.assertRaises(TypeError, lambda: x // a)
247
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000248 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000249 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000250 zero = 0
251 self.assertRaises(TypeError, lambda: zero // a)
252 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Tim Peters2a799bf2002-12-16 20:18:38 +0000253
254 def test_basic_attributes(self):
255 days, seconds, us = 1, 7, 31
256 td = timedelta(days, seconds, us)
257 self.assertEqual(td.days, days)
258 self.assertEqual(td.seconds, seconds)
259 self.assertEqual(td.microseconds, us)
260
Antoine Pitroube6859d2009-11-25 23:02:32 +0000261 def test_total_seconds(self):
262 td = timedelta(days=365)
263 self.assertEqual(td.total_seconds(), 31536000.0)
264 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
265 td = timedelta(seconds=total_seconds)
266 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson0381e3f2010-05-08 14:35:02 +0000267 # Issue8644: Test that td.total_seconds() has the same
268 # accuracy as td / timedelta(seconds=1).
269 for ms in [-1, -2, -123]:
270 td = timedelta(microseconds=ms)
271 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
Antoine Pitroube6859d2009-11-25 23:02:32 +0000272
Tim Peters2a799bf2002-12-16 20:18:38 +0000273 def test_carries(self):
274 t1 = timedelta(days=100,
275 weeks=-7,
276 hours=-24*(100-49),
277 minutes=-3,
278 seconds=12,
279 microseconds=(3*60 - 12) * 1e6 + 1)
280 t2 = timedelta(microseconds=1)
281 self.assertEqual(t1, t2)
282
283 def test_hash_equality(self):
284 t1 = timedelta(days=100,
285 weeks=-7,
286 hours=-24*(100-49),
287 minutes=-3,
288 seconds=12,
289 microseconds=(3*60 - 12) * 1000000)
290 t2 = timedelta()
291 self.assertEqual(hash(t1), hash(t2))
292
293 t1 += timedelta(weeks=7)
294 t2 += timedelta(days=7*7)
295 self.assertEqual(t1, t2)
296 self.assertEqual(hash(t1), hash(t2))
297
298 d = {t1: 1}
299 d[t2] = 2
300 self.assertEqual(len(d), 1)
301 self.assertEqual(d[t1], 2)
302
303 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000304 args = 12, 34, 56
305 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000306 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000307 green = pickler.dumps(orig, proto)
308 derived = unpickler.loads(green)
309 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000310
311 def test_compare(self):
312 t1 = timedelta(2, 3, 4)
313 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000314 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000315 self.assertTrue(t1 <= t2)
316 self.assertTrue(t1 >= t2)
317 self.assertTrue(not t1 != t2)
318 self.assertTrue(not t1 < t2)
319 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000320
321 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
322 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000323 self.assertTrue(t1 < t2)
324 self.assertTrue(t2 > t1)
325 self.assertTrue(t1 <= t2)
326 self.assertTrue(t2 >= t1)
327 self.assertTrue(t1 != t2)
328 self.assertTrue(t2 != t1)
329 self.assertTrue(not t1 == t2)
330 self.assertTrue(not t2 == t1)
331 self.assertTrue(not t1 > t2)
332 self.assertTrue(not t2 < t1)
333 self.assertTrue(not t1 >= t2)
334 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000335
Tim Peters68124bb2003-02-08 03:46:31 +0000336 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000337 self.assertEqual(t1 == badarg, False)
338 self.assertEqual(t1 != badarg, True)
339 self.assertEqual(badarg == t1, False)
340 self.assertEqual(badarg != t1, True)
341
Tim Peters2a799bf2002-12-16 20:18:38 +0000342 self.assertRaises(TypeError, lambda: t1 <= badarg)
343 self.assertRaises(TypeError, lambda: t1 < badarg)
344 self.assertRaises(TypeError, lambda: t1 > badarg)
345 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000346 self.assertRaises(TypeError, lambda: badarg <= t1)
347 self.assertRaises(TypeError, lambda: badarg < t1)
348 self.assertRaises(TypeError, lambda: badarg > t1)
349 self.assertRaises(TypeError, lambda: badarg >= t1)
350
351 def test_str(self):
352 td = timedelta
353 eq = self.assertEqual
354
355 eq(str(td(1)), "1 day, 0:00:00")
356 eq(str(td(-1)), "-1 day, 0:00:00")
357 eq(str(td(2)), "2 days, 0:00:00")
358 eq(str(td(-2)), "-2 days, 0:00:00")
359
360 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
361 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
362 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
363 "-210 days, 23:12:34")
364
365 eq(str(td(milliseconds=1)), "0:00:00.001000")
366 eq(str(td(microseconds=3)), "0:00:00.000003")
367
368 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
369 microseconds=999999)),
370 "999999999 days, 23:59:59.999999")
371
372 def test_roundtrip(self):
373 for td in (timedelta(days=999999999, hours=23, minutes=59,
374 seconds=59, microseconds=999999),
375 timedelta(days=-999999999),
376 timedelta(days=1, seconds=2, microseconds=3)):
377
378 # Verify td -> string -> td identity.
379 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000380 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000381 s = s[9:]
382 td2 = eval(s)
383 self.assertEqual(td, td2)
384
385 # Verify identity via reconstructing from pieces.
386 td2 = timedelta(td.days, td.seconds, td.microseconds)
387 self.assertEqual(td, td2)
388
389 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000390 self.assertIsInstance(timedelta.min, timedelta)
391 self.assertIsInstance(timedelta.max, timedelta)
392 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000393 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000394 self.assertEqual(timedelta.min, timedelta(-999999999))
395 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
396 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
397
398 def test_overflow(self):
399 tiny = timedelta.resolution
400
401 td = timedelta.min + tiny
402 td -= tiny # no problem
403 self.assertRaises(OverflowError, td.__sub__, tiny)
404 self.assertRaises(OverflowError, td.__add__, -tiny)
405
406 td = timedelta.max - tiny
407 td += tiny # no problem
408 self.assertRaises(OverflowError, td.__add__, tiny)
409 self.assertRaises(OverflowError, td.__sub__, -tiny)
410
411 self.assertRaises(OverflowError, lambda: -timedelta.max)
412
413 def test_microsecond_rounding(self):
414 td = timedelta
415 eq = self.assertEqual
416
417 # Single-field rounding.
418 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
419 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
420 eq(td(milliseconds=0.6/1000), td(microseconds=1))
421 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
422
423 # Rounding due to contributions from more than one field.
424 us_per_hour = 3600e6
425 us_per_day = us_per_hour * 24
426 eq(td(days=.4/us_per_day), td(0))
427 eq(td(hours=.2/us_per_hour), td(0))
428 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
429
430 eq(td(days=-.4/us_per_day), td(0))
431 eq(td(hours=-.2/us_per_hour), td(0))
432 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
433
434 def test_massive_normalization(self):
435 td = timedelta(microseconds=-1)
436 self.assertEqual((td.days, td.seconds, td.microseconds),
437 (-1, 24*3600-1, 999999))
438
439 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000440 self.assertTrue(timedelta(1))
441 self.assertTrue(timedelta(0, 1))
442 self.assertTrue(timedelta(0, 0, 1))
443 self.assertTrue(timedelta(microseconds=1))
444 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000445
Tim Petersb0c854d2003-05-17 15:57:00 +0000446 def test_subclass_timedelta(self):
447
448 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000449 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000450 def from_td(td):
451 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000452
453 def as_hours(self):
454 sum = (self.days * 24 +
455 self.seconds / 3600.0 +
456 self.microseconds / 3600e6)
457 return round(sum)
458
459 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000460 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000461 self.assertEqual(t1.as_hours(), 24)
462
463 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000464 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000465 self.assertEqual(t2.as_hours(), -25)
466
467 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000468 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000469 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000470 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000471 self.assertEqual(t3.days, t4.days)
472 self.assertEqual(t3.seconds, t4.seconds)
473 self.assertEqual(t3.microseconds, t4.microseconds)
474 self.assertEqual(str(t3), str(t4))
475 self.assertEqual(t4.as_hours(), -1)
476
Mark Dickinson7c186e22010-04-20 22:32:49 +0000477 def test_division(self):
478 t = timedelta(hours=1, minutes=24, seconds=19)
479 second = timedelta(seconds=1)
480 self.assertEqual(t / second, 5059.0)
481 self.assertEqual(t // second, 5059)
482
483 t = timedelta(minutes=2, seconds=30)
484 minute = timedelta(minutes=1)
485 self.assertEqual(t / minute, 2.5)
486 self.assertEqual(t // minute, 2)
487
488 zerotd = timedelta(0)
489 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
490 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
491
492 self.assertRaises(TypeError, truediv, t, 2)
493 # note: floor division of a timedelta by an integer *is*
494 # currently permitted.
495
496 def test_remainder(self):
497 t = timedelta(minutes=2, seconds=30)
498 minute = timedelta(minutes=1)
499 r = t % minute
500 self.assertEqual(r, timedelta(seconds=30))
501
502 t = timedelta(minutes=-2, seconds=30)
503 r = t % minute
504 self.assertEqual(r, timedelta(seconds=30))
505
506 zerotd = timedelta(0)
507 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
508
509 self.assertRaises(TypeError, mod, t, 10)
510
511 def test_divmod(self):
512 t = timedelta(minutes=2, seconds=30)
513 minute = timedelta(minutes=1)
514 q, r = divmod(t, minute)
515 self.assertEqual(q, 2)
516 self.assertEqual(r, timedelta(seconds=30))
517
518 t = timedelta(minutes=-2, seconds=30)
519 q, r = divmod(t, minute)
520 self.assertEqual(q, -2)
521 self.assertEqual(r, timedelta(seconds=30))
522
523 zerotd = timedelta(0)
524 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
525
526 self.assertRaises(TypeError, divmod, t, 10)
527
528
Tim Peters2a799bf2002-12-16 20:18:38 +0000529#############################################################################
530# date tests
531
532class TestDateOnly(unittest.TestCase):
533 # Tests here won't pass if also run on datetime objects, so don't
534 # subclass this to test datetimes too.
535
536 def test_delta_non_days_ignored(self):
537 dt = date(2000, 1, 2)
538 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
539 microseconds=5)
540 days = timedelta(delta.days)
541 self.assertEqual(days, timedelta(1))
542
543 dt2 = dt + delta
544 self.assertEqual(dt2, dt + days)
545
546 dt2 = delta + dt
547 self.assertEqual(dt2, dt + days)
548
549 dt2 = dt - delta
550 self.assertEqual(dt2, dt - days)
551
552 delta = -delta
553 days = timedelta(delta.days)
554 self.assertEqual(days, timedelta(-2))
555
556 dt2 = dt + delta
557 self.assertEqual(dt2, dt + days)
558
559 dt2 = delta + dt
560 self.assertEqual(dt2, dt + days)
561
562 dt2 = dt - delta
563 self.assertEqual(dt2, dt - days)
564
Tim Peters604c0132004-06-07 23:04:33 +0000565class SubclassDate(date):
566 sub_var = 1
567
Guido van Rossumd8faa362007-04-27 19:54:29 +0000568class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000569 # Tests here should pass for both dates and datetimes, except for a
570 # few tests that TestDateTime overrides.
571
572 theclass = date
573
574 def test_basic_attributes(self):
575 dt = self.theclass(2002, 3, 1)
576 self.assertEqual(dt.year, 2002)
577 self.assertEqual(dt.month, 3)
578 self.assertEqual(dt.day, 1)
579
580 def test_roundtrip(self):
581 for dt in (self.theclass(1, 2, 3),
582 self.theclass.today()):
583 # Verify dt -> string -> date identity.
584 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000585 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000586 s = s[9:]
587 dt2 = eval(s)
588 self.assertEqual(dt, dt2)
589
590 # Verify identity via reconstructing from pieces.
591 dt2 = self.theclass(dt.year, dt.month, dt.day)
592 self.assertEqual(dt, dt2)
593
594 def test_ordinal_conversions(self):
595 # Check some fixed values.
596 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
597 (1, 12, 31, 365),
598 (2, 1, 1, 366),
599 # first example from "Calendrical Calculations"
600 (1945, 11, 12, 710347)]:
601 d = self.theclass(y, m, d)
602 self.assertEqual(n, d.toordinal())
603 fromord = self.theclass.fromordinal(n)
604 self.assertEqual(d, fromord)
605 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000606 # if we're checking something fancier than a date, verify
607 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000608 self.assertEqual(fromord.hour, 0)
609 self.assertEqual(fromord.minute, 0)
610 self.assertEqual(fromord.second, 0)
611 self.assertEqual(fromord.microsecond, 0)
612
Tim Peters0bf60bd2003-01-08 20:40:01 +0000613 # Check first and last days of year spottily across the whole
614 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000615 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000616 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
617 d = self.theclass(year, 1, 1)
618 n = d.toordinal()
619 d2 = self.theclass.fromordinal(n)
620 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000621 # Verify that moving back a day gets to the end of year-1.
622 if year > 1:
623 d = self.theclass.fromordinal(n-1)
624 d2 = self.theclass(year-1, 12, 31)
625 self.assertEqual(d, d2)
626 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000627
628 # Test every day in a leap-year and a non-leap year.
629 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
630 for year, isleap in (2000, True), (2002, False):
631 n = self.theclass(year, 1, 1).toordinal()
632 for month, maxday in zip(range(1, 13), dim):
633 if month == 2 and isleap:
634 maxday += 1
635 for day in range(1, maxday+1):
636 d = self.theclass(year, month, day)
637 self.assertEqual(d.toordinal(), n)
638 self.assertEqual(d, self.theclass.fromordinal(n))
639 n += 1
640
641 def test_extreme_ordinals(self):
642 a = self.theclass.min
643 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
644 aord = a.toordinal()
645 b = a.fromordinal(aord)
646 self.assertEqual(a, b)
647
648 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
649
650 b = a + timedelta(days=1)
651 self.assertEqual(b.toordinal(), aord + 1)
652 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
653
654 a = self.theclass.max
655 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
656 aord = a.toordinal()
657 b = a.fromordinal(aord)
658 self.assertEqual(a, b)
659
660 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
661
662 b = a - timedelta(days=1)
663 self.assertEqual(b.toordinal(), aord - 1)
664 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
665
666 def test_bad_constructor_arguments(self):
667 # bad years
668 self.theclass(MINYEAR, 1, 1) # no exception
669 self.theclass(MAXYEAR, 1, 1) # no exception
670 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
671 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
672 # bad months
673 self.theclass(2000, 1, 1) # no exception
674 self.theclass(2000, 12, 1) # no exception
675 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
676 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
677 # bad days
678 self.theclass(2000, 2, 29) # no exception
679 self.theclass(2004, 2, 29) # no exception
680 self.theclass(2400, 2, 29) # no exception
681 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
682 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
683 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
684 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
685 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
686 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
687
688 def test_hash_equality(self):
689 d = self.theclass(2000, 12, 31)
690 # same thing
691 e = self.theclass(2000, 12, 31)
692 self.assertEqual(d, e)
693 self.assertEqual(hash(d), hash(e))
694
695 dic = {d: 1}
696 dic[e] = 2
697 self.assertEqual(len(dic), 1)
698 self.assertEqual(dic[d], 2)
699 self.assertEqual(dic[e], 2)
700
701 d = self.theclass(2001, 1, 1)
702 # same thing
703 e = self.theclass(2001, 1, 1)
704 self.assertEqual(d, e)
705 self.assertEqual(hash(d), hash(e))
706
707 dic = {d: 1}
708 dic[e] = 2
709 self.assertEqual(len(dic), 1)
710 self.assertEqual(dic[d], 2)
711 self.assertEqual(dic[e], 2)
712
713 def test_computations(self):
714 a = self.theclass(2002, 1, 31)
715 b = self.theclass(1956, 1, 31)
716
717 diff = a-b
718 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
719 self.assertEqual(diff.seconds, 0)
720 self.assertEqual(diff.microseconds, 0)
721
722 day = timedelta(1)
723 week = timedelta(7)
724 a = self.theclass(2002, 3, 2)
725 self.assertEqual(a + day, self.theclass(2002, 3, 3))
726 self.assertEqual(day + a, self.theclass(2002, 3, 3))
727 self.assertEqual(a - day, self.theclass(2002, 3, 1))
728 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
729 self.assertEqual(a + week, self.theclass(2002, 3, 9))
730 self.assertEqual(a - week, self.theclass(2002, 2, 23))
731 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
732 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
733 self.assertEqual((a + week) - a, week)
734 self.assertEqual((a + day) - a, day)
735 self.assertEqual((a - week) - a, -week)
736 self.assertEqual((a - day) - a, -day)
737 self.assertEqual(a - (a + week), -week)
738 self.assertEqual(a - (a + day), -day)
739 self.assertEqual(a - (a - week), week)
740 self.assertEqual(a - (a - day), day)
741
Mark Dickinson5c2db372009-12-05 20:28:34 +0000742 # Add/sub ints or floats should be illegal
743 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000744 self.assertRaises(TypeError, lambda: a+i)
745 self.assertRaises(TypeError, lambda: a-i)
746 self.assertRaises(TypeError, lambda: i+a)
747 self.assertRaises(TypeError, lambda: i-a)
748
749 # delta - date is senseless.
750 self.assertRaises(TypeError, lambda: day - a)
751 # mixing date and (delta or date) via * or // is senseless
752 self.assertRaises(TypeError, lambda: day * a)
753 self.assertRaises(TypeError, lambda: a * day)
754 self.assertRaises(TypeError, lambda: day // a)
755 self.assertRaises(TypeError, lambda: a // day)
756 self.assertRaises(TypeError, lambda: a * a)
757 self.assertRaises(TypeError, lambda: a // a)
758 # date + date is senseless
759 self.assertRaises(TypeError, lambda: a + a)
760
761 def test_overflow(self):
762 tiny = self.theclass.resolution
763
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000764 for delta in [tiny, timedelta(1), timedelta(2)]:
765 dt = self.theclass.min + delta
766 dt -= delta # no problem
767 self.assertRaises(OverflowError, dt.__sub__, delta)
768 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000769
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000770 dt = self.theclass.max - delta
771 dt += delta # no problem
772 self.assertRaises(OverflowError, dt.__add__, delta)
773 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000774
775 def test_fromtimestamp(self):
776 import time
777
778 # Try an arbitrary fixed value.
779 year, month, day = 1999, 9, 19
780 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
781 d = self.theclass.fromtimestamp(ts)
782 self.assertEqual(d.year, year)
783 self.assertEqual(d.month, month)
784 self.assertEqual(d.day, day)
785
Tim Peters1b6f7a92004-06-20 02:50:16 +0000786 def test_insane_fromtimestamp(self):
787 # It's possible that some platform maps time_t to double,
788 # and that this test will fail there. This test should
789 # exempt such platforms (provided they return reasonable
790 # results!).
791 for insane in -1e200, 1e200:
792 self.assertRaises(ValueError, self.theclass.fromtimestamp,
793 insane)
794
Tim Peters2a799bf2002-12-16 20:18:38 +0000795 def test_today(self):
796 import time
797
798 # We claim that today() is like fromtimestamp(time.time()), so
799 # prove it.
800 for dummy in range(3):
801 today = self.theclass.today()
802 ts = time.time()
803 todayagain = self.theclass.fromtimestamp(ts)
804 if today == todayagain:
805 break
806 # There are several legit reasons that could fail:
807 # 1. It recently became midnight, between the today() and the
808 # time() calls.
809 # 2. The platform time() has such fine resolution that we'll
810 # never get the same value twice.
811 # 3. The platform time() has poor resolution, and we just
812 # happened to call today() right before a resolution quantum
813 # boundary.
814 # 4. The system clock got fiddled between calls.
815 # In any case, wait a little while and try again.
816 time.sleep(0.1)
817
818 # It worked or it didn't. If it didn't, assume it's reason #2, and
819 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000820 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000821 abs(todayagain - today) < timedelta(seconds=0.5))
822
823 def test_weekday(self):
824 for i in range(7):
825 # March 4, 2002 is a Monday
826 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
827 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
828 # January 2, 1956 is a Monday
829 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
830 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
831
832 def test_isocalendar(self):
833 # Check examples from
834 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
835 for i in range(7):
836 d = self.theclass(2003, 12, 22+i)
837 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
838 d = self.theclass(2003, 12, 29) + timedelta(i)
839 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
840 d = self.theclass(2004, 1, 5+i)
841 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
842 d = self.theclass(2009, 12, 21+i)
843 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
844 d = self.theclass(2009, 12, 28) + timedelta(i)
845 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
846 d = self.theclass(2010, 1, 4+i)
847 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
848
849 def test_iso_long_years(self):
850 # Calculate long ISO years and compare to table from
851 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
852 ISO_LONG_YEARS_TABLE = """
853 4 32 60 88
854 9 37 65 93
855 15 43 71 99
856 20 48 76
857 26 54 82
858
859 105 133 161 189
860 111 139 167 195
861 116 144 172
862 122 150 178
863 128 156 184
864
865 201 229 257 285
866 207 235 263 291
867 212 240 268 296
868 218 246 274
869 224 252 280
870
871 303 331 359 387
872 308 336 364 392
873 314 342 370 398
874 320 348 376
875 325 353 381
876 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000877 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000878 L = []
879 for i in range(400):
880 d = self.theclass(2000+i, 12, 31)
881 d1 = self.theclass(1600+i, 12, 31)
882 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
883 if d.isocalendar()[1] == 53:
884 L.append(i)
885 self.assertEqual(L, iso_long_years)
886
887 def test_isoformat(self):
888 t = self.theclass(2, 3, 2)
889 self.assertEqual(t.isoformat(), "0002-03-02")
890
891 def test_ctime(self):
892 t = self.theclass(2002, 3, 2)
893 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
894
895 def test_strftime(self):
896 t = self.theclass(2005, 3, 2)
897 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000898 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000899 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000900
901 self.assertRaises(TypeError, t.strftime) # needs an arg
902 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
903 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
904
Georg Brandlf78e02b2008-06-10 17:40:04 +0000905 # test that unicode input is allowed (issue 2782)
906 self.assertEqual(t.strftime("%m"), "03")
907
Tim Peters2a799bf2002-12-16 20:18:38 +0000908 # A naive object replaces %z and %Z w/ empty strings.
909 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
910
Benjamin Petersone1cdfd72009-01-18 21:02:37 +0000911 #make sure that invalid format specifiers are handled correctly
912 #self.assertRaises(ValueError, t.strftime, "%e")
913 #self.assertRaises(ValueError, t.strftime, "%")
914 #self.assertRaises(ValueError, t.strftime, "%#")
915
916 #oh well, some systems just ignore those invalid ones.
917 #at least, excercise them to make sure that no crashes
918 #are generated
919 for f in ["%e", "%", "%#"]:
920 try:
921 t.strftime(f)
922 except ValueError:
923 pass
924
925 #check that this standard extension works
926 t.strftime("%f")
927
Georg Brandlf78e02b2008-06-10 17:40:04 +0000928
Eric Smith1ba31142007-09-11 18:06:02 +0000929 def test_format(self):
930 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000931 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000932
933 # check that a derived class's __str__() gets called
934 class A(self.theclass):
935 def __str__(self):
936 return 'A'
937 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000938 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000939
940 # check that a derived class's strftime gets called
941 class B(self.theclass):
942 def strftime(self, format_spec):
943 return 'B'
944 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000945 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000946
947 for fmt in ["m:%m d:%d y:%y",
948 "m:%m d:%d y:%y H:%H M:%M S:%S",
949 "%z %Z",
950 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +0000951 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
952 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
953 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +0000954
Tim Peters2a799bf2002-12-16 20:18:38 +0000955 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000956 self.assertIsInstance(self.theclass.min, self.theclass)
957 self.assertIsInstance(self.theclass.max, self.theclass)
958 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000959 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000960
961 def test_extreme_timedelta(self):
962 big = self.theclass.max - self.theclass.min
963 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
964 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
965 # n == 315537897599999999 ~= 2**58.13
966 justasbig = timedelta(0, 0, n)
967 self.assertEqual(big, justasbig)
968 self.assertEqual(self.theclass.min + big, self.theclass.max)
969 self.assertEqual(self.theclass.max - big, self.theclass.min)
970
971 def test_timetuple(self):
972 for i in range(7):
973 # January 2, 1956 is a Monday (0)
974 d = self.theclass(1956, 1, 2+i)
975 t = d.timetuple()
976 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
977 # February 1, 1956 is a Wednesday (2)
978 d = self.theclass(1956, 2, 1+i)
979 t = d.timetuple()
980 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
981 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
982 # of the year.
983 d = self.theclass(1956, 3, 1+i)
984 t = d.timetuple()
985 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
986 self.assertEqual(t.tm_year, 1956)
987 self.assertEqual(t.tm_mon, 3)
988 self.assertEqual(t.tm_mday, 1+i)
989 self.assertEqual(t.tm_hour, 0)
990 self.assertEqual(t.tm_min, 0)
991 self.assertEqual(t.tm_sec, 0)
992 self.assertEqual(t.tm_wday, (3+i)%7)
993 self.assertEqual(t.tm_yday, 61+i)
994 self.assertEqual(t.tm_isdst, -1)
995
996 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000997 args = 6, 7, 23
998 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000999 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001000 green = pickler.dumps(orig, proto)
1001 derived = unpickler.loads(green)
1002 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001003
1004 def test_compare(self):
1005 t1 = self.theclass(2, 3, 4)
1006 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001007 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001008 self.assertTrue(t1 <= t2)
1009 self.assertTrue(t1 >= t2)
1010 self.assertTrue(not t1 != t2)
1011 self.assertTrue(not t1 < t2)
1012 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001013
1014 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1015 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001016 self.assertTrue(t1 < t2)
1017 self.assertTrue(t2 > t1)
1018 self.assertTrue(t1 <= t2)
1019 self.assertTrue(t2 >= t1)
1020 self.assertTrue(t1 != t2)
1021 self.assertTrue(t2 != t1)
1022 self.assertTrue(not t1 == t2)
1023 self.assertTrue(not t2 == t1)
1024 self.assertTrue(not t1 > t2)
1025 self.assertTrue(not t2 < t1)
1026 self.assertTrue(not t1 >= t2)
1027 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001028
Tim Peters68124bb2003-02-08 03:46:31 +00001029 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001030 self.assertEqual(t1 == badarg, False)
1031 self.assertEqual(t1 != badarg, True)
1032 self.assertEqual(badarg == t1, False)
1033 self.assertEqual(badarg != t1, True)
1034
Tim Peters2a799bf2002-12-16 20:18:38 +00001035 self.assertRaises(TypeError, lambda: t1 < badarg)
1036 self.assertRaises(TypeError, lambda: t1 > badarg)
1037 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001038 self.assertRaises(TypeError, lambda: badarg <= t1)
1039 self.assertRaises(TypeError, lambda: badarg < t1)
1040 self.assertRaises(TypeError, lambda: badarg > t1)
1041 self.assertRaises(TypeError, lambda: badarg >= t1)
1042
Tim Peters8d81a012003-01-24 22:36:34 +00001043 def test_mixed_compare(self):
1044 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001045
1046 # Our class can be compared for equality to other classes
1047 self.assertEqual(our == 1, False)
1048 self.assertEqual(1 == our, False)
1049 self.assertEqual(our != 1, True)
1050 self.assertEqual(1 != our, True)
1051
1052 # But the ordering is undefined
1053 self.assertRaises(TypeError, lambda: our < 1)
1054 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001055
Guido van Rossum19960592006-08-24 17:29:38 +00001056 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001057
Guido van Rossum19960592006-08-24 17:29:38 +00001058 class SomeClass:
1059 pass
1060
1061 their = SomeClass()
1062 self.assertEqual(our == their, False)
1063 self.assertEqual(their == our, False)
1064 self.assertEqual(our != their, True)
1065 self.assertEqual(their != our, True)
1066 self.assertRaises(TypeError, lambda: our < their)
1067 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001068
Guido van Rossum19960592006-08-24 17:29:38 +00001069 # However, if the other class explicitly defines ordering
1070 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001071
Guido van Rossum19960592006-08-24 17:29:38 +00001072 class LargerThanAnything:
1073 def __lt__(self, other):
1074 return False
1075 def __le__(self, other):
1076 return isinstance(other, LargerThanAnything)
1077 def __eq__(self, other):
1078 return isinstance(other, LargerThanAnything)
1079 def __ne__(self, other):
1080 return not isinstance(other, LargerThanAnything)
1081 def __gt__(self, other):
1082 return not isinstance(other, LargerThanAnything)
1083 def __ge__(self, other):
1084 return True
1085
1086 their = LargerThanAnything()
1087 self.assertEqual(our == their, False)
1088 self.assertEqual(their == our, False)
1089 self.assertEqual(our != their, True)
1090 self.assertEqual(their != our, True)
1091 self.assertEqual(our < their, True)
1092 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001093
Tim Peters2a799bf2002-12-16 20:18:38 +00001094 def test_bool(self):
1095 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001096 self.assertTrue(self.theclass.min)
1097 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001098
Guido van Rossum04110fb2007-08-24 16:32:05 +00001099 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001100 # For nasty technical reasons, we can't handle years before 1900.
1101 cls = self.theclass
1102 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1103 for y in 1, 49, 51, 99, 100, 1000, 1899:
1104 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001105
1106 def test_replace(self):
1107 cls = self.theclass
1108 args = [1, 2, 3]
1109 base = cls(*args)
1110 self.assertEqual(base, base.replace())
1111
1112 i = 0
1113 for name, newval in (("year", 2),
1114 ("month", 3),
1115 ("day", 4)):
1116 newargs = args[:]
1117 newargs[i] = newval
1118 expected = cls(*newargs)
1119 got = base.replace(**{name: newval})
1120 self.assertEqual(expected, got)
1121 i += 1
1122
1123 # Out of bounds.
1124 base = cls(2000, 2, 29)
1125 self.assertRaises(ValueError, base.replace, year=2001)
1126
Tim Petersa98924a2003-05-17 05:55:19 +00001127 def test_subclass_date(self):
1128
1129 class C(self.theclass):
1130 theAnswer = 42
1131
1132 def __new__(cls, *args, **kws):
1133 temp = kws.copy()
1134 extra = temp.pop('extra')
1135 result = self.theclass.__new__(cls, *args, **temp)
1136 result.extra = extra
1137 return result
1138
1139 def newmeth(self, start):
1140 return start + self.year + self.month
1141
1142 args = 2003, 4, 14
1143
1144 dt1 = self.theclass(*args)
1145 dt2 = C(*args, **{'extra': 7})
1146
1147 self.assertEqual(dt2.__class__, C)
1148 self.assertEqual(dt2.theAnswer, 42)
1149 self.assertEqual(dt2.extra, 7)
1150 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1151 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1152
Tim Peters604c0132004-06-07 23:04:33 +00001153 def test_pickling_subclass_date(self):
1154
1155 args = 6, 7, 23
1156 orig = SubclassDate(*args)
1157 for pickler, unpickler, proto in pickle_choices:
1158 green = pickler.dumps(orig, proto)
1159 derived = unpickler.loads(green)
1160 self.assertEqual(orig, derived)
1161
Tim Peters3f606292004-03-21 23:38:41 +00001162 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001163 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001164 # This is a low-overhead backdoor. A user can (by intent or
1165 # mistake) pass a string directly, which (if it's the right length)
1166 # will get treated like a pickle, and bypass the normal sanity
1167 # checks in the constructor. This can create insane objects.
1168 # The constructor doesn't want to burn the time to validate all
1169 # fields, but does check the month field. This stops, e.g.,
1170 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001171 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001172 if not issubclass(self.theclass, datetime):
1173 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001174 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001175 self.assertRaises(TypeError, self.theclass,
1176 base[:2] + month_byte + base[3:])
1177 for ord_byte in range(1, 13):
1178 # This shouldn't blow up because of the month byte alone. If
1179 # the implementation changes to do more-careful checking, it may
1180 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001181 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001182
Tim Peters2a799bf2002-12-16 20:18:38 +00001183#############################################################################
1184# datetime tests
1185
Tim Peters604c0132004-06-07 23:04:33 +00001186class SubclassDatetime(datetime):
1187 sub_var = 1
1188
Tim Peters2a799bf2002-12-16 20:18:38 +00001189class TestDateTime(TestDate):
1190
1191 theclass = datetime
1192
1193 def test_basic_attributes(self):
1194 dt = self.theclass(2002, 3, 1, 12, 0)
1195 self.assertEqual(dt.year, 2002)
1196 self.assertEqual(dt.month, 3)
1197 self.assertEqual(dt.day, 1)
1198 self.assertEqual(dt.hour, 12)
1199 self.assertEqual(dt.minute, 0)
1200 self.assertEqual(dt.second, 0)
1201 self.assertEqual(dt.microsecond, 0)
1202
1203 def test_basic_attributes_nonzero(self):
1204 # Make sure all attributes are non-zero so bugs in
1205 # bit-shifting access show up.
1206 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1207 self.assertEqual(dt.year, 2002)
1208 self.assertEqual(dt.month, 3)
1209 self.assertEqual(dt.day, 1)
1210 self.assertEqual(dt.hour, 12)
1211 self.assertEqual(dt.minute, 59)
1212 self.assertEqual(dt.second, 59)
1213 self.assertEqual(dt.microsecond, 8000)
1214
1215 def test_roundtrip(self):
1216 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1217 self.theclass.now()):
1218 # Verify dt -> string -> datetime identity.
1219 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001220 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001221 s = s[9:]
1222 dt2 = eval(s)
1223 self.assertEqual(dt, dt2)
1224
1225 # Verify identity via reconstructing from pieces.
1226 dt2 = self.theclass(dt.year, dt.month, dt.day,
1227 dt.hour, dt.minute, dt.second,
1228 dt.microsecond)
1229 self.assertEqual(dt, dt2)
1230
1231 def test_isoformat(self):
1232 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1233 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1234 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1235 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001236 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001237 # str is ISO format with the separator forced to a blank.
1238 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1239
1240 t = self.theclass(2, 3, 2)
1241 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1242 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1243 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1244 # str is ISO format with the separator forced to a blank.
1245 self.assertEqual(str(t), "0002-03-02 00:00:00")
1246
Eric Smith1ba31142007-09-11 18:06:02 +00001247 def test_format(self):
1248 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001249 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001250
1251 # check that a derived class's __str__() gets called
1252 class A(self.theclass):
1253 def __str__(self):
1254 return 'A'
1255 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001256 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001257
1258 # check that a derived class's strftime gets called
1259 class B(self.theclass):
1260 def strftime(self, format_spec):
1261 return 'B'
1262 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001263 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001264
1265 for fmt in ["m:%m d:%d y:%y",
1266 "m:%m d:%d y:%y H:%H M:%M S:%S",
1267 "%z %Z",
1268 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001269 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1270 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1271 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001272
Tim Peters2a799bf2002-12-16 20:18:38 +00001273 def test_more_ctime(self):
1274 # Test fields that TestDate doesn't touch.
1275 import time
1276
1277 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1278 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1279 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1280 # out. The difference is that t.ctime() produces " 2" for the day,
1281 # but platform ctime() produces "02" for the day. According to
1282 # C99, t.ctime() is correct here.
1283 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1284
1285 # So test a case where that difference doesn't matter.
1286 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1287 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1288
1289 def test_tz_independent_comparing(self):
1290 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1291 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1292 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1293 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001294 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001295
1296 # Make sure comparison doesn't forget microseconds, and isn't done
1297 # via comparing a float timestamp (an IEEE double doesn't have enough
1298 # precision to span microsecond resolution across years 1 thru 9999,
1299 # so comparing via timestamp necessarily calls some distinct values
1300 # equal).
1301 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1302 us = timedelta(microseconds=1)
1303 dt2 = dt1 + us
1304 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001305 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001306
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001307 def test_strftime_with_bad_tzname_replace(self):
1308 # verify ok if tzinfo.tzname().replace() returns a non-string
1309 class MyTzInfo(FixedOffset):
1310 def tzname(self, dt):
1311 class MyStr(str):
1312 def replace(self, *args):
1313 return None
1314 return MyStr('name')
1315 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1316 self.assertRaises(TypeError, t.strftime, '%Z')
1317
Tim Peters2a799bf2002-12-16 20:18:38 +00001318 def test_bad_constructor_arguments(self):
1319 # bad years
1320 self.theclass(MINYEAR, 1, 1) # no exception
1321 self.theclass(MAXYEAR, 1, 1) # no exception
1322 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1323 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1324 # bad months
1325 self.theclass(2000, 1, 1) # no exception
1326 self.theclass(2000, 12, 1) # no exception
1327 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1328 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1329 # bad days
1330 self.theclass(2000, 2, 29) # no exception
1331 self.theclass(2004, 2, 29) # no exception
1332 self.theclass(2400, 2, 29) # no exception
1333 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1334 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1335 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1336 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1337 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1338 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1339 # bad hours
1340 self.theclass(2000, 1, 31, 0) # no exception
1341 self.theclass(2000, 1, 31, 23) # no exception
1342 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1343 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1344 # bad minutes
1345 self.theclass(2000, 1, 31, 23, 0) # no exception
1346 self.theclass(2000, 1, 31, 23, 59) # no exception
1347 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1348 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1349 # bad seconds
1350 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1351 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1352 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1353 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1354 # bad microseconds
1355 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1356 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1357 self.assertRaises(ValueError, self.theclass,
1358 2000, 1, 31, 23, 59, 59, -1)
1359 self.assertRaises(ValueError, self.theclass,
1360 2000, 1, 31, 23, 59, 59,
1361 1000000)
1362
1363 def test_hash_equality(self):
1364 d = self.theclass(2000, 12, 31, 23, 30, 17)
1365 e = self.theclass(2000, 12, 31, 23, 30, 17)
1366 self.assertEqual(d, e)
1367 self.assertEqual(hash(d), hash(e))
1368
1369 dic = {d: 1}
1370 dic[e] = 2
1371 self.assertEqual(len(dic), 1)
1372 self.assertEqual(dic[d], 2)
1373 self.assertEqual(dic[e], 2)
1374
1375 d = self.theclass(2001, 1, 1, 0, 5, 17)
1376 e = self.theclass(2001, 1, 1, 0, 5, 17)
1377 self.assertEqual(d, e)
1378 self.assertEqual(hash(d), hash(e))
1379
1380 dic = {d: 1}
1381 dic[e] = 2
1382 self.assertEqual(len(dic), 1)
1383 self.assertEqual(dic[d], 2)
1384 self.assertEqual(dic[e], 2)
1385
1386 def test_computations(self):
1387 a = self.theclass(2002, 1, 31)
1388 b = self.theclass(1956, 1, 31)
1389 diff = a-b
1390 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1391 self.assertEqual(diff.seconds, 0)
1392 self.assertEqual(diff.microseconds, 0)
1393 a = self.theclass(2002, 3, 2, 17, 6)
1394 millisec = timedelta(0, 0, 1000)
1395 hour = timedelta(0, 3600)
1396 day = timedelta(1)
1397 week = timedelta(7)
1398 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1399 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1400 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1401 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1402 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1403 self.assertEqual(a - hour, a + -hour)
1404 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1405 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1406 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1407 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1408 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1409 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1410 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1411 self.assertEqual((a + week) - a, week)
1412 self.assertEqual((a + day) - a, day)
1413 self.assertEqual((a + hour) - a, hour)
1414 self.assertEqual((a + millisec) - a, millisec)
1415 self.assertEqual((a - week) - a, -week)
1416 self.assertEqual((a - day) - a, -day)
1417 self.assertEqual((a - hour) - a, -hour)
1418 self.assertEqual((a - millisec) - a, -millisec)
1419 self.assertEqual(a - (a + week), -week)
1420 self.assertEqual(a - (a + day), -day)
1421 self.assertEqual(a - (a + hour), -hour)
1422 self.assertEqual(a - (a + millisec), -millisec)
1423 self.assertEqual(a - (a - week), week)
1424 self.assertEqual(a - (a - day), day)
1425 self.assertEqual(a - (a - hour), hour)
1426 self.assertEqual(a - (a - millisec), millisec)
1427 self.assertEqual(a + (week + day + hour + millisec),
1428 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1429 self.assertEqual(a + (week + day + hour + millisec),
1430 (((a + week) + day) + hour) + millisec)
1431 self.assertEqual(a - (week + day + hour + millisec),
1432 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1433 self.assertEqual(a - (week + day + hour + millisec),
1434 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001435 # Add/sub ints or floats should be illegal
1436 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001437 self.assertRaises(TypeError, lambda: a+i)
1438 self.assertRaises(TypeError, lambda: a-i)
1439 self.assertRaises(TypeError, lambda: i+a)
1440 self.assertRaises(TypeError, lambda: i-a)
1441
1442 # delta - datetime is senseless.
1443 self.assertRaises(TypeError, lambda: day - a)
1444 # mixing datetime and (delta or datetime) via * or // is senseless
1445 self.assertRaises(TypeError, lambda: day * a)
1446 self.assertRaises(TypeError, lambda: a * day)
1447 self.assertRaises(TypeError, lambda: day // a)
1448 self.assertRaises(TypeError, lambda: a // day)
1449 self.assertRaises(TypeError, lambda: a * a)
1450 self.assertRaises(TypeError, lambda: a // a)
1451 # datetime + datetime is senseless
1452 self.assertRaises(TypeError, lambda: a + a)
1453
1454 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001455 args = 6, 7, 23, 20, 59, 1, 64**2
1456 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001457 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001458 green = pickler.dumps(orig, proto)
1459 derived = unpickler.loads(green)
1460 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001461
Guido van Rossum275666f2003-02-07 21:49:01 +00001462 def test_more_pickling(self):
1463 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1464 s = pickle.dumps(a)
1465 b = pickle.loads(s)
1466 self.assertEqual(b.year, 2003)
1467 self.assertEqual(b.month, 2)
1468 self.assertEqual(b.day, 7)
1469
Tim Peters604c0132004-06-07 23:04:33 +00001470 def test_pickling_subclass_datetime(self):
1471 args = 6, 7, 23, 20, 59, 1, 64**2
1472 orig = SubclassDatetime(*args)
1473 for pickler, unpickler, proto in pickle_choices:
1474 green = pickler.dumps(orig, proto)
1475 derived = unpickler.loads(green)
1476 self.assertEqual(orig, derived)
1477
Tim Peters2a799bf2002-12-16 20:18:38 +00001478 def test_more_compare(self):
1479 # The test_compare() inherited from TestDate covers the error cases.
1480 # We just want to test lexicographic ordering on the members datetime
1481 # has that date lacks.
1482 args = [2000, 11, 29, 20, 58, 16, 999998]
1483 t1 = self.theclass(*args)
1484 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001485 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001486 self.assertTrue(t1 <= t2)
1487 self.assertTrue(t1 >= t2)
1488 self.assertTrue(not t1 != t2)
1489 self.assertTrue(not t1 < t2)
1490 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001491
1492 for i in range(len(args)):
1493 newargs = args[:]
1494 newargs[i] = args[i] + 1
1495 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001496 self.assertTrue(t1 < t2)
1497 self.assertTrue(t2 > t1)
1498 self.assertTrue(t1 <= t2)
1499 self.assertTrue(t2 >= t1)
1500 self.assertTrue(t1 != t2)
1501 self.assertTrue(t2 != t1)
1502 self.assertTrue(not t1 == t2)
1503 self.assertTrue(not t2 == t1)
1504 self.assertTrue(not t1 > t2)
1505 self.assertTrue(not t2 < t1)
1506 self.assertTrue(not t1 >= t2)
1507 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001508
1509
1510 # A helper for timestamp constructor tests.
1511 def verify_field_equality(self, expected, got):
1512 self.assertEqual(expected.tm_year, got.year)
1513 self.assertEqual(expected.tm_mon, got.month)
1514 self.assertEqual(expected.tm_mday, got.day)
1515 self.assertEqual(expected.tm_hour, got.hour)
1516 self.assertEqual(expected.tm_min, got.minute)
1517 self.assertEqual(expected.tm_sec, got.second)
1518
1519 def test_fromtimestamp(self):
1520 import time
1521
1522 ts = time.time()
1523 expected = time.localtime(ts)
1524 got = self.theclass.fromtimestamp(ts)
1525 self.verify_field_equality(expected, got)
1526
1527 def test_utcfromtimestamp(self):
1528 import time
1529
1530 ts = time.time()
1531 expected = time.gmtime(ts)
1532 got = self.theclass.utcfromtimestamp(ts)
1533 self.verify_field_equality(expected, got)
1534
Thomas Wouters477c8d52006-05-27 19:21:47 +00001535 def test_microsecond_rounding(self):
1536 # Test whether fromtimestamp "rounds up" floats that are less
1537 # than one microsecond smaller than an integer.
1538 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1539 self.theclass.fromtimestamp(1))
1540
Tim Peters1b6f7a92004-06-20 02:50:16 +00001541 def test_insane_fromtimestamp(self):
1542 # It's possible that some platform maps time_t to double,
1543 # and that this test will fail there. This test should
1544 # exempt such platforms (provided they return reasonable
1545 # results!).
1546 for insane in -1e200, 1e200:
1547 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1548 insane)
1549
1550 def test_insane_utcfromtimestamp(self):
1551 # It's possible that some platform maps time_t to double,
1552 # and that this test will fail there. This test should
1553 # exempt such platforms (provided they return reasonable
1554 # results!).
1555 for insane in -1e200, 1e200:
1556 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1557 insane)
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001558 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001559 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001560 # The result is tz-dependent; at least test that this doesn't
1561 # fail (like it did before bug 1646728 was fixed).
1562 self.theclass.fromtimestamp(-1.05)
1563
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001564 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001565 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001566 d = self.theclass.utcfromtimestamp(-1.05)
1567 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1568
Tim Peters2a799bf2002-12-16 20:18:38 +00001569 def test_utcnow(self):
1570 import time
1571
1572 # Call it a success if utcnow() and utcfromtimestamp() are within
1573 # a second of each other.
1574 tolerance = timedelta(seconds=1)
1575 for dummy in range(3):
1576 from_now = self.theclass.utcnow()
1577 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1578 if abs(from_timestamp - from_now) <= tolerance:
1579 break
1580 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001581 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001582
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001583 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001584 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001585
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001586 string = '2004-12-01 13:02:47.197'
1587 format = '%Y-%m-%d %H:%M:%S.%f'
1588 result, frac = _strptime._strptime(string, format)
1589 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001590 got = self.theclass.strptime(string, format)
1591 self.assertEqual(expected, got)
1592
Tim Peters2a799bf2002-12-16 20:18:38 +00001593 def test_more_timetuple(self):
1594 # This tests fields beyond those tested by the TestDate.test_timetuple.
1595 t = self.theclass(2004, 12, 31, 6, 22, 33)
1596 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1597 self.assertEqual(t.timetuple(),
1598 (t.year, t.month, t.day,
1599 t.hour, t.minute, t.second,
1600 t.weekday(),
1601 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1602 -1))
1603 tt = t.timetuple()
1604 self.assertEqual(tt.tm_year, t.year)
1605 self.assertEqual(tt.tm_mon, t.month)
1606 self.assertEqual(tt.tm_mday, t.day)
1607 self.assertEqual(tt.tm_hour, t.hour)
1608 self.assertEqual(tt.tm_min, t.minute)
1609 self.assertEqual(tt.tm_sec, t.second)
1610 self.assertEqual(tt.tm_wday, t.weekday())
1611 self.assertEqual(tt.tm_yday, t.toordinal() -
1612 date(t.year, 1, 1).toordinal() + 1)
1613 self.assertEqual(tt.tm_isdst, -1)
1614
1615 def test_more_strftime(self):
1616 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001617 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1618 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1619 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001620
1621 def test_extract(self):
1622 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1623 self.assertEqual(dt.date(), date(2002, 3, 4))
1624 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1625
1626 def test_combine(self):
1627 d = date(2002, 3, 4)
1628 t = time(18, 45, 3, 1234)
1629 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1630 combine = self.theclass.combine
1631 dt = combine(d, t)
1632 self.assertEqual(dt, expected)
1633
1634 dt = combine(time=t, date=d)
1635 self.assertEqual(dt, expected)
1636
1637 self.assertEqual(d, dt.date())
1638 self.assertEqual(t, dt.time())
1639 self.assertEqual(dt, combine(dt.date(), dt.time()))
1640
1641 self.assertRaises(TypeError, combine) # need an arg
1642 self.assertRaises(TypeError, combine, d) # need two args
1643 self.assertRaises(TypeError, combine, t, d) # args reversed
1644 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1645 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1646
Tim Peters12bf3392002-12-24 05:41:27 +00001647 def test_replace(self):
1648 cls = self.theclass
1649 args = [1, 2, 3, 4, 5, 6, 7]
1650 base = cls(*args)
1651 self.assertEqual(base, base.replace())
1652
1653 i = 0
1654 for name, newval in (("year", 2),
1655 ("month", 3),
1656 ("day", 4),
1657 ("hour", 5),
1658 ("minute", 6),
1659 ("second", 7),
1660 ("microsecond", 8)):
1661 newargs = args[:]
1662 newargs[i] = newval
1663 expected = cls(*newargs)
1664 got = base.replace(**{name: newval})
1665 self.assertEqual(expected, got)
1666 i += 1
1667
1668 # Out of bounds.
1669 base = cls(2000, 2, 29)
1670 self.assertRaises(ValueError, base.replace, year=2001)
1671
Tim Peters80475bb2002-12-25 07:40:55 +00001672 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001673 # Pretty boring! The TZ test is more interesting here. astimezone()
1674 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001675 dt = self.theclass.now()
1676 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001677 self.assertRaises(TypeError, dt.astimezone) # not enough args
1678 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1679 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001680 self.assertRaises(ValueError, dt.astimezone, f) # naive
1681 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001682
Tim Peters52dcce22003-01-23 16:36:11 +00001683 class Bogus(tzinfo):
1684 def utcoffset(self, dt): return None
1685 def dst(self, dt): return timedelta(0)
1686 bog = Bogus()
1687 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1688
1689 class AlsoBogus(tzinfo):
1690 def utcoffset(self, dt): return timedelta(0)
1691 def dst(self, dt): return None
1692 alsobog = AlsoBogus()
1693 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001694
Tim Petersa98924a2003-05-17 05:55:19 +00001695 def test_subclass_datetime(self):
1696
1697 class C(self.theclass):
1698 theAnswer = 42
1699
1700 def __new__(cls, *args, **kws):
1701 temp = kws.copy()
1702 extra = temp.pop('extra')
1703 result = self.theclass.__new__(cls, *args, **temp)
1704 result.extra = extra
1705 return result
1706
1707 def newmeth(self, start):
1708 return start + self.year + self.month + self.second
1709
1710 args = 2003, 4, 14, 12, 13, 41
1711
1712 dt1 = self.theclass(*args)
1713 dt2 = C(*args, **{'extra': 7})
1714
1715 self.assertEqual(dt2.__class__, C)
1716 self.assertEqual(dt2.theAnswer, 42)
1717 self.assertEqual(dt2.extra, 7)
1718 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1719 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1720 dt1.second - 7)
1721
Tim Peters604c0132004-06-07 23:04:33 +00001722class SubclassTime(time):
1723 sub_var = 1
1724
Guido van Rossumd8faa362007-04-27 19:54:29 +00001725class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001726
1727 theclass = time
1728
1729 def test_basic_attributes(self):
1730 t = self.theclass(12, 0)
1731 self.assertEqual(t.hour, 12)
1732 self.assertEqual(t.minute, 0)
1733 self.assertEqual(t.second, 0)
1734 self.assertEqual(t.microsecond, 0)
1735
1736 def test_basic_attributes_nonzero(self):
1737 # Make sure all attributes are non-zero so bugs in
1738 # bit-shifting access show up.
1739 t = self.theclass(12, 59, 59, 8000)
1740 self.assertEqual(t.hour, 12)
1741 self.assertEqual(t.minute, 59)
1742 self.assertEqual(t.second, 59)
1743 self.assertEqual(t.microsecond, 8000)
1744
1745 def test_roundtrip(self):
1746 t = self.theclass(1, 2, 3, 4)
1747
1748 # Verify t -> string -> time identity.
1749 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001750 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001751 s = s[9:]
1752 t2 = eval(s)
1753 self.assertEqual(t, t2)
1754
1755 # Verify identity via reconstructing from pieces.
1756 t2 = self.theclass(t.hour, t.minute, t.second,
1757 t.microsecond)
1758 self.assertEqual(t, t2)
1759
1760 def test_comparing(self):
1761 args = [1, 2, 3, 4]
1762 t1 = self.theclass(*args)
1763 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001764 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001765 self.assertTrue(t1 <= t2)
1766 self.assertTrue(t1 >= t2)
1767 self.assertTrue(not t1 != t2)
1768 self.assertTrue(not t1 < t2)
1769 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001770
1771 for i in range(len(args)):
1772 newargs = args[:]
1773 newargs[i] = args[i] + 1
1774 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001775 self.assertTrue(t1 < t2)
1776 self.assertTrue(t2 > t1)
1777 self.assertTrue(t1 <= t2)
1778 self.assertTrue(t2 >= t1)
1779 self.assertTrue(t1 != t2)
1780 self.assertTrue(t2 != t1)
1781 self.assertTrue(not t1 == t2)
1782 self.assertTrue(not t2 == t1)
1783 self.assertTrue(not t1 > t2)
1784 self.assertTrue(not t2 < t1)
1785 self.assertTrue(not t1 >= t2)
1786 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001787
Tim Peters68124bb2003-02-08 03:46:31 +00001788 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001789 self.assertEqual(t1 == badarg, False)
1790 self.assertEqual(t1 != badarg, True)
1791 self.assertEqual(badarg == t1, False)
1792 self.assertEqual(badarg != t1, True)
1793
Tim Peters2a799bf2002-12-16 20:18:38 +00001794 self.assertRaises(TypeError, lambda: t1 <= badarg)
1795 self.assertRaises(TypeError, lambda: t1 < badarg)
1796 self.assertRaises(TypeError, lambda: t1 > badarg)
1797 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001798 self.assertRaises(TypeError, lambda: badarg <= t1)
1799 self.assertRaises(TypeError, lambda: badarg < t1)
1800 self.assertRaises(TypeError, lambda: badarg > t1)
1801 self.assertRaises(TypeError, lambda: badarg >= t1)
1802
1803 def test_bad_constructor_arguments(self):
1804 # bad hours
1805 self.theclass(0, 0) # no exception
1806 self.theclass(23, 0) # no exception
1807 self.assertRaises(ValueError, self.theclass, -1, 0)
1808 self.assertRaises(ValueError, self.theclass, 24, 0)
1809 # bad minutes
1810 self.theclass(23, 0) # no exception
1811 self.theclass(23, 59) # no exception
1812 self.assertRaises(ValueError, self.theclass, 23, -1)
1813 self.assertRaises(ValueError, self.theclass, 23, 60)
1814 # bad seconds
1815 self.theclass(23, 59, 0) # no exception
1816 self.theclass(23, 59, 59) # no exception
1817 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1818 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1819 # bad microseconds
1820 self.theclass(23, 59, 59, 0) # no exception
1821 self.theclass(23, 59, 59, 999999) # no exception
1822 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1823 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1824
1825 def test_hash_equality(self):
1826 d = self.theclass(23, 30, 17)
1827 e = self.theclass(23, 30, 17)
1828 self.assertEqual(d, e)
1829 self.assertEqual(hash(d), hash(e))
1830
1831 dic = {d: 1}
1832 dic[e] = 2
1833 self.assertEqual(len(dic), 1)
1834 self.assertEqual(dic[d], 2)
1835 self.assertEqual(dic[e], 2)
1836
1837 d = self.theclass(0, 5, 17)
1838 e = self.theclass(0, 5, 17)
1839 self.assertEqual(d, e)
1840 self.assertEqual(hash(d), hash(e))
1841
1842 dic = {d: 1}
1843 dic[e] = 2
1844 self.assertEqual(len(dic), 1)
1845 self.assertEqual(dic[d], 2)
1846 self.assertEqual(dic[e], 2)
1847
1848 def test_isoformat(self):
1849 t = self.theclass(4, 5, 1, 123)
1850 self.assertEqual(t.isoformat(), "04:05:01.000123")
1851 self.assertEqual(t.isoformat(), str(t))
1852
1853 t = self.theclass()
1854 self.assertEqual(t.isoformat(), "00:00:00")
1855 self.assertEqual(t.isoformat(), str(t))
1856
1857 t = self.theclass(microsecond=1)
1858 self.assertEqual(t.isoformat(), "00:00:00.000001")
1859 self.assertEqual(t.isoformat(), str(t))
1860
1861 t = self.theclass(microsecond=10)
1862 self.assertEqual(t.isoformat(), "00:00:00.000010")
1863 self.assertEqual(t.isoformat(), str(t))
1864
1865 t = self.theclass(microsecond=100)
1866 self.assertEqual(t.isoformat(), "00:00:00.000100")
1867 self.assertEqual(t.isoformat(), str(t))
1868
1869 t = self.theclass(microsecond=1000)
1870 self.assertEqual(t.isoformat(), "00:00:00.001000")
1871 self.assertEqual(t.isoformat(), str(t))
1872
1873 t = self.theclass(microsecond=10000)
1874 self.assertEqual(t.isoformat(), "00:00:00.010000")
1875 self.assertEqual(t.isoformat(), str(t))
1876
1877 t = self.theclass(microsecond=100000)
1878 self.assertEqual(t.isoformat(), "00:00:00.100000")
1879 self.assertEqual(t.isoformat(), str(t))
1880
Thomas Wouterscf297e42007-02-23 15:07:44 +00001881 def test_1653736(self):
1882 # verify it doesn't accept extra keyword arguments
1883 t = self.theclass(second=1)
1884 self.assertRaises(TypeError, t.isoformat, foo=3)
1885
Tim Peters2a799bf2002-12-16 20:18:38 +00001886 def test_strftime(self):
1887 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001888 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001889 # A naive object replaces %z and %Z with empty strings.
1890 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1891
Eric Smith1ba31142007-09-11 18:06:02 +00001892 def test_format(self):
1893 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001894 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001895
1896 # check that a derived class's __str__() gets called
1897 class A(self.theclass):
1898 def __str__(self):
1899 return 'A'
1900 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001901 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001902
1903 # check that a derived class's strftime gets called
1904 class B(self.theclass):
1905 def strftime(self, format_spec):
1906 return 'B'
1907 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001908 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001909
1910 for fmt in ['%H %M %S',
1911 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001912 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1913 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1914 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001915
Tim Peters2a799bf2002-12-16 20:18:38 +00001916 def test_str(self):
1917 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1918 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1919 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1920 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1921 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1922
1923 def test_repr(self):
1924 name = 'datetime.' + self.theclass.__name__
1925 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1926 "%s(1, 2, 3, 4)" % name)
1927 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1928 "%s(10, 2, 3, 4000)" % name)
1929 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1930 "%s(0, 2, 3, 400000)" % name)
1931 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1932 "%s(12, 2, 3)" % name)
1933 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1934 "%s(23, 15)" % name)
1935
1936 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00001937 self.assertIsInstance(self.theclass.min, self.theclass)
1938 self.assertIsInstance(self.theclass.max, self.theclass)
1939 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001940 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001941
1942 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001943 args = 20, 59, 16, 64**2
1944 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001945 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001946 green = pickler.dumps(orig, proto)
1947 derived = unpickler.loads(green)
1948 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001949
Tim Peters604c0132004-06-07 23:04:33 +00001950 def test_pickling_subclass_time(self):
1951 args = 20, 59, 16, 64**2
1952 orig = SubclassTime(*args)
1953 for pickler, unpickler, proto in pickle_choices:
1954 green = pickler.dumps(orig, proto)
1955 derived = unpickler.loads(green)
1956 self.assertEqual(orig, derived)
1957
Tim Peters2a799bf2002-12-16 20:18:38 +00001958 def test_bool(self):
1959 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001960 self.assertTrue(cls(1))
1961 self.assertTrue(cls(0, 1))
1962 self.assertTrue(cls(0, 0, 1))
1963 self.assertTrue(cls(0, 0, 0, 1))
1964 self.assertTrue(not cls(0))
1965 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001966
Tim Peters12bf3392002-12-24 05:41:27 +00001967 def test_replace(self):
1968 cls = self.theclass
1969 args = [1, 2, 3, 4]
1970 base = cls(*args)
1971 self.assertEqual(base, base.replace())
1972
1973 i = 0
1974 for name, newval in (("hour", 5),
1975 ("minute", 6),
1976 ("second", 7),
1977 ("microsecond", 8)):
1978 newargs = args[:]
1979 newargs[i] = newval
1980 expected = cls(*newargs)
1981 got = base.replace(**{name: newval})
1982 self.assertEqual(expected, got)
1983 i += 1
1984
1985 # Out of bounds.
1986 base = cls(1)
1987 self.assertRaises(ValueError, base.replace, hour=24)
1988 self.assertRaises(ValueError, base.replace, minute=-1)
1989 self.assertRaises(ValueError, base.replace, second=100)
1990 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1991
Tim Petersa98924a2003-05-17 05:55:19 +00001992 def test_subclass_time(self):
1993
1994 class C(self.theclass):
1995 theAnswer = 42
1996
1997 def __new__(cls, *args, **kws):
1998 temp = kws.copy()
1999 extra = temp.pop('extra')
2000 result = self.theclass.__new__(cls, *args, **temp)
2001 result.extra = extra
2002 return result
2003
2004 def newmeth(self, start):
2005 return start + self.hour + self.second
2006
2007 args = 4, 5, 6
2008
2009 dt1 = self.theclass(*args)
2010 dt2 = C(*args, **{'extra': 7})
2011
2012 self.assertEqual(dt2.__class__, C)
2013 self.assertEqual(dt2.theAnswer, 42)
2014 self.assertEqual(dt2.extra, 7)
2015 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2016 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2017
Armin Rigof4afb212005-11-07 07:15:48 +00002018 def test_backdoor_resistance(self):
2019 # see TestDate.test_backdoor_resistance().
2020 base = '2:59.0'
2021 for hour_byte in ' ', '9', chr(24), '\xff':
2022 self.assertRaises(TypeError, self.theclass,
2023 hour_byte + base[1:])
2024
Tim Peters855fe882002-12-22 03:43:39 +00002025# A mixin for classes with a tzinfo= argument. Subclasses must define
2026# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002027# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002028class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002029
Tim Petersbad8ff02002-12-30 20:52:32 +00002030 def test_argument_passing(self):
2031 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002032 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002033 class introspective(tzinfo):
2034 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002035 def utcoffset(self, dt):
2036 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002037 dst = utcoffset
2038
2039 obj = cls(1, 2, 3, tzinfo=introspective())
2040
Tim Peters0bf60bd2003-01-08 20:40:01 +00002041 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002042 self.assertEqual(obj.tzname(), expected)
2043
Tim Peters0bf60bd2003-01-08 20:40:01 +00002044 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002045 self.assertEqual(obj.utcoffset(), expected)
2046 self.assertEqual(obj.dst(), expected)
2047
Tim Peters855fe882002-12-22 03:43:39 +00002048 def test_bad_tzinfo_classes(self):
2049 cls = self.theclass
2050 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002051
Tim Peters855fe882002-12-22 03:43:39 +00002052 class NiceTry(object):
2053 def __init__(self): pass
2054 def utcoffset(self, dt): pass
2055 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2056
2057 class BetterTry(tzinfo):
2058 def __init__(self): pass
2059 def utcoffset(self, dt): pass
2060 b = BetterTry()
2061 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002062 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002063
2064 def test_utc_offset_out_of_bounds(self):
2065 class Edgy(tzinfo):
2066 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002067 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002068 def utcoffset(self, dt):
2069 return self.offset
2070
2071 cls = self.theclass
2072 for offset, legit in ((-1440, False),
2073 (-1439, True),
2074 (1439, True),
2075 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002076 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002077 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002078 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002079 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002080 else:
2081 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002082 if legit:
2083 aofs = abs(offset)
2084 h, m = divmod(aofs, 60)
2085 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002086 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002087 t = t.timetz()
2088 self.assertEqual(str(t), "01:02:03" + tag)
2089 else:
2090 self.assertRaises(ValueError, str, t)
2091
2092 def test_tzinfo_classes(self):
2093 cls = self.theclass
2094 class C1(tzinfo):
2095 def utcoffset(self, dt): return None
2096 def dst(self, dt): return None
2097 def tzname(self, dt): return None
2098 for t in (cls(1, 1, 1),
2099 cls(1, 1, 1, tzinfo=None),
2100 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002101 self.assertTrue(t.utcoffset() is None)
2102 self.assertTrue(t.dst() is None)
2103 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002104
Tim Peters855fe882002-12-22 03:43:39 +00002105 class C3(tzinfo):
2106 def utcoffset(self, dt): return timedelta(minutes=-1439)
2107 def dst(self, dt): return timedelta(minutes=1439)
2108 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002109 t = cls(1, 1, 1, tzinfo=C3())
2110 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2111 self.assertEqual(t.dst(), timedelta(minutes=1439))
2112 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002113
2114 # Wrong types.
2115 class C4(tzinfo):
2116 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002117 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002118 def tzname(self, dt): return 0
2119 t = cls(1, 1, 1, tzinfo=C4())
2120 self.assertRaises(TypeError, t.utcoffset)
2121 self.assertRaises(TypeError, t.dst)
2122 self.assertRaises(TypeError, t.tzname)
2123
2124 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002125 class C6(tzinfo):
2126 def utcoffset(self, dt): return timedelta(hours=-24)
2127 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002128 t = cls(1, 1, 1, tzinfo=C6())
2129 self.assertRaises(ValueError, t.utcoffset)
2130 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002131
2132 # Not a whole number of minutes.
2133 class C7(tzinfo):
2134 def utcoffset(self, dt): return timedelta(seconds=61)
2135 def dst(self, dt): return timedelta(microseconds=-81)
2136 t = cls(1, 1, 1, tzinfo=C7())
2137 self.assertRaises(ValueError, t.utcoffset)
2138 self.assertRaises(ValueError, t.dst)
2139
Tim Peters4c0db782002-12-26 05:01:19 +00002140 def test_aware_compare(self):
2141 cls = self.theclass
2142
Tim Peters60c76e42002-12-27 00:41:11 +00002143 # Ensure that utcoffset() gets ignored if the comparands have
2144 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002145 class OperandDependentOffset(tzinfo):
2146 def utcoffset(self, t):
2147 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002148 # d0 and d1 equal after adjustment
2149 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002150 else:
Tim Peters397301e2003-01-02 21:28:08 +00002151 # d2 off in the weeds
2152 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002153
2154 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2155 d0 = base.replace(minute=3)
2156 d1 = base.replace(minute=9)
2157 d2 = base.replace(minute=11)
2158 for x in d0, d1, d2:
2159 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002160 for op in lt, le, gt, ge, eq, ne:
2161 got = op(x, y)
2162 expected = op(x.minute, y.minute)
2163 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002164
2165 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002166 # Note that a time can't actually have an operand-depedent offset,
2167 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2168 # so skip this test for time.
2169 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002170 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2171 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2172 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2173 for x in d0, d1, d2:
2174 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002175 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002176 if (x is d0 or x is d1) and (y is d0 or y is d1):
2177 expected = 0
2178 elif x is y is d2:
2179 expected = 0
2180 elif x is d2:
2181 expected = -1
2182 else:
2183 assert y is d2
2184 expected = 1
2185 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002186
Tim Peters855fe882002-12-22 03:43:39 +00002187
Tim Peters0bf60bd2003-01-08 20:40:01 +00002188# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002189class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002190 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002191
2192 def test_empty(self):
2193 t = self.theclass()
2194 self.assertEqual(t.hour, 0)
2195 self.assertEqual(t.minute, 0)
2196 self.assertEqual(t.second, 0)
2197 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002198 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002199
Tim Peters2a799bf2002-12-16 20:18:38 +00002200 def test_zones(self):
2201 est = FixedOffset(-300, "EST", 1)
2202 utc = FixedOffset(0, "UTC", -2)
2203 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002204 t1 = time( 7, 47, tzinfo=est)
2205 t2 = time(12, 47, tzinfo=utc)
2206 t3 = time(13, 47, tzinfo=met)
2207 t4 = time(microsecond=40)
2208 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002209
2210 self.assertEqual(t1.tzinfo, est)
2211 self.assertEqual(t2.tzinfo, utc)
2212 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002213 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002214 self.assertEqual(t5.tzinfo, utc)
2215
Tim Peters855fe882002-12-22 03:43:39 +00002216 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2217 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2218 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002219 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002220 self.assertRaises(TypeError, t1.utcoffset, "no args")
2221
2222 self.assertEqual(t1.tzname(), "EST")
2223 self.assertEqual(t2.tzname(), "UTC")
2224 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002225 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002226 self.assertRaises(TypeError, t1.tzname, "no args")
2227
Tim Peters855fe882002-12-22 03:43:39 +00002228 self.assertEqual(t1.dst(), timedelta(minutes=1))
2229 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2230 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002231 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002232 self.assertRaises(TypeError, t1.dst, "no args")
2233
2234 self.assertEqual(hash(t1), hash(t2))
2235 self.assertEqual(hash(t1), hash(t3))
2236 self.assertEqual(hash(t2), hash(t3))
2237
2238 self.assertEqual(t1, t2)
2239 self.assertEqual(t1, t3)
2240 self.assertEqual(t2, t3)
2241 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2242 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2243 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2244
2245 self.assertEqual(str(t1), "07:47:00-05:00")
2246 self.assertEqual(str(t2), "12:47:00+00:00")
2247 self.assertEqual(str(t3), "13:47:00+01:00")
2248 self.assertEqual(str(t4), "00:00:00.000040")
2249 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2250
2251 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2252 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2253 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2254 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2255 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2256
Tim Peters0bf60bd2003-01-08 20:40:01 +00002257 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002258 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2259 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2260 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2261 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2262 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2263
2264 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2265 "07:47:00 %Z=EST %z=-0500")
2266 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2267 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2268
2269 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002270 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002271 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2272 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2273
Tim Petersb92bb712002-12-21 17:44:07 +00002274 # Check that an invalid tzname result raises an exception.
2275 class Badtzname(tzinfo):
2276 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002277 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002278 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2279 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002280
2281 def test_hash_edge_cases(self):
2282 # Offsets that overflow a basic time.
2283 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2284 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2285 self.assertEqual(hash(t1), hash(t2))
2286
2287 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2288 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2289 self.assertEqual(hash(t1), hash(t2))
2290
Tim Peters2a799bf2002-12-16 20:18:38 +00002291 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002292 # Try one without a tzinfo.
2293 args = 20, 59, 16, 64**2
2294 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002295 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002296 green = pickler.dumps(orig, proto)
2297 derived = unpickler.loads(green)
2298 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002299
2300 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002301 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002302 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002303 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002304 green = pickler.dumps(orig, proto)
2305 derived = unpickler.loads(green)
2306 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002307 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002308 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2309 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002310
2311 def test_more_bool(self):
2312 # Test cases with non-None tzinfo.
2313 cls = self.theclass
2314
2315 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002316 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002317
2318 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002319 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002320
2321 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002322 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002323
2324 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002325 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002326
2327 # Mostly ensuring this doesn't overflow internally.
2328 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002329 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002330
2331 # But this should yield a value error -- the utcoffset is bogus.
2332 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2333 self.assertRaises(ValueError, lambda: bool(t))
2334
2335 # Likewise.
2336 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2337 self.assertRaises(ValueError, lambda: bool(t))
2338
Tim Peters12bf3392002-12-24 05:41:27 +00002339 def test_replace(self):
2340 cls = self.theclass
2341 z100 = FixedOffset(100, "+100")
2342 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2343 args = [1, 2, 3, 4, z100]
2344 base = cls(*args)
2345 self.assertEqual(base, base.replace())
2346
2347 i = 0
2348 for name, newval in (("hour", 5),
2349 ("minute", 6),
2350 ("second", 7),
2351 ("microsecond", 8),
2352 ("tzinfo", zm200)):
2353 newargs = args[:]
2354 newargs[i] = newval
2355 expected = cls(*newargs)
2356 got = base.replace(**{name: newval})
2357 self.assertEqual(expected, got)
2358 i += 1
2359
2360 # Ensure we can get rid of a tzinfo.
2361 self.assertEqual(base.tzname(), "+100")
2362 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002363 self.assertTrue(base2.tzinfo is None)
2364 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002365
2366 # Ensure we can add one.
2367 base3 = base2.replace(tzinfo=z100)
2368 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002369 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002370
2371 # Out of bounds.
2372 base = cls(1)
2373 self.assertRaises(ValueError, base.replace, hour=24)
2374 self.assertRaises(ValueError, base.replace, minute=-1)
2375 self.assertRaises(ValueError, base.replace, second=100)
2376 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2377
Tim Peters60c76e42002-12-27 00:41:11 +00002378 def test_mixed_compare(self):
2379 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002380 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002381 self.assertEqual(t1, t2)
2382 t2 = t2.replace(tzinfo=None)
2383 self.assertEqual(t1, t2)
2384 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2385 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002386 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2387 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002388
Tim Peters0bf60bd2003-01-08 20:40:01 +00002389 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002390 class Varies(tzinfo):
2391 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002392 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002393 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002394 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002395 return self.offset
2396
2397 v = Varies()
2398 t1 = t2.replace(tzinfo=v)
2399 t2 = t2.replace(tzinfo=v)
2400 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2401 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2402 self.assertEqual(t1, t2)
2403
2404 # But if they're not identical, it isn't ignored.
2405 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002406 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002407
Tim Petersa98924a2003-05-17 05:55:19 +00002408 def test_subclass_timetz(self):
2409
2410 class C(self.theclass):
2411 theAnswer = 42
2412
2413 def __new__(cls, *args, **kws):
2414 temp = kws.copy()
2415 extra = temp.pop('extra')
2416 result = self.theclass.__new__(cls, *args, **temp)
2417 result.extra = extra
2418 return result
2419
2420 def newmeth(self, start):
2421 return start + self.hour + self.second
2422
2423 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2424
2425 dt1 = self.theclass(*args)
2426 dt2 = C(*args, **{'extra': 7})
2427
2428 self.assertEqual(dt2.__class__, C)
2429 self.assertEqual(dt2.theAnswer, 42)
2430 self.assertEqual(dt2.extra, 7)
2431 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2432 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2433
Tim Peters4c0db782002-12-26 05:01:19 +00002434
Tim Peters0bf60bd2003-01-08 20:40:01 +00002435# Testing datetime objects with a non-None tzinfo.
2436
Guido van Rossumd8faa362007-04-27 19:54:29 +00002437class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002438 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002439
2440 def test_trivial(self):
2441 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2442 self.assertEqual(dt.year, 1)
2443 self.assertEqual(dt.month, 2)
2444 self.assertEqual(dt.day, 3)
2445 self.assertEqual(dt.hour, 4)
2446 self.assertEqual(dt.minute, 5)
2447 self.assertEqual(dt.second, 6)
2448 self.assertEqual(dt.microsecond, 7)
2449 self.assertEqual(dt.tzinfo, None)
2450
2451 def test_even_more_compare(self):
2452 # The test_compare() and test_more_compare() inherited from TestDate
2453 # and TestDateTime covered non-tzinfo cases.
2454
2455 # Smallest possible after UTC adjustment.
2456 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2457 # Largest possible after UTC adjustment.
2458 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2459 tzinfo=FixedOffset(-1439, ""))
2460
2461 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002462 self.assertTrue(t1 < t2)
2463 self.assertTrue(t1 != t2)
2464 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002465
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002466 self.assertEqual(t1, t1)
2467 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002468
2469 # Equal afer adjustment.
2470 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2471 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2472 self.assertEqual(t1, t2)
2473
2474 # Change t1 not to subtract a minute, and t1 should be larger.
2475 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002476 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002477
2478 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2479 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002480 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002481
2482 # Back to the original t1, but make seconds resolve it.
2483 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2484 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002485 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002486
2487 # Likewise, but make microseconds resolve it.
2488 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2489 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002490 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002491
2492 # Make t2 naive and it should fail.
2493 t2 = self.theclass.min
2494 self.assertRaises(TypeError, lambda: t1 == t2)
2495 self.assertEqual(t2, t2)
2496
2497 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2498 class Naive(tzinfo):
2499 def utcoffset(self, dt): return None
2500 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2501 self.assertRaises(TypeError, lambda: t1 == t2)
2502 self.assertEqual(t2, t2)
2503
2504 # OTOH, it's OK to compare two of these mixing the two ways of being
2505 # naive.
2506 t1 = self.theclass(5, 6, 7)
2507 self.assertEqual(t1, t2)
2508
2509 # Try a bogus uctoffset.
2510 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002511 def utcoffset(self, dt):
2512 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002513 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2514 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002515 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002516
Tim Peters2a799bf2002-12-16 20:18:38 +00002517 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002518 # Try one without a tzinfo.
2519 args = 6, 7, 23, 20, 59, 1, 64**2
2520 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002521 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002522 green = pickler.dumps(orig, proto)
2523 derived = unpickler.loads(green)
2524 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002525
2526 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002527 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002528 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002529 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002530 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002531 green = pickler.dumps(orig, proto)
2532 derived = unpickler.loads(green)
2533 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002534 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002535 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2536 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002537
2538 def test_extreme_hashes(self):
2539 # If an attempt is made to hash these via subtracting the offset
2540 # then hashing a datetime object, OverflowError results. The
2541 # Python implementation used to blow up here.
2542 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2543 hash(t)
2544 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2545 tzinfo=FixedOffset(-1439, ""))
2546 hash(t)
2547
2548 # OTOH, an OOB offset should blow up.
2549 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2550 self.assertRaises(ValueError, hash, t)
2551
2552 def test_zones(self):
2553 est = FixedOffset(-300, "EST")
2554 utc = FixedOffset(0, "UTC")
2555 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002556 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2557 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2558 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002559 self.assertEqual(t1.tzinfo, est)
2560 self.assertEqual(t2.tzinfo, utc)
2561 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002562 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2563 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2564 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002565 self.assertEqual(t1.tzname(), "EST")
2566 self.assertEqual(t2.tzname(), "UTC")
2567 self.assertEqual(t3.tzname(), "MET")
2568 self.assertEqual(hash(t1), hash(t2))
2569 self.assertEqual(hash(t1), hash(t3))
2570 self.assertEqual(hash(t2), hash(t3))
2571 self.assertEqual(t1, t2)
2572 self.assertEqual(t1, t3)
2573 self.assertEqual(t2, t3)
2574 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2575 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2576 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002577 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002578 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2579 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2580 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2581
2582 def test_combine(self):
2583 met = FixedOffset(60, "MET")
2584 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002585 tz = time(18, 45, 3, 1234, tzinfo=met)
2586 dt = datetime.combine(d, tz)
2587 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002588 tzinfo=met))
2589
2590 def test_extract(self):
2591 met = FixedOffset(60, "MET")
2592 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2593 self.assertEqual(dt.date(), date(2002, 3, 4))
2594 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002595 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002596
2597 def test_tz_aware_arithmetic(self):
2598 import random
2599
2600 now = self.theclass.now()
2601 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002602 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002603 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002604 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002605 self.assertEqual(nowaware.timetz(), timeaware)
2606
2607 # Can't mix aware and non-aware.
2608 self.assertRaises(TypeError, lambda: now - nowaware)
2609 self.assertRaises(TypeError, lambda: nowaware - now)
2610
Tim Peters0bf60bd2003-01-08 20:40:01 +00002611 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002612 self.assertRaises(TypeError, lambda: now + nowaware)
2613 self.assertRaises(TypeError, lambda: nowaware + now)
2614 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2615
2616 # Subtracting should yield 0.
2617 self.assertEqual(now - now, timedelta(0))
2618 self.assertEqual(nowaware - nowaware, timedelta(0))
2619
2620 # Adding a delta should preserve tzinfo.
2621 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2622 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002623 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002624 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002625 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002626 self.assertEqual(nowawareplus, nowawareplus2)
2627
2628 # that - delta should be what we started with, and that - what we
2629 # started with should be delta.
2630 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002631 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002632 self.assertEqual(nowaware, diff)
2633 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2634 self.assertEqual(nowawareplus - nowaware, delta)
2635
2636 # Make up a random timezone.
2637 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002638 # Attach it to nowawareplus.
2639 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002640 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002641 # Make sure the difference takes the timezone adjustments into account.
2642 got = nowaware - nowawareplus
2643 # Expected: (nowaware base - nowaware offset) -
2644 # (nowawareplus base - nowawareplus offset) =
2645 # (nowaware base - nowawareplus base) +
2646 # (nowawareplus offset - nowaware offset) =
2647 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002648 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002649 self.assertEqual(got, expected)
2650
2651 # Try max possible difference.
2652 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2653 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2654 tzinfo=FixedOffset(-1439, "max"))
2655 maxdiff = max - min
2656 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2657 timedelta(minutes=2*1439))
2658
2659 def test_tzinfo_now(self):
2660 meth = self.theclass.now
2661 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2662 base = meth()
2663 # Try with and without naming the keyword.
2664 off42 = FixedOffset(42, "42")
2665 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002666 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002667 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002668 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002669 # Bad argument with and w/o naming the keyword.
2670 self.assertRaises(TypeError, meth, 16)
2671 self.assertRaises(TypeError, meth, tzinfo=16)
2672 # Bad keyword name.
2673 self.assertRaises(TypeError, meth, tinfo=off42)
2674 # Too many args.
2675 self.assertRaises(TypeError, meth, off42, off42)
2676
Tim Peters10cadce2003-01-23 19:58:02 +00002677 # We don't know which time zone we're in, and don't have a tzinfo
2678 # class to represent it, so seeing whether a tz argument actually
2679 # does a conversion is tricky.
2680 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2681 utc = FixedOffset(0, "utc", 0)
2682 for dummy in range(3):
2683 now = datetime.now(weirdtz)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002684 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002685 utcnow = datetime.utcnow().replace(tzinfo=utc)
2686 now2 = utcnow.astimezone(weirdtz)
2687 if abs(now - now2) < timedelta(seconds=30):
2688 break
2689 # Else the code is broken, or more than 30 seconds passed between
2690 # calls; assuming the latter, just try again.
2691 else:
2692 # Three strikes and we're out.
2693 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2694
Tim Peters2a799bf2002-12-16 20:18:38 +00002695 def test_tzinfo_fromtimestamp(self):
2696 import time
2697 meth = self.theclass.fromtimestamp
2698 ts = time.time()
2699 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2700 base = meth(ts)
2701 # Try with and without naming the keyword.
2702 off42 = FixedOffset(42, "42")
2703 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002704 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002705 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002706 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002707 # Bad argument with and w/o naming the keyword.
2708 self.assertRaises(TypeError, meth, ts, 16)
2709 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2710 # Bad keyword name.
2711 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2712 # Too many args.
2713 self.assertRaises(TypeError, meth, ts, off42, off42)
2714 # Too few args.
2715 self.assertRaises(TypeError, meth)
2716
Tim Peters2a44a8d2003-01-23 20:53:10 +00002717 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002718 timestamp = 1000000000
2719 utcdatetime = datetime.utcfromtimestamp(timestamp)
2720 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2721 # But on some flavor of Mac, it's nowhere near that. So we can't have
2722 # any idea here what time that actually is, we can only test that
2723 # relative changes match.
2724 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2725 tz = FixedOffset(utcoffset, "tz", 0)
2726 expected = utcdatetime + utcoffset
2727 got = datetime.fromtimestamp(timestamp, tz)
2728 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002729
Tim Peters2a799bf2002-12-16 20:18:38 +00002730 def test_tzinfo_utcnow(self):
2731 meth = self.theclass.utcnow
2732 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2733 base = meth()
2734 # Try with and without naming the keyword; for whatever reason,
2735 # utcnow() doesn't accept a tzinfo argument.
2736 off42 = FixedOffset(42, "42")
2737 self.assertRaises(TypeError, meth, off42)
2738 self.assertRaises(TypeError, meth, tzinfo=off42)
2739
2740 def test_tzinfo_utcfromtimestamp(self):
2741 import time
2742 meth = self.theclass.utcfromtimestamp
2743 ts = time.time()
2744 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2745 base = meth(ts)
2746 # Try with and without naming the keyword; for whatever reason,
2747 # utcfromtimestamp() doesn't accept a tzinfo argument.
2748 off42 = FixedOffset(42, "42")
2749 self.assertRaises(TypeError, meth, ts, off42)
2750 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2751
2752 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002753 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002754 # DST flag.
2755 class DST(tzinfo):
2756 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002757 if isinstance(dstvalue, int):
2758 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002759 self.dstvalue = dstvalue
2760 def dst(self, dt):
2761 return self.dstvalue
2762
2763 cls = self.theclass
2764 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2765 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2766 t = d.timetuple()
2767 self.assertEqual(1, t.tm_year)
2768 self.assertEqual(1, t.tm_mon)
2769 self.assertEqual(1, t.tm_mday)
2770 self.assertEqual(10, t.tm_hour)
2771 self.assertEqual(20, t.tm_min)
2772 self.assertEqual(30, t.tm_sec)
2773 self.assertEqual(0, t.tm_wday)
2774 self.assertEqual(1, t.tm_yday)
2775 self.assertEqual(flag, t.tm_isdst)
2776
2777 # dst() returns wrong type.
2778 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2779
2780 # dst() at the edge.
2781 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2782 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2783
2784 # dst() out of range.
2785 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2786 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2787
2788 def test_utctimetuple(self):
2789 class DST(tzinfo):
2790 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002791 if isinstance(dstvalue, int):
2792 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002793 self.dstvalue = dstvalue
2794 def dst(self, dt):
2795 return self.dstvalue
2796
2797 cls = self.theclass
2798 # This can't work: DST didn't implement utcoffset.
2799 self.assertRaises(NotImplementedError,
2800 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2801
2802 class UOFS(DST):
2803 def __init__(self, uofs, dofs=None):
2804 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002805 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002806 def utcoffset(self, dt):
2807 return self.uofs
2808
2809 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2810 # in effect for a UTC time.
2811 for dstvalue in -33, 33, 0, None:
2812 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2813 t = d.utctimetuple()
2814 self.assertEqual(d.year, t.tm_year)
2815 self.assertEqual(d.month, t.tm_mon)
2816 self.assertEqual(d.day, t.tm_mday)
2817 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2818 self.assertEqual(13, t.tm_min)
2819 self.assertEqual(d.second, t.tm_sec)
2820 self.assertEqual(d.weekday(), t.tm_wday)
2821 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2822 t.tm_yday)
2823 self.assertEqual(0, t.tm_isdst)
2824
2825 # At the edges, UTC adjustment can normalize into years out-of-range
2826 # for a datetime object. Ensure that a correct timetuple is
2827 # created anyway.
2828 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2829 # That goes back 1 minute less than a full day.
2830 t = tiny.utctimetuple()
2831 self.assertEqual(t.tm_year, MINYEAR-1)
2832 self.assertEqual(t.tm_mon, 12)
2833 self.assertEqual(t.tm_mday, 31)
2834 self.assertEqual(t.tm_hour, 0)
2835 self.assertEqual(t.tm_min, 1)
2836 self.assertEqual(t.tm_sec, 37)
2837 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2838 self.assertEqual(t.tm_isdst, 0)
2839
2840 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2841 # That goes forward 1 minute less than a full day.
2842 t = huge.utctimetuple()
2843 self.assertEqual(t.tm_year, MAXYEAR+1)
2844 self.assertEqual(t.tm_mon, 1)
2845 self.assertEqual(t.tm_mday, 1)
2846 self.assertEqual(t.tm_hour, 23)
2847 self.assertEqual(t.tm_min, 58)
2848 self.assertEqual(t.tm_sec, 37)
2849 self.assertEqual(t.tm_yday, 1)
2850 self.assertEqual(t.tm_isdst, 0)
2851
2852 def test_tzinfo_isoformat(self):
2853 zero = FixedOffset(0, "+00:00")
2854 plus = FixedOffset(220, "+03:40")
2855 minus = FixedOffset(-231, "-03:51")
2856 unknown = FixedOffset(None, "")
2857
2858 cls = self.theclass
2859 datestr = '0001-02-03'
2860 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002861 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002862 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2863 timestr = '04:05:59' + (us and '.987001' or '')
2864 ofsstr = ofs is not None and d.tzname() or ''
2865 tailstr = timestr + ofsstr
2866 iso = d.isoformat()
2867 self.assertEqual(iso, datestr + 'T' + tailstr)
2868 self.assertEqual(iso, d.isoformat('T'))
2869 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002870 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002871 self.assertEqual(str(d), datestr + ' ' + tailstr)
2872
Tim Peters12bf3392002-12-24 05:41:27 +00002873 def test_replace(self):
2874 cls = self.theclass
2875 z100 = FixedOffset(100, "+100")
2876 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2877 args = [1, 2, 3, 4, 5, 6, 7, z100]
2878 base = cls(*args)
2879 self.assertEqual(base, base.replace())
2880
2881 i = 0
2882 for name, newval in (("year", 2),
2883 ("month", 3),
2884 ("day", 4),
2885 ("hour", 5),
2886 ("minute", 6),
2887 ("second", 7),
2888 ("microsecond", 8),
2889 ("tzinfo", zm200)):
2890 newargs = args[:]
2891 newargs[i] = newval
2892 expected = cls(*newargs)
2893 got = base.replace(**{name: newval})
2894 self.assertEqual(expected, got)
2895 i += 1
2896
2897 # Ensure we can get rid of a tzinfo.
2898 self.assertEqual(base.tzname(), "+100")
2899 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002900 self.assertTrue(base2.tzinfo is None)
2901 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002902
2903 # Ensure we can add one.
2904 base3 = base2.replace(tzinfo=z100)
2905 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002906 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002907
2908 # Out of bounds.
2909 base = cls(2000, 2, 29)
2910 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002911
Tim Peters80475bb2002-12-25 07:40:55 +00002912 def test_more_astimezone(self):
2913 # The inherited test_astimezone covered some trivial and error cases.
2914 fnone = FixedOffset(None, "None")
2915 f44m = FixedOffset(44, "44")
2916 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2917
Tim Peters10cadce2003-01-23 19:58:02 +00002918 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002919 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002920 # Replacing with degenerate tzinfo raises an exception.
2921 self.assertRaises(ValueError, dt.astimezone, fnone)
2922 # Ditto with None tz.
2923 self.assertRaises(TypeError, dt.astimezone, None)
2924 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002925 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002926 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002927 self.assertEqual(x.date(), dt.date())
2928 self.assertEqual(x.time(), dt.time())
2929
2930 # Replacing with different tzinfo does adjust.
2931 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002932 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002933 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2934 expected = dt - dt.utcoffset() # in effect, convert to UTC
2935 expected += fm5h.utcoffset(dt) # and from there to local time
2936 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2937 self.assertEqual(got.date(), expected.date())
2938 self.assertEqual(got.time(), expected.time())
2939 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002940 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002941 self.assertEqual(got, expected)
2942
Tim Peters4c0db782002-12-26 05:01:19 +00002943 def test_aware_subtract(self):
2944 cls = self.theclass
2945
Tim Peters60c76e42002-12-27 00:41:11 +00002946 # Ensure that utcoffset() is ignored when the operands have the
2947 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002948 class OperandDependentOffset(tzinfo):
2949 def utcoffset(self, t):
2950 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002951 # d0 and d1 equal after adjustment
2952 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002953 else:
Tim Peters397301e2003-01-02 21:28:08 +00002954 # d2 off in the weeds
2955 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002956
2957 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2958 d0 = base.replace(minute=3)
2959 d1 = base.replace(minute=9)
2960 d2 = base.replace(minute=11)
2961 for x in d0, d1, d2:
2962 for y in d0, d1, d2:
2963 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002964 expected = timedelta(minutes=x.minute - y.minute)
2965 self.assertEqual(got, expected)
2966
2967 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2968 # ignored.
2969 base = cls(8, 9, 10, 11, 12, 13, 14)
2970 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2971 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2972 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2973 for x in d0, d1, d2:
2974 for y in d0, d1, d2:
2975 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002976 if (x is d0 or x is d1) and (y is d0 or y is d1):
2977 expected = timedelta(0)
2978 elif x is y is d2:
2979 expected = timedelta(0)
2980 elif x is d2:
2981 expected = timedelta(minutes=(11-59)-0)
2982 else:
2983 assert y is d2
2984 expected = timedelta(minutes=0-(11-59))
2985 self.assertEqual(got, expected)
2986
Tim Peters60c76e42002-12-27 00:41:11 +00002987 def test_mixed_compare(self):
2988 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002989 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002990 self.assertEqual(t1, t2)
2991 t2 = t2.replace(tzinfo=None)
2992 self.assertEqual(t1, t2)
2993 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2994 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002995 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2996 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002997
Tim Peters0bf60bd2003-01-08 20:40:01 +00002998 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002999 class Varies(tzinfo):
3000 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003001 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003002 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003003 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003004 return self.offset
3005
3006 v = Varies()
3007 t1 = t2.replace(tzinfo=v)
3008 t2 = t2.replace(tzinfo=v)
3009 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3010 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3011 self.assertEqual(t1, t2)
3012
3013 # But if they're not identical, it isn't ignored.
3014 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003015 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003016
Tim Petersa98924a2003-05-17 05:55:19 +00003017 def test_subclass_datetimetz(self):
3018
3019 class C(self.theclass):
3020 theAnswer = 42
3021
3022 def __new__(cls, *args, **kws):
3023 temp = kws.copy()
3024 extra = temp.pop('extra')
3025 result = self.theclass.__new__(cls, *args, **temp)
3026 result.extra = extra
3027 return result
3028
3029 def newmeth(self, start):
3030 return start + self.hour + self.year
3031
3032 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3033
3034 dt1 = self.theclass(*args)
3035 dt2 = C(*args, **{'extra': 7})
3036
3037 self.assertEqual(dt2.__class__, C)
3038 self.assertEqual(dt2.theAnswer, 42)
3039 self.assertEqual(dt2.extra, 7)
3040 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3041 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3042
Tim Peters621818b2002-12-29 23:44:49 +00003043# Pain to set up DST-aware tzinfo classes.
3044
3045def first_sunday_on_or_after(dt):
3046 days_to_go = 6 - dt.weekday()
3047 if days_to_go:
3048 dt += timedelta(days_to_go)
3049 return dt
3050
3051ZERO = timedelta(0)
3052HOUR = timedelta(hours=1)
3053DAY = timedelta(days=1)
3054# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3055DSTSTART = datetime(1, 4, 1, 2)
3056# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003057# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3058# being standard time on that day, there is no spelling in local time of
3059# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3060DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003061
3062class USTimeZone(tzinfo):
3063
3064 def __init__(self, hours, reprname, stdname, dstname):
3065 self.stdoffset = timedelta(hours=hours)
3066 self.reprname = reprname
3067 self.stdname = stdname
3068 self.dstname = dstname
3069
3070 def __repr__(self):
3071 return self.reprname
3072
3073 def tzname(self, dt):
3074 if self.dst(dt):
3075 return self.dstname
3076 else:
3077 return self.stdname
3078
3079 def utcoffset(self, dt):
3080 return self.stdoffset + self.dst(dt)
3081
3082 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003083 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003084 # An exception instead may be sensible here, in one or more of
3085 # the cases.
3086 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003087 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003088
3089 # Find first Sunday in April.
3090 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3091 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3092
3093 # Find last Sunday in October.
3094 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3095 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3096
Tim Peters621818b2002-12-29 23:44:49 +00003097 # Can't compare naive to aware objects, so strip the timezone from
3098 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003099 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003100 return HOUR
3101 else:
3102 return ZERO
3103
Tim Peters521fc152002-12-31 17:36:56 +00003104Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3105Central = USTimeZone(-6, "Central", "CST", "CDT")
3106Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3107Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003108utc_real = FixedOffset(0, "UTC", 0)
3109# For better test coverage, we want another flavor of UTC that's west of
3110# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003111utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003112
3113class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003114 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003115 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003116 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003117
Tim Peters0bf60bd2003-01-08 20:40:01 +00003118 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003119
Tim Peters521fc152002-12-31 17:36:56 +00003120 # Check a time that's inside DST.
3121 def checkinside(self, dt, tz, utc, dston, dstoff):
3122 self.assertEqual(dt.dst(), HOUR)
3123
3124 # Conversion to our own timezone is always an identity.
3125 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003126
3127 asutc = dt.astimezone(utc)
3128 there_and_back = asutc.astimezone(tz)
3129
3130 # Conversion to UTC and back isn't always an identity here,
3131 # because there are redundant spellings (in local time) of
3132 # UTC time when DST begins: the clock jumps from 1:59:59
3133 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3134 # make sense then. The classes above treat 2:MM:SS as
3135 # daylight time then (it's "after 2am"), really an alias
3136 # for 1:MM:SS standard time. The latter form is what
3137 # conversion back from UTC produces.
3138 if dt.date() == dston.date() and dt.hour == 2:
3139 # We're in the redundant hour, and coming back from
3140 # UTC gives the 1:MM:SS standard-time spelling.
3141 self.assertEqual(there_and_back + HOUR, dt)
3142 # Although during was considered to be in daylight
3143 # time, there_and_back is not.
3144 self.assertEqual(there_and_back.dst(), ZERO)
3145 # They're the same times in UTC.
3146 self.assertEqual(there_and_back.astimezone(utc),
3147 dt.astimezone(utc))
3148 else:
3149 # We're not in the redundant hour.
3150 self.assertEqual(dt, there_and_back)
3151
Tim Peters327098a2003-01-20 22:54:38 +00003152 # Because we have a redundant spelling when DST begins, there is
3153 # (unforunately) an hour when DST ends that can't be spelled at all in
3154 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3155 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3156 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3157 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3158 # expressed in local time. Nevertheless, we want conversion back
3159 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003160 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003161 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003162 if dt.date() == dstoff.date() and dt.hour == 0:
3163 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003164 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003165 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3166 nexthour_utc += HOUR
3167 nexthour_tz = nexthour_utc.astimezone(tz)
3168 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003169 else:
Tim Peters327098a2003-01-20 22:54:38 +00003170 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003171
3172 # Check a time that's outside DST.
3173 def checkoutside(self, dt, tz, utc):
3174 self.assertEqual(dt.dst(), ZERO)
3175
3176 # Conversion to our own timezone is always an identity.
3177 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003178
3179 # Converting to UTC and back is an identity too.
3180 asutc = dt.astimezone(utc)
3181 there_and_back = asutc.astimezone(tz)
3182 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003183
Tim Peters1024bf82002-12-30 17:09:40 +00003184 def convert_between_tz_and_utc(self, tz, utc):
3185 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003186 # Because 1:MM on the day DST ends is taken as being standard time,
3187 # there is no spelling in tz for the last hour of daylight time.
3188 # For purposes of the test, the last hour of DST is 0:MM, which is
3189 # taken as being daylight time (and 1:MM is taken as being standard
3190 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003191 dstoff = self.dstoff.replace(tzinfo=tz)
3192 for delta in (timedelta(weeks=13),
3193 DAY,
3194 HOUR,
3195 timedelta(minutes=1),
3196 timedelta(microseconds=1)):
3197
Tim Peters521fc152002-12-31 17:36:56 +00003198 self.checkinside(dston, tz, utc, dston, dstoff)
3199 for during in dston + delta, dstoff - delta:
3200 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003201
Tim Peters521fc152002-12-31 17:36:56 +00003202 self.checkoutside(dstoff, tz, utc)
3203 for outside in dston - delta, dstoff + delta:
3204 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003205
Tim Peters621818b2002-12-29 23:44:49 +00003206 def test_easy(self):
3207 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003208 self.convert_between_tz_and_utc(Eastern, utc_real)
3209 self.convert_between_tz_and_utc(Pacific, utc_real)
3210 self.convert_between_tz_and_utc(Eastern, utc_fake)
3211 self.convert_between_tz_and_utc(Pacific, utc_fake)
3212 # The next is really dancing near the edge. It works because
3213 # Pacific and Eastern are far enough apart that their "problem
3214 # hours" don't overlap.
3215 self.convert_between_tz_and_utc(Eastern, Pacific)
3216 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003217 # OTOH, these fail! Don't enable them. The difficulty is that
3218 # the edge case tests assume that every hour is representable in
3219 # the "utc" class. This is always true for a fixed-offset tzinfo
3220 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3221 # For these adjacent DST-aware time zones, the range of time offsets
3222 # tested ends up creating hours in the one that aren't representable
3223 # in the other. For the same reason, we would see failures in the
3224 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3225 # offset deltas in convert_between_tz_and_utc().
3226 #
3227 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3228 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003229
Tim Petersf3615152003-01-01 21:51:37 +00003230 def test_tricky(self):
3231 # 22:00 on day before daylight starts.
3232 fourback = self.dston - timedelta(hours=4)
3233 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003234 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003235 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3236 # 2", we should get the 3 spelling.
3237 # If we plug 22:00 the day before into Eastern, it "looks like std
3238 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3239 # to 22:00 lands on 2:00, which makes no sense in local time (the
3240 # local clock jumps from 1 to 3). The point here is to make sure we
3241 # get the 3 spelling.
3242 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003243 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003244 self.assertEqual(expected, got)
3245
3246 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3247 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003248 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003249 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3250 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3251 # spelling.
3252 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003253 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003254 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003255
Tim Petersadf64202003-01-04 06:03:15 +00003256 # Now on the day DST ends, we want "repeat an hour" behavior.
3257 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3258 # EST 23:MM 0:MM 1:MM 2:MM
3259 # EDT 0:MM 1:MM 2:MM 3:MM
3260 # wall 0:MM 1:MM 1:MM 2:MM against these
3261 for utc in utc_real, utc_fake:
3262 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003263 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003264 # Convert that to UTC.
3265 first_std_hour -= tz.utcoffset(None)
3266 # Adjust for possibly fake UTC.
3267 asutc = first_std_hour + utc.utcoffset(None)
3268 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3269 # tz=Eastern.
3270 asutcbase = asutc.replace(tzinfo=utc)
3271 for tzhour in (0, 1, 1, 2):
3272 expectedbase = self.dstoff.replace(hour=tzhour)
3273 for minute in 0, 30, 59:
3274 expected = expectedbase.replace(minute=minute)
3275 asutc = asutcbase.replace(minute=minute)
3276 astz = asutc.astimezone(tz)
3277 self.assertEqual(astz.replace(tzinfo=None), expected)
3278 asutcbase += HOUR
3279
3280
Tim Peters710fb152003-01-02 19:35:54 +00003281 def test_bogus_dst(self):
3282 class ok(tzinfo):
3283 def utcoffset(self, dt): return HOUR
3284 def dst(self, dt): return HOUR
3285
3286 now = self.theclass.now().replace(tzinfo=utc_real)
3287 # Doesn't blow up.
3288 now.astimezone(ok())
3289
3290 # Does blow up.
3291 class notok(ok):
3292 def dst(self, dt): return None
3293 self.assertRaises(ValueError, now.astimezone, notok())
3294
Tim Peters52dcce22003-01-23 16:36:11 +00003295 def test_fromutc(self):
3296 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3297 now = datetime.utcnow().replace(tzinfo=utc_real)
3298 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3299 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3300 enow = Eastern.fromutc(now) # doesn't blow up
3301 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3302 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3303 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3304
3305 # Always converts UTC to standard time.
3306 class FauxUSTimeZone(USTimeZone):
3307 def fromutc(self, dt):
3308 return dt + self.stdoffset
3309 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3310
3311 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3312 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3313 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3314
3315 # Check around DST start.
3316 start = self.dston.replace(hour=4, tzinfo=Eastern)
3317 fstart = start.replace(tzinfo=FEastern)
3318 for wall in 23, 0, 1, 3, 4, 5:
3319 expected = start.replace(hour=wall)
3320 if wall == 23:
3321 expected -= timedelta(days=1)
3322 got = Eastern.fromutc(start)
3323 self.assertEqual(expected, got)
3324
3325 expected = fstart + FEastern.stdoffset
3326 got = FEastern.fromutc(fstart)
3327 self.assertEqual(expected, got)
3328
3329 # Ensure astimezone() calls fromutc() too.
3330 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3331 self.assertEqual(expected, got)
3332
3333 start += HOUR
3334 fstart += HOUR
3335
3336 # Check around DST end.
3337 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3338 fstart = start.replace(tzinfo=FEastern)
3339 for wall in 0, 1, 1, 2, 3, 4:
3340 expected = start.replace(hour=wall)
3341 got = Eastern.fromutc(start)
3342 self.assertEqual(expected, got)
3343
3344 expected = fstart + FEastern.stdoffset
3345 got = FEastern.fromutc(fstart)
3346 self.assertEqual(expected, got)
3347
3348 # Ensure astimezone() calls fromutc() too.
3349 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3350 self.assertEqual(expected, got)
3351
3352 start += HOUR
3353 fstart += HOUR
3354
Tim Peters710fb152003-01-02 19:35:54 +00003355
Tim Peters528ca532004-09-16 01:30:50 +00003356#############################################################################
3357# oddballs
3358
3359class Oddballs(unittest.TestCase):
3360
3361 def test_bug_1028306(self):
3362 # Trying to compare a date to a datetime should act like a mixed-
3363 # type comparison, despite that datetime is a subclass of date.
3364 as_date = date.today()
3365 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003366 self.assertTrue(as_date != as_datetime)
3367 self.assertTrue(as_datetime != as_date)
3368 self.assertTrue(not as_date == as_datetime)
3369 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003370 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3371 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3372 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3373 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3374 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3375 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3376 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3377 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3378
3379 # Neverthelss, comparison should work with the base-class (date)
3380 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003381 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003382 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003383 as_different = as_datetime.replace(day= different_day)
3384 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003385
3386 # And date should compare with other subclasses of date. If a
3387 # subclass wants to stop this, it's up to the subclass to do so.
3388 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3389 self.assertEqual(as_date, date_sc)
3390 self.assertEqual(date_sc, as_date)
3391
3392 # Ditto for datetimes.
3393 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3394 as_date.day, 0, 0, 0)
3395 self.assertEqual(as_datetime, datetime_sc)
3396 self.assertEqual(datetime_sc, as_datetime)
3397
Tim Peters2a799bf2002-12-16 20:18:38 +00003398def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003399 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003400
3401if __name__ == "__main__":
3402 test_main()