blob: 4818482e59b8ba63792e279fe3100ed290573bdd [file] [log] [blame]
Tim Peters0bf60bd2003-01-08 20:40:01 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
Tim Peters2a799bf2002-12-16 20:18:38 +00005
Guido van Rossumd8faa362007-04-27 19:54:29 +00006import os
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00008import unittest
9
Mark Dickinsona56c4672009-01-27 18:17:45 +000010from operator import lt, le, gt, ge, eq, ne
11
Benjamin Petersonee8712c2008-05-20 21:35:26 +000012from test import support
Tim Peters2a799bf2002-12-16 20:18:38 +000013
14from datetime import MINYEAR, MAXYEAR
15from datetime import timedelta
16from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000017from datetime import time
18from datetime import date, datetime
19
Guido van Rossumbe6fe542007-07-19 23:55:34 +000020pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
21assert len(pickle_choices) == 3
Guido van Rossum177e41a2003-01-30 22:06:23 +000022
Tim Peters68124bb2003-02-08 03:46:31 +000023# An arbitrary collection of objects of non-datetime types, for testing
24# mixed-type comparisons.
Mark Dickinson5c2db372009-12-05 20:28:34 +000025OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000026
Tim Peters2a799bf2002-12-16 20:18:38 +000027
28#############################################################################
29# module tests
30
31class TestModule(unittest.TestCase):
32
33 def test_constants(self):
34 import datetime
35 self.assertEqual(datetime.MINYEAR, 1)
36 self.assertEqual(datetime.MAXYEAR, 9999)
37
38#############################################################################
39# tzinfo tests
40
41class FixedOffset(tzinfo):
42 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000043 if isinstance(offset, int):
44 offset = timedelta(minutes=offset)
45 if isinstance(dstoffset, int):
46 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000047 self.__offset = offset
48 self.__name = name
49 self.__dstoffset = dstoffset
50 def __repr__(self):
51 return self.__name.lower()
52 def utcoffset(self, dt):
53 return self.__offset
54 def tzname(self, dt):
55 return self.__name
56 def dst(self, dt):
57 return self.__dstoffset
58
Tim Petersfb8472c2002-12-21 05:04:42 +000059class PicklableFixedOffset(FixedOffset):
60 def __init__(self, offset=None, name=None, dstoffset=None):
61 FixedOffset.__init__(self, offset, name, dstoffset)
62
Tim Peters2a799bf2002-12-16 20:18:38 +000063class TestTZInfo(unittest.TestCase):
64
65 def test_non_abstractness(self):
66 # In order to allow subclasses to get pickled, the C implementation
67 # wasn't able to get away with having __init__ raise
68 # NotImplementedError.
69 useless = tzinfo()
70 dt = datetime.max
71 self.assertRaises(NotImplementedError, useless.tzname, dt)
72 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
73 self.assertRaises(NotImplementedError, useless.dst, dt)
74
75 def test_subclass_must_override(self):
76 class NotEnough(tzinfo):
77 def __init__(self, offset, name):
78 self.__offset = offset
79 self.__name = name
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000080 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000081 ne = NotEnough(3, "NotByALongShot")
Ezio Melottie9615932010-01-24 19:26:24 +000082 self.assertIsInstance(ne, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000083
84 dt = datetime.now()
85 self.assertRaises(NotImplementedError, ne.tzname, dt)
86 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
87 self.assertRaises(NotImplementedError, ne.dst, dt)
88
89 def test_normal(self):
90 fo = FixedOffset(3, "Three")
Ezio Melottie9615932010-01-24 19:26:24 +000091 self.assertIsInstance(fo, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000092 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +000093 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +000094 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +000095 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +000096
97 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +000098 # There's no point to pickling tzinfo objects on their own (they
99 # carry no data), but they need to be picklable anyway else
100 # concrete subclasses can't be pickled.
101 orig = tzinfo.__new__(tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000102 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000103 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000104 green = pickler.dumps(orig, proto)
105 derived = unpickler.loads(green)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000106 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000107
108 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000109 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000110 offset = timedelta(minutes=-300)
111 orig = PicklableFixedOffset(offset, 'cookie')
Ezio Melottie9615932010-01-24 19:26:24 +0000112 self.assertIsInstance(orig, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000113 self.assertTrue(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000114 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000115 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000116 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000117 green = pickler.dumps(orig, proto)
118 derived = unpickler.loads(green)
Ezio Melottie9615932010-01-24 19:26:24 +0000119 self.assertIsInstance(derived, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000120 self.assertTrue(type(derived) is PicklableFixedOffset)
Tim Petersf2715e02003-02-19 02:35:07 +0000121 self.assertEqual(derived.utcoffset(None), offset)
122 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000123
124#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000125# Base clase for testing a particular aspect of timedelta, time, date and
126# datetime comparisons.
127
Guido van Rossumd8faa362007-04-27 19:54:29 +0000128class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000129 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
130
131 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
132 # legit constructor.
133
134 def test_harmless_mixed_comparison(self):
135 me = self.theclass(1, 1, 1)
136
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000137 self.assertFalse(me == ())
138 self.assertTrue(me != ())
139 self.assertFalse(() == me)
140 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000141
Benjamin Peterson577473f2010-01-19 00:09:57 +0000142 self.assertIn(me, [1, 20, [], me])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000143 self.assertIn([], [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000144
145 def test_harmful_mixed_comparison(self):
146 me = self.theclass(1, 1, 1)
147
148 self.assertRaises(TypeError, lambda: me < ())
149 self.assertRaises(TypeError, lambda: me <= ())
150 self.assertRaises(TypeError, lambda: me > ())
151 self.assertRaises(TypeError, lambda: me >= ())
152
153 self.assertRaises(TypeError, lambda: () < me)
154 self.assertRaises(TypeError, lambda: () <= me)
155 self.assertRaises(TypeError, lambda: () > me)
156 self.assertRaises(TypeError, lambda: () >= me)
157
Tim Peters07534a62003-02-07 22:50:28 +0000158#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000159# timedelta tests
160
Guido van Rossumd8faa362007-04-27 19:54:29 +0000161class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000162
163 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000164
165 def test_constructor(self):
166 eq = self.assertEqual
167 td = timedelta
168
169 # Check keyword args to constructor
170 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
171 milliseconds=0, microseconds=0))
172 eq(td(1), td(days=1))
173 eq(td(0, 1), td(seconds=1))
174 eq(td(0, 0, 1), td(microseconds=1))
175 eq(td(weeks=1), td(days=7))
176 eq(td(days=1), td(hours=24))
177 eq(td(hours=1), td(minutes=60))
178 eq(td(minutes=1), td(seconds=60))
179 eq(td(seconds=1), td(milliseconds=1000))
180 eq(td(milliseconds=1), td(microseconds=1000))
181
182 # Check float args to constructor
183 eq(td(weeks=1.0/7), td(days=1))
184 eq(td(days=1.0/24), td(hours=1))
185 eq(td(hours=1.0/60), td(minutes=1))
186 eq(td(minutes=1.0/60), td(seconds=1))
187 eq(td(seconds=0.001), td(milliseconds=1))
188 eq(td(milliseconds=0.001), td(microseconds=1))
189
190 def test_computations(self):
191 eq = self.assertEqual
192 td = timedelta
193
194 a = td(7) # One week
195 b = td(0, 60) # One minute
196 c = td(0, 0, 1000) # One millisecond
197 eq(a+b+c, td(7, 60, 1000))
198 eq(a-b, td(6, 24*3600 - 60))
199 eq(-a, td(-7))
200 eq(+a, td(7))
201 eq(-b, td(-1, 24*3600 - 60))
202 eq(-c, td(-1, 24*3600 - 1, 999000))
203 eq(abs(a), a)
204 eq(abs(-a), a)
205 eq(td(6, 24*3600), a)
206 eq(td(0, 0, 60*1000000), b)
207 eq(a*10, td(70))
208 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000209 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000210 eq(b*10, td(0, 600))
211 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000212 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000213 eq(c*10, td(0, 0, 10000))
214 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000215 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000216 eq(a*-1, -a)
217 eq(b*-2, -b-b)
218 eq(c*-2, -c+-c)
219 eq(b*(60*24), (b*60)*24)
220 eq(b*(60*24), (60*b)*24)
221 eq(c*1000, td(0, 1))
222 eq(1000*c, td(0, 1))
223 eq(a//7, td(1))
224 eq(b//10, td(0, 6))
225 eq(c//1000, td(0, 0, 1))
226 eq(a//10, td(0, 7*24*360))
227 eq(a//3600000, td(0, 0, 7*24*1000))
228
229 def test_disallowed_computations(self):
230 a = timedelta(42)
231
Mark Dickinson5c2db372009-12-05 20:28:34 +0000232 # Add/sub ints or floats should be illegal
233 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000234 self.assertRaises(TypeError, lambda: a+i)
235 self.assertRaises(TypeError, lambda: a-i)
236 self.assertRaises(TypeError, lambda: i+a)
237 self.assertRaises(TypeError, lambda: i-a)
238
239 # Mul/div by float isn't supported.
240 x = 2.3
241 self.assertRaises(TypeError, lambda: a*x)
242 self.assertRaises(TypeError, lambda: x*a)
243 self.assertRaises(TypeError, lambda: a/x)
244 self.assertRaises(TypeError, lambda: x/a)
245 self.assertRaises(TypeError, lambda: a // x)
246 self.assertRaises(TypeError, lambda: x // a)
247
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000248 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000249 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000250 zero = 0
251 self.assertRaises(TypeError, lambda: zero // a)
252 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Tim Peters2a799bf2002-12-16 20:18:38 +0000253
254 def test_basic_attributes(self):
255 days, seconds, us = 1, 7, 31
256 td = timedelta(days, seconds, us)
257 self.assertEqual(td.days, days)
258 self.assertEqual(td.seconds, seconds)
259 self.assertEqual(td.microseconds, us)
260
Antoine Pitroube6859d2009-11-25 23:02:32 +0000261 def test_total_seconds(self):
262 td = timedelta(days=365)
263 self.assertEqual(td.total_seconds(), 31536000.0)
264 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
265 td = timedelta(seconds=total_seconds)
266 self.assertEqual(td.total_seconds(), total_seconds)
267
Tim Peters2a799bf2002-12-16 20:18:38 +0000268 def test_carries(self):
269 t1 = timedelta(days=100,
270 weeks=-7,
271 hours=-24*(100-49),
272 minutes=-3,
273 seconds=12,
274 microseconds=(3*60 - 12) * 1e6 + 1)
275 t2 = timedelta(microseconds=1)
276 self.assertEqual(t1, t2)
277
278 def test_hash_equality(self):
279 t1 = timedelta(days=100,
280 weeks=-7,
281 hours=-24*(100-49),
282 minutes=-3,
283 seconds=12,
284 microseconds=(3*60 - 12) * 1000000)
285 t2 = timedelta()
286 self.assertEqual(hash(t1), hash(t2))
287
288 t1 += timedelta(weeks=7)
289 t2 += timedelta(days=7*7)
290 self.assertEqual(t1, t2)
291 self.assertEqual(hash(t1), hash(t2))
292
293 d = {t1: 1}
294 d[t2] = 2
295 self.assertEqual(len(d), 1)
296 self.assertEqual(d[t1], 2)
297
298 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000299 args = 12, 34, 56
300 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000301 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000302 green = pickler.dumps(orig, proto)
303 derived = unpickler.loads(green)
304 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000305
306 def test_compare(self):
307 t1 = timedelta(2, 3, 4)
308 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000309 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000310 self.assertTrue(t1 <= t2)
311 self.assertTrue(t1 >= t2)
312 self.assertTrue(not t1 != t2)
313 self.assertTrue(not t1 < t2)
314 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000315
316 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
317 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000318 self.assertTrue(t1 < t2)
319 self.assertTrue(t2 > t1)
320 self.assertTrue(t1 <= t2)
321 self.assertTrue(t2 >= t1)
322 self.assertTrue(t1 != t2)
323 self.assertTrue(t2 != t1)
324 self.assertTrue(not t1 == t2)
325 self.assertTrue(not t2 == t1)
326 self.assertTrue(not t1 > t2)
327 self.assertTrue(not t2 < t1)
328 self.assertTrue(not t1 >= t2)
329 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000330
Tim Peters68124bb2003-02-08 03:46:31 +0000331 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000332 self.assertEqual(t1 == badarg, False)
333 self.assertEqual(t1 != badarg, True)
334 self.assertEqual(badarg == t1, False)
335 self.assertEqual(badarg != t1, True)
336
Tim Peters2a799bf2002-12-16 20:18:38 +0000337 self.assertRaises(TypeError, lambda: t1 <= badarg)
338 self.assertRaises(TypeError, lambda: t1 < badarg)
339 self.assertRaises(TypeError, lambda: t1 > badarg)
340 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000341 self.assertRaises(TypeError, lambda: badarg <= t1)
342 self.assertRaises(TypeError, lambda: badarg < t1)
343 self.assertRaises(TypeError, lambda: badarg > t1)
344 self.assertRaises(TypeError, lambda: badarg >= t1)
345
346 def test_str(self):
347 td = timedelta
348 eq = self.assertEqual
349
350 eq(str(td(1)), "1 day, 0:00:00")
351 eq(str(td(-1)), "-1 day, 0:00:00")
352 eq(str(td(2)), "2 days, 0:00:00")
353 eq(str(td(-2)), "-2 days, 0:00:00")
354
355 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
356 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
357 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
358 "-210 days, 23:12:34")
359
360 eq(str(td(milliseconds=1)), "0:00:00.001000")
361 eq(str(td(microseconds=3)), "0:00:00.000003")
362
363 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
364 microseconds=999999)),
365 "999999999 days, 23:59:59.999999")
366
367 def test_roundtrip(self):
368 for td in (timedelta(days=999999999, hours=23, minutes=59,
369 seconds=59, microseconds=999999),
370 timedelta(days=-999999999),
371 timedelta(days=1, seconds=2, microseconds=3)):
372
373 # Verify td -> string -> td identity.
374 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000375 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000376 s = s[9:]
377 td2 = eval(s)
378 self.assertEqual(td, td2)
379
380 # Verify identity via reconstructing from pieces.
381 td2 = timedelta(td.days, td.seconds, td.microseconds)
382 self.assertEqual(td, td2)
383
384 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000385 self.assertIsInstance(timedelta.min, timedelta)
386 self.assertIsInstance(timedelta.max, timedelta)
387 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000388 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000389 self.assertEqual(timedelta.min, timedelta(-999999999))
390 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
391 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
392
393 def test_overflow(self):
394 tiny = timedelta.resolution
395
396 td = timedelta.min + tiny
397 td -= tiny # no problem
398 self.assertRaises(OverflowError, td.__sub__, tiny)
399 self.assertRaises(OverflowError, td.__add__, -tiny)
400
401 td = timedelta.max - tiny
402 td += tiny # no problem
403 self.assertRaises(OverflowError, td.__add__, tiny)
404 self.assertRaises(OverflowError, td.__sub__, -tiny)
405
406 self.assertRaises(OverflowError, lambda: -timedelta.max)
407
408 def test_microsecond_rounding(self):
409 td = timedelta
410 eq = self.assertEqual
411
412 # Single-field rounding.
413 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
414 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
415 eq(td(milliseconds=0.6/1000), td(microseconds=1))
416 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
417
418 # Rounding due to contributions from more than one field.
419 us_per_hour = 3600e6
420 us_per_day = us_per_hour * 24
421 eq(td(days=.4/us_per_day), td(0))
422 eq(td(hours=.2/us_per_hour), td(0))
423 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
424
425 eq(td(days=-.4/us_per_day), td(0))
426 eq(td(hours=-.2/us_per_hour), td(0))
427 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
428
429 def test_massive_normalization(self):
430 td = timedelta(microseconds=-1)
431 self.assertEqual((td.days, td.seconds, td.microseconds),
432 (-1, 24*3600-1, 999999))
433
434 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000435 self.assertTrue(timedelta(1))
436 self.assertTrue(timedelta(0, 1))
437 self.assertTrue(timedelta(0, 0, 1))
438 self.assertTrue(timedelta(microseconds=1))
439 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000440
Tim Petersb0c854d2003-05-17 15:57:00 +0000441 def test_subclass_timedelta(self):
442
443 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000444 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000445 def from_td(td):
446 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000447
448 def as_hours(self):
449 sum = (self.days * 24 +
450 self.seconds / 3600.0 +
451 self.microseconds / 3600e6)
452 return round(sum)
453
454 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000455 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000456 self.assertEqual(t1.as_hours(), 24)
457
458 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000459 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000460 self.assertEqual(t2.as_hours(), -25)
461
462 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000463 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000464 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000465 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000466 self.assertEqual(t3.days, t4.days)
467 self.assertEqual(t3.seconds, t4.seconds)
468 self.assertEqual(t3.microseconds, t4.microseconds)
469 self.assertEqual(str(t3), str(t4))
470 self.assertEqual(t4.as_hours(), -1)
471
Tim Peters2a799bf2002-12-16 20:18:38 +0000472#############################################################################
473# date tests
474
475class TestDateOnly(unittest.TestCase):
476 # Tests here won't pass if also run on datetime objects, so don't
477 # subclass this to test datetimes too.
478
479 def test_delta_non_days_ignored(self):
480 dt = date(2000, 1, 2)
481 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
482 microseconds=5)
483 days = timedelta(delta.days)
484 self.assertEqual(days, timedelta(1))
485
486 dt2 = dt + delta
487 self.assertEqual(dt2, dt + days)
488
489 dt2 = delta + dt
490 self.assertEqual(dt2, dt + days)
491
492 dt2 = dt - delta
493 self.assertEqual(dt2, dt - days)
494
495 delta = -delta
496 days = timedelta(delta.days)
497 self.assertEqual(days, timedelta(-2))
498
499 dt2 = dt + delta
500 self.assertEqual(dt2, dt + days)
501
502 dt2 = delta + dt
503 self.assertEqual(dt2, dt + days)
504
505 dt2 = dt - delta
506 self.assertEqual(dt2, dt - days)
507
Tim Peters604c0132004-06-07 23:04:33 +0000508class SubclassDate(date):
509 sub_var = 1
510
Guido van Rossumd8faa362007-04-27 19:54:29 +0000511class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000512 # Tests here should pass for both dates and datetimes, except for a
513 # few tests that TestDateTime overrides.
514
515 theclass = date
516
517 def test_basic_attributes(self):
518 dt = self.theclass(2002, 3, 1)
519 self.assertEqual(dt.year, 2002)
520 self.assertEqual(dt.month, 3)
521 self.assertEqual(dt.day, 1)
522
523 def test_roundtrip(self):
524 for dt in (self.theclass(1, 2, 3),
525 self.theclass.today()):
526 # Verify dt -> string -> date identity.
527 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000528 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000529 s = s[9:]
530 dt2 = eval(s)
531 self.assertEqual(dt, dt2)
532
533 # Verify identity via reconstructing from pieces.
534 dt2 = self.theclass(dt.year, dt.month, dt.day)
535 self.assertEqual(dt, dt2)
536
537 def test_ordinal_conversions(self):
538 # Check some fixed values.
539 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
540 (1, 12, 31, 365),
541 (2, 1, 1, 366),
542 # first example from "Calendrical Calculations"
543 (1945, 11, 12, 710347)]:
544 d = self.theclass(y, m, d)
545 self.assertEqual(n, d.toordinal())
546 fromord = self.theclass.fromordinal(n)
547 self.assertEqual(d, fromord)
548 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000549 # if we're checking something fancier than a date, verify
550 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000551 self.assertEqual(fromord.hour, 0)
552 self.assertEqual(fromord.minute, 0)
553 self.assertEqual(fromord.second, 0)
554 self.assertEqual(fromord.microsecond, 0)
555
Tim Peters0bf60bd2003-01-08 20:40:01 +0000556 # Check first and last days of year spottily across the whole
557 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000558 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000559 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
560 d = self.theclass(year, 1, 1)
561 n = d.toordinal()
562 d2 = self.theclass.fromordinal(n)
563 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000564 # Verify that moving back a day gets to the end of year-1.
565 if year > 1:
566 d = self.theclass.fromordinal(n-1)
567 d2 = self.theclass(year-1, 12, 31)
568 self.assertEqual(d, d2)
569 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000570
571 # Test every day in a leap-year and a non-leap year.
572 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
573 for year, isleap in (2000, True), (2002, False):
574 n = self.theclass(year, 1, 1).toordinal()
575 for month, maxday in zip(range(1, 13), dim):
576 if month == 2 and isleap:
577 maxday += 1
578 for day in range(1, maxday+1):
579 d = self.theclass(year, month, day)
580 self.assertEqual(d.toordinal(), n)
581 self.assertEqual(d, self.theclass.fromordinal(n))
582 n += 1
583
584 def test_extreme_ordinals(self):
585 a = self.theclass.min
586 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
587 aord = a.toordinal()
588 b = a.fromordinal(aord)
589 self.assertEqual(a, b)
590
591 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
592
593 b = a + timedelta(days=1)
594 self.assertEqual(b.toordinal(), aord + 1)
595 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
596
597 a = self.theclass.max
598 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
599 aord = a.toordinal()
600 b = a.fromordinal(aord)
601 self.assertEqual(a, b)
602
603 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
604
605 b = a - timedelta(days=1)
606 self.assertEqual(b.toordinal(), aord - 1)
607 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
608
609 def test_bad_constructor_arguments(self):
610 # bad years
611 self.theclass(MINYEAR, 1, 1) # no exception
612 self.theclass(MAXYEAR, 1, 1) # no exception
613 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
614 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
615 # bad months
616 self.theclass(2000, 1, 1) # no exception
617 self.theclass(2000, 12, 1) # no exception
618 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
619 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
620 # bad days
621 self.theclass(2000, 2, 29) # no exception
622 self.theclass(2004, 2, 29) # no exception
623 self.theclass(2400, 2, 29) # no exception
624 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
625 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
626 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
627 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
628 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
629 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
630
631 def test_hash_equality(self):
632 d = self.theclass(2000, 12, 31)
633 # same thing
634 e = self.theclass(2000, 12, 31)
635 self.assertEqual(d, e)
636 self.assertEqual(hash(d), hash(e))
637
638 dic = {d: 1}
639 dic[e] = 2
640 self.assertEqual(len(dic), 1)
641 self.assertEqual(dic[d], 2)
642 self.assertEqual(dic[e], 2)
643
644 d = self.theclass(2001, 1, 1)
645 # same thing
646 e = self.theclass(2001, 1, 1)
647 self.assertEqual(d, e)
648 self.assertEqual(hash(d), hash(e))
649
650 dic = {d: 1}
651 dic[e] = 2
652 self.assertEqual(len(dic), 1)
653 self.assertEqual(dic[d], 2)
654 self.assertEqual(dic[e], 2)
655
656 def test_computations(self):
657 a = self.theclass(2002, 1, 31)
658 b = self.theclass(1956, 1, 31)
659
660 diff = a-b
661 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
662 self.assertEqual(diff.seconds, 0)
663 self.assertEqual(diff.microseconds, 0)
664
665 day = timedelta(1)
666 week = timedelta(7)
667 a = self.theclass(2002, 3, 2)
668 self.assertEqual(a + day, self.theclass(2002, 3, 3))
669 self.assertEqual(day + a, self.theclass(2002, 3, 3))
670 self.assertEqual(a - day, self.theclass(2002, 3, 1))
671 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
672 self.assertEqual(a + week, self.theclass(2002, 3, 9))
673 self.assertEqual(a - week, self.theclass(2002, 2, 23))
674 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
675 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
676 self.assertEqual((a + week) - a, week)
677 self.assertEqual((a + day) - a, day)
678 self.assertEqual((a - week) - a, -week)
679 self.assertEqual((a - day) - a, -day)
680 self.assertEqual(a - (a + week), -week)
681 self.assertEqual(a - (a + day), -day)
682 self.assertEqual(a - (a - week), week)
683 self.assertEqual(a - (a - day), day)
684
Mark Dickinson5c2db372009-12-05 20:28:34 +0000685 # Add/sub ints or floats should be illegal
686 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000687 self.assertRaises(TypeError, lambda: a+i)
688 self.assertRaises(TypeError, lambda: a-i)
689 self.assertRaises(TypeError, lambda: i+a)
690 self.assertRaises(TypeError, lambda: i-a)
691
692 # delta - date is senseless.
693 self.assertRaises(TypeError, lambda: day - a)
694 # mixing date and (delta or date) via * or // is senseless
695 self.assertRaises(TypeError, lambda: day * a)
696 self.assertRaises(TypeError, lambda: a * day)
697 self.assertRaises(TypeError, lambda: day // a)
698 self.assertRaises(TypeError, lambda: a // day)
699 self.assertRaises(TypeError, lambda: a * a)
700 self.assertRaises(TypeError, lambda: a // a)
701 # date + date is senseless
702 self.assertRaises(TypeError, lambda: a + a)
703
704 def test_overflow(self):
705 tiny = self.theclass.resolution
706
707 dt = self.theclass.min + tiny
708 dt -= tiny # no problem
709 self.assertRaises(OverflowError, dt.__sub__, tiny)
710 self.assertRaises(OverflowError, dt.__add__, -tiny)
711
712 dt = self.theclass.max - tiny
713 dt += tiny # no problem
714 self.assertRaises(OverflowError, dt.__add__, tiny)
715 self.assertRaises(OverflowError, dt.__sub__, -tiny)
716
717 def test_fromtimestamp(self):
718 import time
719
720 # Try an arbitrary fixed value.
721 year, month, day = 1999, 9, 19
722 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
723 d = self.theclass.fromtimestamp(ts)
724 self.assertEqual(d.year, year)
725 self.assertEqual(d.month, month)
726 self.assertEqual(d.day, day)
727
Tim Peters1b6f7a92004-06-20 02:50:16 +0000728 def test_insane_fromtimestamp(self):
729 # It's possible that some platform maps time_t to double,
730 # and that this test will fail there. This test should
731 # exempt such platforms (provided they return reasonable
732 # results!).
733 for insane in -1e200, 1e200:
734 self.assertRaises(ValueError, self.theclass.fromtimestamp,
735 insane)
736
Tim Peters2a799bf2002-12-16 20:18:38 +0000737 def test_today(self):
738 import time
739
740 # We claim that today() is like fromtimestamp(time.time()), so
741 # prove it.
742 for dummy in range(3):
743 today = self.theclass.today()
744 ts = time.time()
745 todayagain = self.theclass.fromtimestamp(ts)
746 if today == todayagain:
747 break
748 # There are several legit reasons that could fail:
749 # 1. It recently became midnight, between the today() and the
750 # time() calls.
751 # 2. The platform time() has such fine resolution that we'll
752 # never get the same value twice.
753 # 3. The platform time() has poor resolution, and we just
754 # happened to call today() right before a resolution quantum
755 # boundary.
756 # 4. The system clock got fiddled between calls.
757 # In any case, wait a little while and try again.
758 time.sleep(0.1)
759
760 # It worked or it didn't. If it didn't, assume it's reason #2, and
761 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000762 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000763 abs(todayagain - today) < timedelta(seconds=0.5))
764
765 def test_weekday(self):
766 for i in range(7):
767 # March 4, 2002 is a Monday
768 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
769 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
770 # January 2, 1956 is a Monday
771 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
772 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
773
774 def test_isocalendar(self):
775 # Check examples from
776 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
777 for i in range(7):
778 d = self.theclass(2003, 12, 22+i)
779 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
780 d = self.theclass(2003, 12, 29) + timedelta(i)
781 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
782 d = self.theclass(2004, 1, 5+i)
783 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
784 d = self.theclass(2009, 12, 21+i)
785 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
786 d = self.theclass(2009, 12, 28) + timedelta(i)
787 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
788 d = self.theclass(2010, 1, 4+i)
789 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
790
791 def test_iso_long_years(self):
792 # Calculate long ISO years and compare to table from
793 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
794 ISO_LONG_YEARS_TABLE = """
795 4 32 60 88
796 9 37 65 93
797 15 43 71 99
798 20 48 76
799 26 54 82
800
801 105 133 161 189
802 111 139 167 195
803 116 144 172
804 122 150 178
805 128 156 184
806
807 201 229 257 285
808 207 235 263 291
809 212 240 268 296
810 218 246 274
811 224 252 280
812
813 303 331 359 387
814 308 336 364 392
815 314 342 370 398
816 320 348 376
817 325 353 381
818 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000819 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000820 L = []
821 for i in range(400):
822 d = self.theclass(2000+i, 12, 31)
823 d1 = self.theclass(1600+i, 12, 31)
824 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
825 if d.isocalendar()[1] == 53:
826 L.append(i)
827 self.assertEqual(L, iso_long_years)
828
829 def test_isoformat(self):
830 t = self.theclass(2, 3, 2)
831 self.assertEqual(t.isoformat(), "0002-03-02")
832
833 def test_ctime(self):
834 t = self.theclass(2002, 3, 2)
835 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
836
837 def test_strftime(self):
838 t = self.theclass(2005, 3, 2)
839 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000840 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000841 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000842
843 self.assertRaises(TypeError, t.strftime) # needs an arg
844 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
845 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
846
Georg Brandlf78e02b2008-06-10 17:40:04 +0000847 # test that unicode input is allowed (issue 2782)
848 self.assertEqual(t.strftime("%m"), "03")
849
Tim Peters2a799bf2002-12-16 20:18:38 +0000850 # A naive object replaces %z and %Z w/ empty strings.
851 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
852
Benjamin Petersone1cdfd72009-01-18 21:02:37 +0000853 #make sure that invalid format specifiers are handled correctly
854 #self.assertRaises(ValueError, t.strftime, "%e")
855 #self.assertRaises(ValueError, t.strftime, "%")
856 #self.assertRaises(ValueError, t.strftime, "%#")
857
858 #oh well, some systems just ignore those invalid ones.
859 #at least, excercise them to make sure that no crashes
860 #are generated
861 for f in ["%e", "%", "%#"]:
862 try:
863 t.strftime(f)
864 except ValueError:
865 pass
866
867 #check that this standard extension works
868 t.strftime("%f")
869
Georg Brandlf78e02b2008-06-10 17:40:04 +0000870
Eric Smith1ba31142007-09-11 18:06:02 +0000871 def test_format(self):
872 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000873 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000874
875 # check that a derived class's __str__() gets called
876 class A(self.theclass):
877 def __str__(self):
878 return 'A'
879 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000880 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000881
882 # check that a derived class's strftime gets called
883 class B(self.theclass):
884 def strftime(self, format_spec):
885 return 'B'
886 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000887 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000888
889 for fmt in ["m:%m d:%d y:%y",
890 "m:%m d:%d y:%y H:%H M:%M S:%S",
891 "%z %Z",
892 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +0000893 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
894 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
895 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +0000896
Tim Peters2a799bf2002-12-16 20:18:38 +0000897 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000898 self.assertIsInstance(self.theclass.min, self.theclass)
899 self.assertIsInstance(self.theclass.max, self.theclass)
900 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000901 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000902
903 def test_extreme_timedelta(self):
904 big = self.theclass.max - self.theclass.min
905 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
906 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
907 # n == 315537897599999999 ~= 2**58.13
908 justasbig = timedelta(0, 0, n)
909 self.assertEqual(big, justasbig)
910 self.assertEqual(self.theclass.min + big, self.theclass.max)
911 self.assertEqual(self.theclass.max - big, self.theclass.min)
912
913 def test_timetuple(self):
914 for i in range(7):
915 # January 2, 1956 is a Monday (0)
916 d = self.theclass(1956, 1, 2+i)
917 t = d.timetuple()
918 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
919 # February 1, 1956 is a Wednesday (2)
920 d = self.theclass(1956, 2, 1+i)
921 t = d.timetuple()
922 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
923 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
924 # of the year.
925 d = self.theclass(1956, 3, 1+i)
926 t = d.timetuple()
927 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
928 self.assertEqual(t.tm_year, 1956)
929 self.assertEqual(t.tm_mon, 3)
930 self.assertEqual(t.tm_mday, 1+i)
931 self.assertEqual(t.tm_hour, 0)
932 self.assertEqual(t.tm_min, 0)
933 self.assertEqual(t.tm_sec, 0)
934 self.assertEqual(t.tm_wday, (3+i)%7)
935 self.assertEqual(t.tm_yday, 61+i)
936 self.assertEqual(t.tm_isdst, -1)
937
938 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000939 args = 6, 7, 23
940 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000941 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000942 green = pickler.dumps(orig, proto)
943 derived = unpickler.loads(green)
944 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000945
946 def test_compare(self):
947 t1 = self.theclass(2, 3, 4)
948 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000949 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000950 self.assertTrue(t1 <= t2)
951 self.assertTrue(t1 >= t2)
952 self.assertTrue(not t1 != t2)
953 self.assertTrue(not t1 < t2)
954 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000955
956 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
957 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000958 self.assertTrue(t1 < t2)
959 self.assertTrue(t2 > t1)
960 self.assertTrue(t1 <= t2)
961 self.assertTrue(t2 >= t1)
962 self.assertTrue(t1 != t2)
963 self.assertTrue(t2 != t1)
964 self.assertTrue(not t1 == t2)
965 self.assertTrue(not t2 == t1)
966 self.assertTrue(not t1 > t2)
967 self.assertTrue(not t2 < t1)
968 self.assertTrue(not t1 >= t2)
969 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000970
Tim Peters68124bb2003-02-08 03:46:31 +0000971 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000972 self.assertEqual(t1 == badarg, False)
973 self.assertEqual(t1 != badarg, True)
974 self.assertEqual(badarg == t1, False)
975 self.assertEqual(badarg != t1, True)
976
Tim Peters2a799bf2002-12-16 20:18:38 +0000977 self.assertRaises(TypeError, lambda: t1 < badarg)
978 self.assertRaises(TypeError, lambda: t1 > badarg)
979 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000980 self.assertRaises(TypeError, lambda: badarg <= t1)
981 self.assertRaises(TypeError, lambda: badarg < t1)
982 self.assertRaises(TypeError, lambda: badarg > t1)
983 self.assertRaises(TypeError, lambda: badarg >= t1)
984
Tim Peters8d81a012003-01-24 22:36:34 +0000985 def test_mixed_compare(self):
986 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000987
988 # Our class can be compared for equality to other classes
989 self.assertEqual(our == 1, False)
990 self.assertEqual(1 == our, False)
991 self.assertEqual(our != 1, True)
992 self.assertEqual(1 != our, True)
993
994 # But the ordering is undefined
995 self.assertRaises(TypeError, lambda: our < 1)
996 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000997
Guido van Rossum19960592006-08-24 17:29:38 +0000998 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +0000999
Guido van Rossum19960592006-08-24 17:29:38 +00001000 class SomeClass:
1001 pass
1002
1003 their = SomeClass()
1004 self.assertEqual(our == their, False)
1005 self.assertEqual(their == our, False)
1006 self.assertEqual(our != their, True)
1007 self.assertEqual(their != our, True)
1008 self.assertRaises(TypeError, lambda: our < their)
1009 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001010
Guido van Rossum19960592006-08-24 17:29:38 +00001011 # However, if the other class explicitly defines ordering
1012 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001013
Guido van Rossum19960592006-08-24 17:29:38 +00001014 class LargerThanAnything:
1015 def __lt__(self, other):
1016 return False
1017 def __le__(self, other):
1018 return isinstance(other, LargerThanAnything)
1019 def __eq__(self, other):
1020 return isinstance(other, LargerThanAnything)
1021 def __ne__(self, other):
1022 return not isinstance(other, LargerThanAnything)
1023 def __gt__(self, other):
1024 return not isinstance(other, LargerThanAnything)
1025 def __ge__(self, other):
1026 return True
1027
1028 their = LargerThanAnything()
1029 self.assertEqual(our == their, False)
1030 self.assertEqual(their == our, False)
1031 self.assertEqual(our != their, True)
1032 self.assertEqual(their != our, True)
1033 self.assertEqual(our < their, True)
1034 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001035
Tim Peters2a799bf2002-12-16 20:18:38 +00001036 def test_bool(self):
1037 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001038 self.assertTrue(self.theclass.min)
1039 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001040
Guido van Rossum04110fb2007-08-24 16:32:05 +00001041 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001042 # For nasty technical reasons, we can't handle years before 1900.
1043 cls = self.theclass
1044 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1045 for y in 1, 49, 51, 99, 100, 1000, 1899:
1046 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001047
1048 def test_replace(self):
1049 cls = self.theclass
1050 args = [1, 2, 3]
1051 base = cls(*args)
1052 self.assertEqual(base, base.replace())
1053
1054 i = 0
1055 for name, newval in (("year", 2),
1056 ("month", 3),
1057 ("day", 4)):
1058 newargs = args[:]
1059 newargs[i] = newval
1060 expected = cls(*newargs)
1061 got = base.replace(**{name: newval})
1062 self.assertEqual(expected, got)
1063 i += 1
1064
1065 # Out of bounds.
1066 base = cls(2000, 2, 29)
1067 self.assertRaises(ValueError, base.replace, year=2001)
1068
Tim Petersa98924a2003-05-17 05:55:19 +00001069 def test_subclass_date(self):
1070
1071 class C(self.theclass):
1072 theAnswer = 42
1073
1074 def __new__(cls, *args, **kws):
1075 temp = kws.copy()
1076 extra = temp.pop('extra')
1077 result = self.theclass.__new__(cls, *args, **temp)
1078 result.extra = extra
1079 return result
1080
1081 def newmeth(self, start):
1082 return start + self.year + self.month
1083
1084 args = 2003, 4, 14
1085
1086 dt1 = self.theclass(*args)
1087 dt2 = C(*args, **{'extra': 7})
1088
1089 self.assertEqual(dt2.__class__, C)
1090 self.assertEqual(dt2.theAnswer, 42)
1091 self.assertEqual(dt2.extra, 7)
1092 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1093 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1094
Tim Peters604c0132004-06-07 23:04:33 +00001095 def test_pickling_subclass_date(self):
1096
1097 args = 6, 7, 23
1098 orig = SubclassDate(*args)
1099 for pickler, unpickler, proto in pickle_choices:
1100 green = pickler.dumps(orig, proto)
1101 derived = unpickler.loads(green)
1102 self.assertEqual(orig, derived)
1103
Tim Peters3f606292004-03-21 23:38:41 +00001104 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001105 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001106 # This is a low-overhead backdoor. A user can (by intent or
1107 # mistake) pass a string directly, which (if it's the right length)
1108 # will get treated like a pickle, and bypass the normal sanity
1109 # checks in the constructor. This can create insane objects.
1110 # The constructor doesn't want to burn the time to validate all
1111 # fields, but does check the month field. This stops, e.g.,
1112 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001113 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001114 if not issubclass(self.theclass, datetime):
1115 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001116 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001117 self.assertRaises(TypeError, self.theclass,
1118 base[:2] + month_byte + base[3:])
1119 for ord_byte in range(1, 13):
1120 # This shouldn't blow up because of the month byte alone. If
1121 # the implementation changes to do more-careful checking, it may
1122 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001123 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001124
Tim Peters2a799bf2002-12-16 20:18:38 +00001125#############################################################################
1126# datetime tests
1127
Tim Peters604c0132004-06-07 23:04:33 +00001128class SubclassDatetime(datetime):
1129 sub_var = 1
1130
Tim Peters2a799bf2002-12-16 20:18:38 +00001131class TestDateTime(TestDate):
1132
1133 theclass = datetime
1134
1135 def test_basic_attributes(self):
1136 dt = self.theclass(2002, 3, 1, 12, 0)
1137 self.assertEqual(dt.year, 2002)
1138 self.assertEqual(dt.month, 3)
1139 self.assertEqual(dt.day, 1)
1140 self.assertEqual(dt.hour, 12)
1141 self.assertEqual(dt.minute, 0)
1142 self.assertEqual(dt.second, 0)
1143 self.assertEqual(dt.microsecond, 0)
1144
1145 def test_basic_attributes_nonzero(self):
1146 # Make sure all attributes are non-zero so bugs in
1147 # bit-shifting access show up.
1148 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1149 self.assertEqual(dt.year, 2002)
1150 self.assertEqual(dt.month, 3)
1151 self.assertEqual(dt.day, 1)
1152 self.assertEqual(dt.hour, 12)
1153 self.assertEqual(dt.minute, 59)
1154 self.assertEqual(dt.second, 59)
1155 self.assertEqual(dt.microsecond, 8000)
1156
1157 def test_roundtrip(self):
1158 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1159 self.theclass.now()):
1160 # Verify dt -> string -> datetime identity.
1161 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001162 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001163 s = s[9:]
1164 dt2 = eval(s)
1165 self.assertEqual(dt, dt2)
1166
1167 # Verify identity via reconstructing from pieces.
1168 dt2 = self.theclass(dt.year, dt.month, dt.day,
1169 dt.hour, dt.minute, dt.second,
1170 dt.microsecond)
1171 self.assertEqual(dt, dt2)
1172
1173 def test_isoformat(self):
1174 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1175 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1176 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1177 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001178 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001179 # str is ISO format with the separator forced to a blank.
1180 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1181
1182 t = self.theclass(2, 3, 2)
1183 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1184 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1185 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1186 # str is ISO format with the separator forced to a blank.
1187 self.assertEqual(str(t), "0002-03-02 00:00:00")
1188
Eric Smith1ba31142007-09-11 18:06:02 +00001189 def test_format(self):
1190 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001191 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001192
1193 # check that a derived class's __str__() gets called
1194 class A(self.theclass):
1195 def __str__(self):
1196 return 'A'
1197 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001198 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001199
1200 # check that a derived class's strftime gets called
1201 class B(self.theclass):
1202 def strftime(self, format_spec):
1203 return 'B'
1204 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001205 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001206
1207 for fmt in ["m:%m d:%d y:%y",
1208 "m:%m d:%d y:%y H:%H M:%M S:%S",
1209 "%z %Z",
1210 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001211 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1212 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1213 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001214
Tim Peters2a799bf2002-12-16 20:18:38 +00001215 def test_more_ctime(self):
1216 # Test fields that TestDate doesn't touch.
1217 import time
1218
1219 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1220 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1221 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1222 # out. The difference is that t.ctime() produces " 2" for the day,
1223 # but platform ctime() produces "02" for the day. According to
1224 # C99, t.ctime() is correct here.
1225 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1226
1227 # So test a case where that difference doesn't matter.
1228 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1229 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1230
1231 def test_tz_independent_comparing(self):
1232 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1233 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1234 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1235 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001236 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001237
1238 # Make sure comparison doesn't forget microseconds, and isn't done
1239 # via comparing a float timestamp (an IEEE double doesn't have enough
1240 # precision to span microsecond resolution across years 1 thru 9999,
1241 # so comparing via timestamp necessarily calls some distinct values
1242 # equal).
1243 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1244 us = timedelta(microseconds=1)
1245 dt2 = dt1 + us
1246 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001247 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001248
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001249 def test_strftime_with_bad_tzname_replace(self):
1250 # verify ok if tzinfo.tzname().replace() returns a non-string
1251 class MyTzInfo(FixedOffset):
1252 def tzname(self, dt):
1253 class MyStr(str):
1254 def replace(self, *args):
1255 return None
1256 return MyStr('name')
1257 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1258 self.assertRaises(TypeError, t.strftime, '%Z')
1259
Tim Peters2a799bf2002-12-16 20:18:38 +00001260 def test_bad_constructor_arguments(self):
1261 # bad years
1262 self.theclass(MINYEAR, 1, 1) # no exception
1263 self.theclass(MAXYEAR, 1, 1) # no exception
1264 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1265 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1266 # bad months
1267 self.theclass(2000, 1, 1) # no exception
1268 self.theclass(2000, 12, 1) # no exception
1269 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1270 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1271 # bad days
1272 self.theclass(2000, 2, 29) # no exception
1273 self.theclass(2004, 2, 29) # no exception
1274 self.theclass(2400, 2, 29) # no exception
1275 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1276 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1277 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1278 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1279 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1280 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1281 # bad hours
1282 self.theclass(2000, 1, 31, 0) # no exception
1283 self.theclass(2000, 1, 31, 23) # no exception
1284 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1285 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1286 # bad minutes
1287 self.theclass(2000, 1, 31, 23, 0) # no exception
1288 self.theclass(2000, 1, 31, 23, 59) # no exception
1289 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1290 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1291 # bad seconds
1292 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1293 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1294 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1295 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1296 # bad microseconds
1297 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1298 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1299 self.assertRaises(ValueError, self.theclass,
1300 2000, 1, 31, 23, 59, 59, -1)
1301 self.assertRaises(ValueError, self.theclass,
1302 2000, 1, 31, 23, 59, 59,
1303 1000000)
1304
1305 def test_hash_equality(self):
1306 d = self.theclass(2000, 12, 31, 23, 30, 17)
1307 e = self.theclass(2000, 12, 31, 23, 30, 17)
1308 self.assertEqual(d, e)
1309 self.assertEqual(hash(d), hash(e))
1310
1311 dic = {d: 1}
1312 dic[e] = 2
1313 self.assertEqual(len(dic), 1)
1314 self.assertEqual(dic[d], 2)
1315 self.assertEqual(dic[e], 2)
1316
1317 d = self.theclass(2001, 1, 1, 0, 5, 17)
1318 e = self.theclass(2001, 1, 1, 0, 5, 17)
1319 self.assertEqual(d, e)
1320 self.assertEqual(hash(d), hash(e))
1321
1322 dic = {d: 1}
1323 dic[e] = 2
1324 self.assertEqual(len(dic), 1)
1325 self.assertEqual(dic[d], 2)
1326 self.assertEqual(dic[e], 2)
1327
1328 def test_computations(self):
1329 a = self.theclass(2002, 1, 31)
1330 b = self.theclass(1956, 1, 31)
1331 diff = a-b
1332 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1333 self.assertEqual(diff.seconds, 0)
1334 self.assertEqual(diff.microseconds, 0)
1335 a = self.theclass(2002, 3, 2, 17, 6)
1336 millisec = timedelta(0, 0, 1000)
1337 hour = timedelta(0, 3600)
1338 day = timedelta(1)
1339 week = timedelta(7)
1340 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1341 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1342 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1343 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1344 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1345 self.assertEqual(a - hour, a + -hour)
1346 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1347 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1348 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1349 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1350 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1351 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1352 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1353 self.assertEqual((a + week) - a, week)
1354 self.assertEqual((a + day) - a, day)
1355 self.assertEqual((a + hour) - a, hour)
1356 self.assertEqual((a + millisec) - a, millisec)
1357 self.assertEqual((a - week) - a, -week)
1358 self.assertEqual((a - day) - a, -day)
1359 self.assertEqual((a - hour) - a, -hour)
1360 self.assertEqual((a - millisec) - a, -millisec)
1361 self.assertEqual(a - (a + week), -week)
1362 self.assertEqual(a - (a + day), -day)
1363 self.assertEqual(a - (a + hour), -hour)
1364 self.assertEqual(a - (a + millisec), -millisec)
1365 self.assertEqual(a - (a - week), week)
1366 self.assertEqual(a - (a - day), day)
1367 self.assertEqual(a - (a - hour), hour)
1368 self.assertEqual(a - (a - millisec), millisec)
1369 self.assertEqual(a + (week + day + hour + millisec),
1370 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1371 self.assertEqual(a + (week + day + hour + millisec),
1372 (((a + week) + day) + hour) + millisec)
1373 self.assertEqual(a - (week + day + hour + millisec),
1374 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1375 self.assertEqual(a - (week + day + hour + millisec),
1376 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001377 # Add/sub ints or floats should be illegal
1378 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001379 self.assertRaises(TypeError, lambda: a+i)
1380 self.assertRaises(TypeError, lambda: a-i)
1381 self.assertRaises(TypeError, lambda: i+a)
1382 self.assertRaises(TypeError, lambda: i-a)
1383
1384 # delta - datetime is senseless.
1385 self.assertRaises(TypeError, lambda: day - a)
1386 # mixing datetime and (delta or datetime) via * or // is senseless
1387 self.assertRaises(TypeError, lambda: day * a)
1388 self.assertRaises(TypeError, lambda: a * day)
1389 self.assertRaises(TypeError, lambda: day // a)
1390 self.assertRaises(TypeError, lambda: a // day)
1391 self.assertRaises(TypeError, lambda: a * a)
1392 self.assertRaises(TypeError, lambda: a // a)
1393 # datetime + datetime is senseless
1394 self.assertRaises(TypeError, lambda: a + a)
1395
1396 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001397 args = 6, 7, 23, 20, 59, 1, 64**2
1398 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001399 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001400 green = pickler.dumps(orig, proto)
1401 derived = unpickler.loads(green)
1402 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001403
Guido van Rossum275666f2003-02-07 21:49:01 +00001404 def test_more_pickling(self):
1405 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1406 s = pickle.dumps(a)
1407 b = pickle.loads(s)
1408 self.assertEqual(b.year, 2003)
1409 self.assertEqual(b.month, 2)
1410 self.assertEqual(b.day, 7)
1411
Tim Peters604c0132004-06-07 23:04:33 +00001412 def test_pickling_subclass_datetime(self):
1413 args = 6, 7, 23, 20, 59, 1, 64**2
1414 orig = SubclassDatetime(*args)
1415 for pickler, unpickler, proto in pickle_choices:
1416 green = pickler.dumps(orig, proto)
1417 derived = unpickler.loads(green)
1418 self.assertEqual(orig, derived)
1419
Tim Peters2a799bf2002-12-16 20:18:38 +00001420 def test_more_compare(self):
1421 # The test_compare() inherited from TestDate covers the error cases.
1422 # We just want to test lexicographic ordering on the members datetime
1423 # has that date lacks.
1424 args = [2000, 11, 29, 20, 58, 16, 999998]
1425 t1 = self.theclass(*args)
1426 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001427 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001428 self.assertTrue(t1 <= t2)
1429 self.assertTrue(t1 >= t2)
1430 self.assertTrue(not t1 != t2)
1431 self.assertTrue(not t1 < t2)
1432 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001433
1434 for i in range(len(args)):
1435 newargs = args[:]
1436 newargs[i] = args[i] + 1
1437 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001438 self.assertTrue(t1 < t2)
1439 self.assertTrue(t2 > t1)
1440 self.assertTrue(t1 <= t2)
1441 self.assertTrue(t2 >= t1)
1442 self.assertTrue(t1 != t2)
1443 self.assertTrue(t2 != t1)
1444 self.assertTrue(not t1 == t2)
1445 self.assertTrue(not t2 == t1)
1446 self.assertTrue(not t1 > t2)
1447 self.assertTrue(not t2 < t1)
1448 self.assertTrue(not t1 >= t2)
1449 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001450
1451
1452 # A helper for timestamp constructor tests.
1453 def verify_field_equality(self, expected, got):
1454 self.assertEqual(expected.tm_year, got.year)
1455 self.assertEqual(expected.tm_mon, got.month)
1456 self.assertEqual(expected.tm_mday, got.day)
1457 self.assertEqual(expected.tm_hour, got.hour)
1458 self.assertEqual(expected.tm_min, got.minute)
1459 self.assertEqual(expected.tm_sec, got.second)
1460
1461 def test_fromtimestamp(self):
1462 import time
1463
1464 ts = time.time()
1465 expected = time.localtime(ts)
1466 got = self.theclass.fromtimestamp(ts)
1467 self.verify_field_equality(expected, got)
1468
1469 def test_utcfromtimestamp(self):
1470 import time
1471
1472 ts = time.time()
1473 expected = time.gmtime(ts)
1474 got = self.theclass.utcfromtimestamp(ts)
1475 self.verify_field_equality(expected, got)
1476
Thomas Wouters477c8d52006-05-27 19:21:47 +00001477 def test_microsecond_rounding(self):
1478 # Test whether fromtimestamp "rounds up" floats that are less
1479 # than one microsecond smaller than an integer.
1480 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1481 self.theclass.fromtimestamp(1))
1482
Tim Peters1b6f7a92004-06-20 02:50:16 +00001483 def test_insane_fromtimestamp(self):
1484 # It's possible that some platform maps time_t to double,
1485 # and that this test will fail there. This test should
1486 # exempt such platforms (provided they return reasonable
1487 # results!).
1488 for insane in -1e200, 1e200:
1489 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1490 insane)
1491
1492 def test_insane_utcfromtimestamp(self):
1493 # It's possible that some platform maps time_t to double,
1494 # and that this test will fail there. This test should
1495 # exempt such platforms (provided they return reasonable
1496 # results!).
1497 for insane in -1e200, 1e200:
1498 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1499 insane)
1500
Guido van Rossumd8faa362007-04-27 19:54:29 +00001501 def test_negative_float_fromtimestamp(self):
1502 # Windows doesn't accept negative timestamps
1503 if os.name == "nt":
1504 return
1505 # The result is tz-dependent; at least test that this doesn't
1506 # fail (like it did before bug 1646728 was fixed).
1507 self.theclass.fromtimestamp(-1.05)
1508
1509 def test_negative_float_utcfromtimestamp(self):
1510 # Windows doesn't accept negative timestamps
1511 if os.name == "nt":
1512 return
1513 d = self.theclass.utcfromtimestamp(-1.05)
1514 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1515
Tim Peters2a799bf2002-12-16 20:18:38 +00001516 def test_utcnow(self):
1517 import time
1518
1519 # Call it a success if utcnow() and utcfromtimestamp() are within
1520 # a second of each other.
1521 tolerance = timedelta(seconds=1)
1522 for dummy in range(3):
1523 from_now = self.theclass.utcnow()
1524 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1525 if abs(from_timestamp - from_now) <= tolerance:
1526 break
1527 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001528 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001529
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001530 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001531 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001532
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001533 string = '2004-12-01 13:02:47.197'
1534 format = '%Y-%m-%d %H:%M:%S.%f'
1535 result, frac = _strptime._strptime(string, format)
1536 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001537 got = self.theclass.strptime(string, format)
1538 self.assertEqual(expected, got)
1539
Tim Peters2a799bf2002-12-16 20:18:38 +00001540 def test_more_timetuple(self):
1541 # This tests fields beyond those tested by the TestDate.test_timetuple.
1542 t = self.theclass(2004, 12, 31, 6, 22, 33)
1543 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1544 self.assertEqual(t.timetuple(),
1545 (t.year, t.month, t.day,
1546 t.hour, t.minute, t.second,
1547 t.weekday(),
1548 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1549 -1))
1550 tt = t.timetuple()
1551 self.assertEqual(tt.tm_year, t.year)
1552 self.assertEqual(tt.tm_mon, t.month)
1553 self.assertEqual(tt.tm_mday, t.day)
1554 self.assertEqual(tt.tm_hour, t.hour)
1555 self.assertEqual(tt.tm_min, t.minute)
1556 self.assertEqual(tt.tm_sec, t.second)
1557 self.assertEqual(tt.tm_wday, t.weekday())
1558 self.assertEqual(tt.tm_yday, t.toordinal() -
1559 date(t.year, 1, 1).toordinal() + 1)
1560 self.assertEqual(tt.tm_isdst, -1)
1561
1562 def test_more_strftime(self):
1563 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001564 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1565 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1566 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001567
1568 def test_extract(self):
1569 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1570 self.assertEqual(dt.date(), date(2002, 3, 4))
1571 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1572
1573 def test_combine(self):
1574 d = date(2002, 3, 4)
1575 t = time(18, 45, 3, 1234)
1576 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1577 combine = self.theclass.combine
1578 dt = combine(d, t)
1579 self.assertEqual(dt, expected)
1580
1581 dt = combine(time=t, date=d)
1582 self.assertEqual(dt, expected)
1583
1584 self.assertEqual(d, dt.date())
1585 self.assertEqual(t, dt.time())
1586 self.assertEqual(dt, combine(dt.date(), dt.time()))
1587
1588 self.assertRaises(TypeError, combine) # need an arg
1589 self.assertRaises(TypeError, combine, d) # need two args
1590 self.assertRaises(TypeError, combine, t, d) # args reversed
1591 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1592 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1593
Tim Peters12bf3392002-12-24 05:41:27 +00001594 def test_replace(self):
1595 cls = self.theclass
1596 args = [1, 2, 3, 4, 5, 6, 7]
1597 base = cls(*args)
1598 self.assertEqual(base, base.replace())
1599
1600 i = 0
1601 for name, newval in (("year", 2),
1602 ("month", 3),
1603 ("day", 4),
1604 ("hour", 5),
1605 ("minute", 6),
1606 ("second", 7),
1607 ("microsecond", 8)):
1608 newargs = args[:]
1609 newargs[i] = newval
1610 expected = cls(*newargs)
1611 got = base.replace(**{name: newval})
1612 self.assertEqual(expected, got)
1613 i += 1
1614
1615 # Out of bounds.
1616 base = cls(2000, 2, 29)
1617 self.assertRaises(ValueError, base.replace, year=2001)
1618
Tim Peters80475bb2002-12-25 07:40:55 +00001619 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001620 # Pretty boring! The TZ test is more interesting here. astimezone()
1621 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001622 dt = self.theclass.now()
1623 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001624 self.assertRaises(TypeError, dt.astimezone) # not enough args
1625 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1626 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001627 self.assertRaises(ValueError, dt.astimezone, f) # naive
1628 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001629
Tim Peters52dcce22003-01-23 16:36:11 +00001630 class Bogus(tzinfo):
1631 def utcoffset(self, dt): return None
1632 def dst(self, dt): return timedelta(0)
1633 bog = Bogus()
1634 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1635
1636 class AlsoBogus(tzinfo):
1637 def utcoffset(self, dt): return timedelta(0)
1638 def dst(self, dt): return None
1639 alsobog = AlsoBogus()
1640 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001641
Tim Petersa98924a2003-05-17 05:55:19 +00001642 def test_subclass_datetime(self):
1643
1644 class C(self.theclass):
1645 theAnswer = 42
1646
1647 def __new__(cls, *args, **kws):
1648 temp = kws.copy()
1649 extra = temp.pop('extra')
1650 result = self.theclass.__new__(cls, *args, **temp)
1651 result.extra = extra
1652 return result
1653
1654 def newmeth(self, start):
1655 return start + self.year + self.month + self.second
1656
1657 args = 2003, 4, 14, 12, 13, 41
1658
1659 dt1 = self.theclass(*args)
1660 dt2 = C(*args, **{'extra': 7})
1661
1662 self.assertEqual(dt2.__class__, C)
1663 self.assertEqual(dt2.theAnswer, 42)
1664 self.assertEqual(dt2.extra, 7)
1665 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1666 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1667 dt1.second - 7)
1668
Tim Peters604c0132004-06-07 23:04:33 +00001669class SubclassTime(time):
1670 sub_var = 1
1671
Guido van Rossumd8faa362007-04-27 19:54:29 +00001672class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001673
1674 theclass = time
1675
1676 def test_basic_attributes(self):
1677 t = self.theclass(12, 0)
1678 self.assertEqual(t.hour, 12)
1679 self.assertEqual(t.minute, 0)
1680 self.assertEqual(t.second, 0)
1681 self.assertEqual(t.microsecond, 0)
1682
1683 def test_basic_attributes_nonzero(self):
1684 # Make sure all attributes are non-zero so bugs in
1685 # bit-shifting access show up.
1686 t = self.theclass(12, 59, 59, 8000)
1687 self.assertEqual(t.hour, 12)
1688 self.assertEqual(t.minute, 59)
1689 self.assertEqual(t.second, 59)
1690 self.assertEqual(t.microsecond, 8000)
1691
1692 def test_roundtrip(self):
1693 t = self.theclass(1, 2, 3, 4)
1694
1695 # Verify t -> string -> time identity.
1696 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001697 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001698 s = s[9:]
1699 t2 = eval(s)
1700 self.assertEqual(t, t2)
1701
1702 # Verify identity via reconstructing from pieces.
1703 t2 = self.theclass(t.hour, t.minute, t.second,
1704 t.microsecond)
1705 self.assertEqual(t, t2)
1706
1707 def test_comparing(self):
1708 args = [1, 2, 3, 4]
1709 t1 = self.theclass(*args)
1710 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001711 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001712 self.assertTrue(t1 <= t2)
1713 self.assertTrue(t1 >= t2)
1714 self.assertTrue(not t1 != t2)
1715 self.assertTrue(not t1 < t2)
1716 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001717
1718 for i in range(len(args)):
1719 newargs = args[:]
1720 newargs[i] = args[i] + 1
1721 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001722 self.assertTrue(t1 < t2)
1723 self.assertTrue(t2 > t1)
1724 self.assertTrue(t1 <= t2)
1725 self.assertTrue(t2 >= t1)
1726 self.assertTrue(t1 != t2)
1727 self.assertTrue(t2 != t1)
1728 self.assertTrue(not t1 == t2)
1729 self.assertTrue(not t2 == t1)
1730 self.assertTrue(not t1 > t2)
1731 self.assertTrue(not t2 < t1)
1732 self.assertTrue(not t1 >= t2)
1733 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001734
Tim Peters68124bb2003-02-08 03:46:31 +00001735 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001736 self.assertEqual(t1 == badarg, False)
1737 self.assertEqual(t1 != badarg, True)
1738 self.assertEqual(badarg == t1, False)
1739 self.assertEqual(badarg != t1, True)
1740
Tim Peters2a799bf2002-12-16 20:18:38 +00001741 self.assertRaises(TypeError, lambda: t1 <= badarg)
1742 self.assertRaises(TypeError, lambda: t1 < badarg)
1743 self.assertRaises(TypeError, lambda: t1 > badarg)
1744 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001745 self.assertRaises(TypeError, lambda: badarg <= t1)
1746 self.assertRaises(TypeError, lambda: badarg < t1)
1747 self.assertRaises(TypeError, lambda: badarg > t1)
1748 self.assertRaises(TypeError, lambda: badarg >= t1)
1749
1750 def test_bad_constructor_arguments(self):
1751 # bad hours
1752 self.theclass(0, 0) # no exception
1753 self.theclass(23, 0) # no exception
1754 self.assertRaises(ValueError, self.theclass, -1, 0)
1755 self.assertRaises(ValueError, self.theclass, 24, 0)
1756 # bad minutes
1757 self.theclass(23, 0) # no exception
1758 self.theclass(23, 59) # no exception
1759 self.assertRaises(ValueError, self.theclass, 23, -1)
1760 self.assertRaises(ValueError, self.theclass, 23, 60)
1761 # bad seconds
1762 self.theclass(23, 59, 0) # no exception
1763 self.theclass(23, 59, 59) # no exception
1764 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1765 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1766 # bad microseconds
1767 self.theclass(23, 59, 59, 0) # no exception
1768 self.theclass(23, 59, 59, 999999) # no exception
1769 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1770 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1771
1772 def test_hash_equality(self):
1773 d = self.theclass(23, 30, 17)
1774 e = self.theclass(23, 30, 17)
1775 self.assertEqual(d, e)
1776 self.assertEqual(hash(d), hash(e))
1777
1778 dic = {d: 1}
1779 dic[e] = 2
1780 self.assertEqual(len(dic), 1)
1781 self.assertEqual(dic[d], 2)
1782 self.assertEqual(dic[e], 2)
1783
1784 d = self.theclass(0, 5, 17)
1785 e = self.theclass(0, 5, 17)
1786 self.assertEqual(d, e)
1787 self.assertEqual(hash(d), hash(e))
1788
1789 dic = {d: 1}
1790 dic[e] = 2
1791 self.assertEqual(len(dic), 1)
1792 self.assertEqual(dic[d], 2)
1793 self.assertEqual(dic[e], 2)
1794
1795 def test_isoformat(self):
1796 t = self.theclass(4, 5, 1, 123)
1797 self.assertEqual(t.isoformat(), "04:05:01.000123")
1798 self.assertEqual(t.isoformat(), str(t))
1799
1800 t = self.theclass()
1801 self.assertEqual(t.isoformat(), "00:00:00")
1802 self.assertEqual(t.isoformat(), str(t))
1803
1804 t = self.theclass(microsecond=1)
1805 self.assertEqual(t.isoformat(), "00:00:00.000001")
1806 self.assertEqual(t.isoformat(), str(t))
1807
1808 t = self.theclass(microsecond=10)
1809 self.assertEqual(t.isoformat(), "00:00:00.000010")
1810 self.assertEqual(t.isoformat(), str(t))
1811
1812 t = self.theclass(microsecond=100)
1813 self.assertEqual(t.isoformat(), "00:00:00.000100")
1814 self.assertEqual(t.isoformat(), str(t))
1815
1816 t = self.theclass(microsecond=1000)
1817 self.assertEqual(t.isoformat(), "00:00:00.001000")
1818 self.assertEqual(t.isoformat(), str(t))
1819
1820 t = self.theclass(microsecond=10000)
1821 self.assertEqual(t.isoformat(), "00:00:00.010000")
1822 self.assertEqual(t.isoformat(), str(t))
1823
1824 t = self.theclass(microsecond=100000)
1825 self.assertEqual(t.isoformat(), "00:00:00.100000")
1826 self.assertEqual(t.isoformat(), str(t))
1827
Thomas Wouterscf297e42007-02-23 15:07:44 +00001828 def test_1653736(self):
1829 # verify it doesn't accept extra keyword arguments
1830 t = self.theclass(second=1)
1831 self.assertRaises(TypeError, t.isoformat, foo=3)
1832
Tim Peters2a799bf2002-12-16 20:18:38 +00001833 def test_strftime(self):
1834 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001835 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001836 # A naive object replaces %z and %Z with empty strings.
1837 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1838
Eric Smith1ba31142007-09-11 18:06:02 +00001839 def test_format(self):
1840 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001841 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001842
1843 # check that a derived class's __str__() gets called
1844 class A(self.theclass):
1845 def __str__(self):
1846 return 'A'
1847 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001848 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001849
1850 # check that a derived class's strftime gets called
1851 class B(self.theclass):
1852 def strftime(self, format_spec):
1853 return 'B'
1854 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001855 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001856
1857 for fmt in ['%H %M %S',
1858 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001859 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1860 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1861 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001862
Tim Peters2a799bf2002-12-16 20:18:38 +00001863 def test_str(self):
1864 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1865 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1866 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1867 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1868 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1869
1870 def test_repr(self):
1871 name = 'datetime.' + self.theclass.__name__
1872 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1873 "%s(1, 2, 3, 4)" % name)
1874 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1875 "%s(10, 2, 3, 4000)" % name)
1876 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1877 "%s(0, 2, 3, 400000)" % name)
1878 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1879 "%s(12, 2, 3)" % name)
1880 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1881 "%s(23, 15)" % name)
1882
1883 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00001884 self.assertIsInstance(self.theclass.min, self.theclass)
1885 self.assertIsInstance(self.theclass.max, self.theclass)
1886 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001887 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001888
1889 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001890 args = 20, 59, 16, 64**2
1891 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001892 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001893 green = pickler.dumps(orig, proto)
1894 derived = unpickler.loads(green)
1895 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001896
Tim Peters604c0132004-06-07 23:04:33 +00001897 def test_pickling_subclass_time(self):
1898 args = 20, 59, 16, 64**2
1899 orig = SubclassTime(*args)
1900 for pickler, unpickler, proto in pickle_choices:
1901 green = pickler.dumps(orig, proto)
1902 derived = unpickler.loads(green)
1903 self.assertEqual(orig, derived)
1904
Tim Peters2a799bf2002-12-16 20:18:38 +00001905 def test_bool(self):
1906 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001907 self.assertTrue(cls(1))
1908 self.assertTrue(cls(0, 1))
1909 self.assertTrue(cls(0, 0, 1))
1910 self.assertTrue(cls(0, 0, 0, 1))
1911 self.assertTrue(not cls(0))
1912 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001913
Tim Peters12bf3392002-12-24 05:41:27 +00001914 def test_replace(self):
1915 cls = self.theclass
1916 args = [1, 2, 3, 4]
1917 base = cls(*args)
1918 self.assertEqual(base, base.replace())
1919
1920 i = 0
1921 for name, newval in (("hour", 5),
1922 ("minute", 6),
1923 ("second", 7),
1924 ("microsecond", 8)):
1925 newargs = args[:]
1926 newargs[i] = newval
1927 expected = cls(*newargs)
1928 got = base.replace(**{name: newval})
1929 self.assertEqual(expected, got)
1930 i += 1
1931
1932 # Out of bounds.
1933 base = cls(1)
1934 self.assertRaises(ValueError, base.replace, hour=24)
1935 self.assertRaises(ValueError, base.replace, minute=-1)
1936 self.assertRaises(ValueError, base.replace, second=100)
1937 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1938
Tim Petersa98924a2003-05-17 05:55:19 +00001939 def test_subclass_time(self):
1940
1941 class C(self.theclass):
1942 theAnswer = 42
1943
1944 def __new__(cls, *args, **kws):
1945 temp = kws.copy()
1946 extra = temp.pop('extra')
1947 result = self.theclass.__new__(cls, *args, **temp)
1948 result.extra = extra
1949 return result
1950
1951 def newmeth(self, start):
1952 return start + self.hour + self.second
1953
1954 args = 4, 5, 6
1955
1956 dt1 = self.theclass(*args)
1957 dt2 = C(*args, **{'extra': 7})
1958
1959 self.assertEqual(dt2.__class__, C)
1960 self.assertEqual(dt2.theAnswer, 42)
1961 self.assertEqual(dt2.extra, 7)
1962 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1963 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1964
Armin Rigof4afb212005-11-07 07:15:48 +00001965 def test_backdoor_resistance(self):
1966 # see TestDate.test_backdoor_resistance().
1967 base = '2:59.0'
1968 for hour_byte in ' ', '9', chr(24), '\xff':
1969 self.assertRaises(TypeError, self.theclass,
1970 hour_byte + base[1:])
1971
Tim Peters855fe882002-12-22 03:43:39 +00001972# A mixin for classes with a tzinfo= argument. Subclasses must define
1973# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001974# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001975class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001976
Tim Petersbad8ff02002-12-30 20:52:32 +00001977 def test_argument_passing(self):
1978 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001979 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001980 class introspective(tzinfo):
1981 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001982 def utcoffset(self, dt):
1983 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001984 dst = utcoffset
1985
1986 obj = cls(1, 2, 3, tzinfo=introspective())
1987
Tim Peters0bf60bd2003-01-08 20:40:01 +00001988 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001989 self.assertEqual(obj.tzname(), expected)
1990
Tim Peters0bf60bd2003-01-08 20:40:01 +00001991 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001992 self.assertEqual(obj.utcoffset(), expected)
1993 self.assertEqual(obj.dst(), expected)
1994
Tim Peters855fe882002-12-22 03:43:39 +00001995 def test_bad_tzinfo_classes(self):
1996 cls = self.theclass
1997 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001998
Tim Peters855fe882002-12-22 03:43:39 +00001999 class NiceTry(object):
2000 def __init__(self): pass
2001 def utcoffset(self, dt): pass
2002 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2003
2004 class BetterTry(tzinfo):
2005 def __init__(self): pass
2006 def utcoffset(self, dt): pass
2007 b = BetterTry()
2008 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002009 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002010
2011 def test_utc_offset_out_of_bounds(self):
2012 class Edgy(tzinfo):
2013 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002014 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002015 def utcoffset(self, dt):
2016 return self.offset
2017
2018 cls = self.theclass
2019 for offset, legit in ((-1440, False),
2020 (-1439, True),
2021 (1439, True),
2022 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002023 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002024 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002025 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002026 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002027 else:
2028 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002029 if legit:
2030 aofs = abs(offset)
2031 h, m = divmod(aofs, 60)
2032 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002033 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002034 t = t.timetz()
2035 self.assertEqual(str(t), "01:02:03" + tag)
2036 else:
2037 self.assertRaises(ValueError, str, t)
2038
2039 def test_tzinfo_classes(self):
2040 cls = self.theclass
2041 class C1(tzinfo):
2042 def utcoffset(self, dt): return None
2043 def dst(self, dt): return None
2044 def tzname(self, dt): return None
2045 for t in (cls(1, 1, 1),
2046 cls(1, 1, 1, tzinfo=None),
2047 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002048 self.assertTrue(t.utcoffset() is None)
2049 self.assertTrue(t.dst() is None)
2050 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002051
Tim Peters855fe882002-12-22 03:43:39 +00002052 class C3(tzinfo):
2053 def utcoffset(self, dt): return timedelta(minutes=-1439)
2054 def dst(self, dt): return timedelta(minutes=1439)
2055 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002056 t = cls(1, 1, 1, tzinfo=C3())
2057 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2058 self.assertEqual(t.dst(), timedelta(minutes=1439))
2059 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002060
2061 # Wrong types.
2062 class C4(tzinfo):
2063 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002064 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002065 def tzname(self, dt): return 0
2066 t = cls(1, 1, 1, tzinfo=C4())
2067 self.assertRaises(TypeError, t.utcoffset)
2068 self.assertRaises(TypeError, t.dst)
2069 self.assertRaises(TypeError, t.tzname)
2070
2071 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002072 class C6(tzinfo):
2073 def utcoffset(self, dt): return timedelta(hours=-24)
2074 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002075 t = cls(1, 1, 1, tzinfo=C6())
2076 self.assertRaises(ValueError, t.utcoffset)
2077 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002078
2079 # Not a whole number of minutes.
2080 class C7(tzinfo):
2081 def utcoffset(self, dt): return timedelta(seconds=61)
2082 def dst(self, dt): return timedelta(microseconds=-81)
2083 t = cls(1, 1, 1, tzinfo=C7())
2084 self.assertRaises(ValueError, t.utcoffset)
2085 self.assertRaises(ValueError, t.dst)
2086
Tim Peters4c0db782002-12-26 05:01:19 +00002087 def test_aware_compare(self):
2088 cls = self.theclass
2089
Tim Peters60c76e42002-12-27 00:41:11 +00002090 # Ensure that utcoffset() gets ignored if the comparands have
2091 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002092 class OperandDependentOffset(tzinfo):
2093 def utcoffset(self, t):
2094 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002095 # d0 and d1 equal after adjustment
2096 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002097 else:
Tim Peters397301e2003-01-02 21:28:08 +00002098 # d2 off in the weeds
2099 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002100
2101 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2102 d0 = base.replace(minute=3)
2103 d1 = base.replace(minute=9)
2104 d2 = base.replace(minute=11)
2105 for x in d0, d1, d2:
2106 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002107 for op in lt, le, gt, ge, eq, ne:
2108 got = op(x, y)
2109 expected = op(x.minute, y.minute)
2110 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002111
2112 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002113 # Note that a time can't actually have an operand-depedent offset,
2114 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2115 # so skip this test for time.
2116 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002117 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2118 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2119 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2120 for x in d0, d1, d2:
2121 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002122 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002123 if (x is d0 or x is d1) and (y is d0 or y is d1):
2124 expected = 0
2125 elif x is y is d2:
2126 expected = 0
2127 elif x is d2:
2128 expected = -1
2129 else:
2130 assert y is d2
2131 expected = 1
2132 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002133
Tim Peters855fe882002-12-22 03:43:39 +00002134
Tim Peters0bf60bd2003-01-08 20:40:01 +00002135# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002136class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002137 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002138
2139 def test_empty(self):
2140 t = self.theclass()
2141 self.assertEqual(t.hour, 0)
2142 self.assertEqual(t.minute, 0)
2143 self.assertEqual(t.second, 0)
2144 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002145 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002146
Tim Peters2a799bf2002-12-16 20:18:38 +00002147 def test_zones(self):
2148 est = FixedOffset(-300, "EST", 1)
2149 utc = FixedOffset(0, "UTC", -2)
2150 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002151 t1 = time( 7, 47, tzinfo=est)
2152 t2 = time(12, 47, tzinfo=utc)
2153 t3 = time(13, 47, tzinfo=met)
2154 t4 = time(microsecond=40)
2155 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002156
2157 self.assertEqual(t1.tzinfo, est)
2158 self.assertEqual(t2.tzinfo, utc)
2159 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002160 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002161 self.assertEqual(t5.tzinfo, utc)
2162
Tim Peters855fe882002-12-22 03:43:39 +00002163 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2164 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2165 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002166 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002167 self.assertRaises(TypeError, t1.utcoffset, "no args")
2168
2169 self.assertEqual(t1.tzname(), "EST")
2170 self.assertEqual(t2.tzname(), "UTC")
2171 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002172 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002173 self.assertRaises(TypeError, t1.tzname, "no args")
2174
Tim Peters855fe882002-12-22 03:43:39 +00002175 self.assertEqual(t1.dst(), timedelta(minutes=1))
2176 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2177 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002178 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002179 self.assertRaises(TypeError, t1.dst, "no args")
2180
2181 self.assertEqual(hash(t1), hash(t2))
2182 self.assertEqual(hash(t1), hash(t3))
2183 self.assertEqual(hash(t2), hash(t3))
2184
2185 self.assertEqual(t1, t2)
2186 self.assertEqual(t1, t3)
2187 self.assertEqual(t2, t3)
2188 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2189 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2190 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2191
2192 self.assertEqual(str(t1), "07:47:00-05:00")
2193 self.assertEqual(str(t2), "12:47:00+00:00")
2194 self.assertEqual(str(t3), "13:47:00+01:00")
2195 self.assertEqual(str(t4), "00:00:00.000040")
2196 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2197
2198 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2199 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2200 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2201 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2202 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2203
Tim Peters0bf60bd2003-01-08 20:40:01 +00002204 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002205 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2206 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2207 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2208 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2209 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2210
2211 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2212 "07:47:00 %Z=EST %z=-0500")
2213 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2214 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2215
2216 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002217 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002218 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2219 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2220
Tim Petersb92bb712002-12-21 17:44:07 +00002221 # Check that an invalid tzname result raises an exception.
2222 class Badtzname(tzinfo):
2223 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002224 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002225 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2226 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002227
2228 def test_hash_edge_cases(self):
2229 # Offsets that overflow a basic time.
2230 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2231 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2232 self.assertEqual(hash(t1), hash(t2))
2233
2234 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2235 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2236 self.assertEqual(hash(t1), hash(t2))
2237
Tim Peters2a799bf2002-12-16 20:18:38 +00002238 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002239 # Try one without a tzinfo.
2240 args = 20, 59, 16, 64**2
2241 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002242 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002243 green = pickler.dumps(orig, proto)
2244 derived = unpickler.loads(green)
2245 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002246
2247 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002248 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002249 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002250 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002251 green = pickler.dumps(orig, proto)
2252 derived = unpickler.loads(green)
2253 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002254 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002255 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2256 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002257
2258 def test_more_bool(self):
2259 # Test cases with non-None tzinfo.
2260 cls = self.theclass
2261
2262 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002263 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002264
2265 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002266 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002267
2268 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002269 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002270
2271 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002272 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002273
2274 # Mostly ensuring this doesn't overflow internally.
2275 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002276 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002277
2278 # But this should yield a value error -- the utcoffset is bogus.
2279 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2280 self.assertRaises(ValueError, lambda: bool(t))
2281
2282 # Likewise.
2283 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2284 self.assertRaises(ValueError, lambda: bool(t))
2285
Tim Peters12bf3392002-12-24 05:41:27 +00002286 def test_replace(self):
2287 cls = self.theclass
2288 z100 = FixedOffset(100, "+100")
2289 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2290 args = [1, 2, 3, 4, z100]
2291 base = cls(*args)
2292 self.assertEqual(base, base.replace())
2293
2294 i = 0
2295 for name, newval in (("hour", 5),
2296 ("minute", 6),
2297 ("second", 7),
2298 ("microsecond", 8),
2299 ("tzinfo", zm200)):
2300 newargs = args[:]
2301 newargs[i] = newval
2302 expected = cls(*newargs)
2303 got = base.replace(**{name: newval})
2304 self.assertEqual(expected, got)
2305 i += 1
2306
2307 # Ensure we can get rid of a tzinfo.
2308 self.assertEqual(base.tzname(), "+100")
2309 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002310 self.assertTrue(base2.tzinfo is None)
2311 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002312
2313 # Ensure we can add one.
2314 base3 = base2.replace(tzinfo=z100)
2315 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002316 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002317
2318 # Out of bounds.
2319 base = cls(1)
2320 self.assertRaises(ValueError, base.replace, hour=24)
2321 self.assertRaises(ValueError, base.replace, minute=-1)
2322 self.assertRaises(ValueError, base.replace, second=100)
2323 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2324
Tim Peters60c76e42002-12-27 00:41:11 +00002325 def test_mixed_compare(self):
2326 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002327 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002328 self.assertEqual(t1, t2)
2329 t2 = t2.replace(tzinfo=None)
2330 self.assertEqual(t1, t2)
2331 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2332 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002333 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2334 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002335
Tim Peters0bf60bd2003-01-08 20:40:01 +00002336 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002337 class Varies(tzinfo):
2338 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002339 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002340 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002341 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002342 return self.offset
2343
2344 v = Varies()
2345 t1 = t2.replace(tzinfo=v)
2346 t2 = t2.replace(tzinfo=v)
2347 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2348 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2349 self.assertEqual(t1, t2)
2350
2351 # But if they're not identical, it isn't ignored.
2352 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002353 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002354
Tim Petersa98924a2003-05-17 05:55:19 +00002355 def test_subclass_timetz(self):
2356
2357 class C(self.theclass):
2358 theAnswer = 42
2359
2360 def __new__(cls, *args, **kws):
2361 temp = kws.copy()
2362 extra = temp.pop('extra')
2363 result = self.theclass.__new__(cls, *args, **temp)
2364 result.extra = extra
2365 return result
2366
2367 def newmeth(self, start):
2368 return start + self.hour + self.second
2369
2370 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2371
2372 dt1 = self.theclass(*args)
2373 dt2 = C(*args, **{'extra': 7})
2374
2375 self.assertEqual(dt2.__class__, C)
2376 self.assertEqual(dt2.theAnswer, 42)
2377 self.assertEqual(dt2.extra, 7)
2378 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2379 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2380
Tim Peters4c0db782002-12-26 05:01:19 +00002381
Tim Peters0bf60bd2003-01-08 20:40:01 +00002382# Testing datetime objects with a non-None tzinfo.
2383
Guido van Rossumd8faa362007-04-27 19:54:29 +00002384class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002385 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002386
2387 def test_trivial(self):
2388 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2389 self.assertEqual(dt.year, 1)
2390 self.assertEqual(dt.month, 2)
2391 self.assertEqual(dt.day, 3)
2392 self.assertEqual(dt.hour, 4)
2393 self.assertEqual(dt.minute, 5)
2394 self.assertEqual(dt.second, 6)
2395 self.assertEqual(dt.microsecond, 7)
2396 self.assertEqual(dt.tzinfo, None)
2397
2398 def test_even_more_compare(self):
2399 # The test_compare() and test_more_compare() inherited from TestDate
2400 # and TestDateTime covered non-tzinfo cases.
2401
2402 # Smallest possible after UTC adjustment.
2403 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2404 # Largest possible after UTC adjustment.
2405 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2406 tzinfo=FixedOffset(-1439, ""))
2407
2408 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002409 self.assertTrue(t1 < t2)
2410 self.assertTrue(t1 != t2)
2411 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002412
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002413 self.assertEqual(t1, t1)
2414 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002415
2416 # Equal afer adjustment.
2417 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2418 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2419 self.assertEqual(t1, t2)
2420
2421 # Change t1 not to subtract a minute, and t1 should be larger.
2422 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002423 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002424
2425 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2426 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002427 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002428
2429 # Back to the original t1, but make seconds resolve it.
2430 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2431 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002432 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002433
2434 # Likewise, but make microseconds resolve it.
2435 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2436 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002437 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002438
2439 # Make t2 naive and it should fail.
2440 t2 = self.theclass.min
2441 self.assertRaises(TypeError, lambda: t1 == t2)
2442 self.assertEqual(t2, t2)
2443
2444 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2445 class Naive(tzinfo):
2446 def utcoffset(self, dt): return None
2447 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2448 self.assertRaises(TypeError, lambda: t1 == t2)
2449 self.assertEqual(t2, t2)
2450
2451 # OTOH, it's OK to compare two of these mixing the two ways of being
2452 # naive.
2453 t1 = self.theclass(5, 6, 7)
2454 self.assertEqual(t1, t2)
2455
2456 # Try a bogus uctoffset.
2457 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002458 def utcoffset(self, dt):
2459 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002460 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2461 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002462 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002463
Tim Peters2a799bf2002-12-16 20:18:38 +00002464 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002465 # Try one without a tzinfo.
2466 args = 6, 7, 23, 20, 59, 1, 64**2
2467 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002468 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002469 green = pickler.dumps(orig, proto)
2470 derived = unpickler.loads(green)
2471 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002472
2473 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002474 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002475 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002476 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002477 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002478 green = pickler.dumps(orig, proto)
2479 derived = unpickler.loads(green)
2480 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002481 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002482 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2483 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002484
2485 def test_extreme_hashes(self):
2486 # If an attempt is made to hash these via subtracting the offset
2487 # then hashing a datetime object, OverflowError results. The
2488 # Python implementation used to blow up here.
2489 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2490 hash(t)
2491 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2492 tzinfo=FixedOffset(-1439, ""))
2493 hash(t)
2494
2495 # OTOH, an OOB offset should blow up.
2496 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2497 self.assertRaises(ValueError, hash, t)
2498
2499 def test_zones(self):
2500 est = FixedOffset(-300, "EST")
2501 utc = FixedOffset(0, "UTC")
2502 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002503 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2504 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2505 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002506 self.assertEqual(t1.tzinfo, est)
2507 self.assertEqual(t2.tzinfo, utc)
2508 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002509 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2510 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2511 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002512 self.assertEqual(t1.tzname(), "EST")
2513 self.assertEqual(t2.tzname(), "UTC")
2514 self.assertEqual(t3.tzname(), "MET")
2515 self.assertEqual(hash(t1), hash(t2))
2516 self.assertEqual(hash(t1), hash(t3))
2517 self.assertEqual(hash(t2), hash(t3))
2518 self.assertEqual(t1, t2)
2519 self.assertEqual(t1, t3)
2520 self.assertEqual(t2, t3)
2521 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2522 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2523 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002524 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002525 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2526 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2527 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2528
2529 def test_combine(self):
2530 met = FixedOffset(60, "MET")
2531 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002532 tz = time(18, 45, 3, 1234, tzinfo=met)
2533 dt = datetime.combine(d, tz)
2534 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002535 tzinfo=met))
2536
2537 def test_extract(self):
2538 met = FixedOffset(60, "MET")
2539 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2540 self.assertEqual(dt.date(), date(2002, 3, 4))
2541 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002542 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002543
2544 def test_tz_aware_arithmetic(self):
2545 import random
2546
2547 now = self.theclass.now()
2548 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002549 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002550 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002551 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002552 self.assertEqual(nowaware.timetz(), timeaware)
2553
2554 # Can't mix aware and non-aware.
2555 self.assertRaises(TypeError, lambda: now - nowaware)
2556 self.assertRaises(TypeError, lambda: nowaware - now)
2557
Tim Peters0bf60bd2003-01-08 20:40:01 +00002558 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002559 self.assertRaises(TypeError, lambda: now + nowaware)
2560 self.assertRaises(TypeError, lambda: nowaware + now)
2561 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2562
2563 # Subtracting should yield 0.
2564 self.assertEqual(now - now, timedelta(0))
2565 self.assertEqual(nowaware - nowaware, timedelta(0))
2566
2567 # Adding a delta should preserve tzinfo.
2568 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2569 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002570 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002571 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002572 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002573 self.assertEqual(nowawareplus, nowawareplus2)
2574
2575 # that - delta should be what we started with, and that - what we
2576 # started with should be delta.
2577 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002578 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002579 self.assertEqual(nowaware, diff)
2580 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2581 self.assertEqual(nowawareplus - nowaware, delta)
2582
2583 # Make up a random timezone.
2584 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002585 # Attach it to nowawareplus.
2586 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002587 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002588 # Make sure the difference takes the timezone adjustments into account.
2589 got = nowaware - nowawareplus
2590 # Expected: (nowaware base - nowaware offset) -
2591 # (nowawareplus base - nowawareplus offset) =
2592 # (nowaware base - nowawareplus base) +
2593 # (nowawareplus offset - nowaware offset) =
2594 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002595 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002596 self.assertEqual(got, expected)
2597
2598 # Try max possible difference.
2599 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2600 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2601 tzinfo=FixedOffset(-1439, "max"))
2602 maxdiff = max - min
2603 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2604 timedelta(minutes=2*1439))
2605
2606 def test_tzinfo_now(self):
2607 meth = self.theclass.now
2608 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2609 base = meth()
2610 # Try with and without naming the keyword.
2611 off42 = FixedOffset(42, "42")
2612 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002613 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002614 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002615 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002616 # Bad argument with and w/o naming the keyword.
2617 self.assertRaises(TypeError, meth, 16)
2618 self.assertRaises(TypeError, meth, tzinfo=16)
2619 # Bad keyword name.
2620 self.assertRaises(TypeError, meth, tinfo=off42)
2621 # Too many args.
2622 self.assertRaises(TypeError, meth, off42, off42)
2623
Tim Peters10cadce2003-01-23 19:58:02 +00002624 # We don't know which time zone we're in, and don't have a tzinfo
2625 # class to represent it, so seeing whether a tz argument actually
2626 # does a conversion is tricky.
2627 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2628 utc = FixedOffset(0, "utc", 0)
2629 for dummy in range(3):
2630 now = datetime.now(weirdtz)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002631 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002632 utcnow = datetime.utcnow().replace(tzinfo=utc)
2633 now2 = utcnow.astimezone(weirdtz)
2634 if abs(now - now2) < timedelta(seconds=30):
2635 break
2636 # Else the code is broken, or more than 30 seconds passed between
2637 # calls; assuming the latter, just try again.
2638 else:
2639 # Three strikes and we're out.
2640 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2641
Tim Peters2a799bf2002-12-16 20:18:38 +00002642 def test_tzinfo_fromtimestamp(self):
2643 import time
2644 meth = self.theclass.fromtimestamp
2645 ts = time.time()
2646 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2647 base = meth(ts)
2648 # Try with and without naming the keyword.
2649 off42 = FixedOffset(42, "42")
2650 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002651 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002652 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002653 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002654 # Bad argument with and w/o naming the keyword.
2655 self.assertRaises(TypeError, meth, ts, 16)
2656 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2657 # Bad keyword name.
2658 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2659 # Too many args.
2660 self.assertRaises(TypeError, meth, ts, off42, off42)
2661 # Too few args.
2662 self.assertRaises(TypeError, meth)
2663
Tim Peters2a44a8d2003-01-23 20:53:10 +00002664 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002665 timestamp = 1000000000
2666 utcdatetime = datetime.utcfromtimestamp(timestamp)
2667 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2668 # But on some flavor of Mac, it's nowhere near that. So we can't have
2669 # any idea here what time that actually is, we can only test that
2670 # relative changes match.
2671 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2672 tz = FixedOffset(utcoffset, "tz", 0)
2673 expected = utcdatetime + utcoffset
2674 got = datetime.fromtimestamp(timestamp, tz)
2675 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002676
Tim Peters2a799bf2002-12-16 20:18:38 +00002677 def test_tzinfo_utcnow(self):
2678 meth = self.theclass.utcnow
2679 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2680 base = meth()
2681 # Try with and without naming the keyword; for whatever reason,
2682 # utcnow() doesn't accept a tzinfo argument.
2683 off42 = FixedOffset(42, "42")
2684 self.assertRaises(TypeError, meth, off42)
2685 self.assertRaises(TypeError, meth, tzinfo=off42)
2686
2687 def test_tzinfo_utcfromtimestamp(self):
2688 import time
2689 meth = self.theclass.utcfromtimestamp
2690 ts = time.time()
2691 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2692 base = meth(ts)
2693 # Try with and without naming the keyword; for whatever reason,
2694 # utcfromtimestamp() doesn't accept a tzinfo argument.
2695 off42 = FixedOffset(42, "42")
2696 self.assertRaises(TypeError, meth, ts, off42)
2697 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2698
2699 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002700 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002701 # DST flag.
2702 class DST(tzinfo):
2703 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002704 if isinstance(dstvalue, int):
2705 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002706 self.dstvalue = dstvalue
2707 def dst(self, dt):
2708 return self.dstvalue
2709
2710 cls = self.theclass
2711 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2712 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2713 t = d.timetuple()
2714 self.assertEqual(1, t.tm_year)
2715 self.assertEqual(1, t.tm_mon)
2716 self.assertEqual(1, t.tm_mday)
2717 self.assertEqual(10, t.tm_hour)
2718 self.assertEqual(20, t.tm_min)
2719 self.assertEqual(30, t.tm_sec)
2720 self.assertEqual(0, t.tm_wday)
2721 self.assertEqual(1, t.tm_yday)
2722 self.assertEqual(flag, t.tm_isdst)
2723
2724 # dst() returns wrong type.
2725 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2726
2727 # dst() at the edge.
2728 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2729 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2730
2731 # dst() out of range.
2732 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2733 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2734
2735 def test_utctimetuple(self):
2736 class DST(tzinfo):
2737 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002738 if isinstance(dstvalue, int):
2739 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002740 self.dstvalue = dstvalue
2741 def dst(self, dt):
2742 return self.dstvalue
2743
2744 cls = self.theclass
2745 # This can't work: DST didn't implement utcoffset.
2746 self.assertRaises(NotImplementedError,
2747 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2748
2749 class UOFS(DST):
2750 def __init__(self, uofs, dofs=None):
2751 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002752 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002753 def utcoffset(self, dt):
2754 return self.uofs
2755
2756 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2757 # in effect for a UTC time.
2758 for dstvalue in -33, 33, 0, None:
2759 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2760 t = d.utctimetuple()
2761 self.assertEqual(d.year, t.tm_year)
2762 self.assertEqual(d.month, t.tm_mon)
2763 self.assertEqual(d.day, t.tm_mday)
2764 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2765 self.assertEqual(13, t.tm_min)
2766 self.assertEqual(d.second, t.tm_sec)
2767 self.assertEqual(d.weekday(), t.tm_wday)
2768 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2769 t.tm_yday)
2770 self.assertEqual(0, t.tm_isdst)
2771
2772 # At the edges, UTC adjustment can normalize into years out-of-range
2773 # for a datetime object. Ensure that a correct timetuple is
2774 # created anyway.
2775 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2776 # That goes back 1 minute less than a full day.
2777 t = tiny.utctimetuple()
2778 self.assertEqual(t.tm_year, MINYEAR-1)
2779 self.assertEqual(t.tm_mon, 12)
2780 self.assertEqual(t.tm_mday, 31)
2781 self.assertEqual(t.tm_hour, 0)
2782 self.assertEqual(t.tm_min, 1)
2783 self.assertEqual(t.tm_sec, 37)
2784 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2785 self.assertEqual(t.tm_isdst, 0)
2786
2787 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2788 # That goes forward 1 minute less than a full day.
2789 t = huge.utctimetuple()
2790 self.assertEqual(t.tm_year, MAXYEAR+1)
2791 self.assertEqual(t.tm_mon, 1)
2792 self.assertEqual(t.tm_mday, 1)
2793 self.assertEqual(t.tm_hour, 23)
2794 self.assertEqual(t.tm_min, 58)
2795 self.assertEqual(t.tm_sec, 37)
2796 self.assertEqual(t.tm_yday, 1)
2797 self.assertEqual(t.tm_isdst, 0)
2798
2799 def test_tzinfo_isoformat(self):
2800 zero = FixedOffset(0, "+00:00")
2801 plus = FixedOffset(220, "+03:40")
2802 minus = FixedOffset(-231, "-03:51")
2803 unknown = FixedOffset(None, "")
2804
2805 cls = self.theclass
2806 datestr = '0001-02-03'
2807 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002808 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002809 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2810 timestr = '04:05:59' + (us and '.987001' or '')
2811 ofsstr = ofs is not None and d.tzname() or ''
2812 tailstr = timestr + ofsstr
2813 iso = d.isoformat()
2814 self.assertEqual(iso, datestr + 'T' + tailstr)
2815 self.assertEqual(iso, d.isoformat('T'))
2816 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002817 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002818 self.assertEqual(str(d), datestr + ' ' + tailstr)
2819
Tim Peters12bf3392002-12-24 05:41:27 +00002820 def test_replace(self):
2821 cls = self.theclass
2822 z100 = FixedOffset(100, "+100")
2823 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2824 args = [1, 2, 3, 4, 5, 6, 7, z100]
2825 base = cls(*args)
2826 self.assertEqual(base, base.replace())
2827
2828 i = 0
2829 for name, newval in (("year", 2),
2830 ("month", 3),
2831 ("day", 4),
2832 ("hour", 5),
2833 ("minute", 6),
2834 ("second", 7),
2835 ("microsecond", 8),
2836 ("tzinfo", zm200)):
2837 newargs = args[:]
2838 newargs[i] = newval
2839 expected = cls(*newargs)
2840 got = base.replace(**{name: newval})
2841 self.assertEqual(expected, got)
2842 i += 1
2843
2844 # Ensure we can get rid of a tzinfo.
2845 self.assertEqual(base.tzname(), "+100")
2846 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002847 self.assertTrue(base2.tzinfo is None)
2848 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002849
2850 # Ensure we can add one.
2851 base3 = base2.replace(tzinfo=z100)
2852 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002853 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002854
2855 # Out of bounds.
2856 base = cls(2000, 2, 29)
2857 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002858
Tim Peters80475bb2002-12-25 07:40:55 +00002859 def test_more_astimezone(self):
2860 # The inherited test_astimezone covered some trivial and error cases.
2861 fnone = FixedOffset(None, "None")
2862 f44m = FixedOffset(44, "44")
2863 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2864
Tim Peters10cadce2003-01-23 19:58:02 +00002865 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002866 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002867 # Replacing with degenerate tzinfo raises an exception.
2868 self.assertRaises(ValueError, dt.astimezone, fnone)
2869 # Ditto with None tz.
2870 self.assertRaises(TypeError, dt.astimezone, None)
2871 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002872 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002873 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002874 self.assertEqual(x.date(), dt.date())
2875 self.assertEqual(x.time(), dt.time())
2876
2877 # Replacing with different tzinfo does adjust.
2878 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002879 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002880 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2881 expected = dt - dt.utcoffset() # in effect, convert to UTC
2882 expected += fm5h.utcoffset(dt) # and from there to local time
2883 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2884 self.assertEqual(got.date(), expected.date())
2885 self.assertEqual(got.time(), expected.time())
2886 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002887 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002888 self.assertEqual(got, expected)
2889
Tim Peters4c0db782002-12-26 05:01:19 +00002890 def test_aware_subtract(self):
2891 cls = self.theclass
2892
Tim Peters60c76e42002-12-27 00:41:11 +00002893 # Ensure that utcoffset() is ignored when the operands have the
2894 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002895 class OperandDependentOffset(tzinfo):
2896 def utcoffset(self, t):
2897 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002898 # d0 and d1 equal after adjustment
2899 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002900 else:
Tim Peters397301e2003-01-02 21:28:08 +00002901 # d2 off in the weeds
2902 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002903
2904 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2905 d0 = base.replace(minute=3)
2906 d1 = base.replace(minute=9)
2907 d2 = base.replace(minute=11)
2908 for x in d0, d1, d2:
2909 for y in d0, d1, d2:
2910 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002911 expected = timedelta(minutes=x.minute - y.minute)
2912 self.assertEqual(got, expected)
2913
2914 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2915 # ignored.
2916 base = cls(8, 9, 10, 11, 12, 13, 14)
2917 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2918 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2919 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2920 for x in d0, d1, d2:
2921 for y in d0, d1, d2:
2922 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002923 if (x is d0 or x is d1) and (y is d0 or y is d1):
2924 expected = timedelta(0)
2925 elif x is y is d2:
2926 expected = timedelta(0)
2927 elif x is d2:
2928 expected = timedelta(minutes=(11-59)-0)
2929 else:
2930 assert y is d2
2931 expected = timedelta(minutes=0-(11-59))
2932 self.assertEqual(got, expected)
2933
Tim Peters60c76e42002-12-27 00:41:11 +00002934 def test_mixed_compare(self):
2935 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002936 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002937 self.assertEqual(t1, t2)
2938 t2 = t2.replace(tzinfo=None)
2939 self.assertEqual(t1, t2)
2940 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2941 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002942 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2943 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002944
Tim Peters0bf60bd2003-01-08 20:40:01 +00002945 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002946 class Varies(tzinfo):
2947 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002948 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002949 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002950 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002951 return self.offset
2952
2953 v = Varies()
2954 t1 = t2.replace(tzinfo=v)
2955 t2 = t2.replace(tzinfo=v)
2956 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2957 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2958 self.assertEqual(t1, t2)
2959
2960 # But if they're not identical, it isn't ignored.
2961 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002962 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002963
Tim Petersa98924a2003-05-17 05:55:19 +00002964 def test_subclass_datetimetz(self):
2965
2966 class C(self.theclass):
2967 theAnswer = 42
2968
2969 def __new__(cls, *args, **kws):
2970 temp = kws.copy()
2971 extra = temp.pop('extra')
2972 result = self.theclass.__new__(cls, *args, **temp)
2973 result.extra = extra
2974 return result
2975
2976 def newmeth(self, start):
2977 return start + self.hour + self.year
2978
2979 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2980
2981 dt1 = self.theclass(*args)
2982 dt2 = C(*args, **{'extra': 7})
2983
2984 self.assertEqual(dt2.__class__, C)
2985 self.assertEqual(dt2.theAnswer, 42)
2986 self.assertEqual(dt2.extra, 7)
2987 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2988 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2989
Tim Peters621818b2002-12-29 23:44:49 +00002990# Pain to set up DST-aware tzinfo classes.
2991
2992def first_sunday_on_or_after(dt):
2993 days_to_go = 6 - dt.weekday()
2994 if days_to_go:
2995 dt += timedelta(days_to_go)
2996 return dt
2997
2998ZERO = timedelta(0)
2999HOUR = timedelta(hours=1)
3000DAY = timedelta(days=1)
3001# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3002DSTSTART = datetime(1, 4, 1, 2)
3003# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003004# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3005# being standard time on that day, there is no spelling in local time of
3006# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3007DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003008
3009class USTimeZone(tzinfo):
3010
3011 def __init__(self, hours, reprname, stdname, dstname):
3012 self.stdoffset = timedelta(hours=hours)
3013 self.reprname = reprname
3014 self.stdname = stdname
3015 self.dstname = dstname
3016
3017 def __repr__(self):
3018 return self.reprname
3019
3020 def tzname(self, dt):
3021 if self.dst(dt):
3022 return self.dstname
3023 else:
3024 return self.stdname
3025
3026 def utcoffset(self, dt):
3027 return self.stdoffset + self.dst(dt)
3028
3029 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003030 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003031 # An exception instead may be sensible here, in one or more of
3032 # the cases.
3033 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003034 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003035
3036 # Find first Sunday in April.
3037 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3038 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3039
3040 # Find last Sunday in October.
3041 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3042 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3043
Tim Peters621818b2002-12-29 23:44:49 +00003044 # Can't compare naive to aware objects, so strip the timezone from
3045 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003046 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003047 return HOUR
3048 else:
3049 return ZERO
3050
Tim Peters521fc152002-12-31 17:36:56 +00003051Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3052Central = USTimeZone(-6, "Central", "CST", "CDT")
3053Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3054Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003055utc_real = FixedOffset(0, "UTC", 0)
3056# For better test coverage, we want another flavor of UTC that's west of
3057# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003058utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003059
3060class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003061 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003062 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003063 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003064
Tim Peters0bf60bd2003-01-08 20:40:01 +00003065 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003066
Tim Peters521fc152002-12-31 17:36:56 +00003067 # Check a time that's inside DST.
3068 def checkinside(self, dt, tz, utc, dston, dstoff):
3069 self.assertEqual(dt.dst(), HOUR)
3070
3071 # Conversion to our own timezone is always an identity.
3072 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003073
3074 asutc = dt.astimezone(utc)
3075 there_and_back = asutc.astimezone(tz)
3076
3077 # Conversion to UTC and back isn't always an identity here,
3078 # because there are redundant spellings (in local time) of
3079 # UTC time when DST begins: the clock jumps from 1:59:59
3080 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3081 # make sense then. The classes above treat 2:MM:SS as
3082 # daylight time then (it's "after 2am"), really an alias
3083 # for 1:MM:SS standard time. The latter form is what
3084 # conversion back from UTC produces.
3085 if dt.date() == dston.date() and dt.hour == 2:
3086 # We're in the redundant hour, and coming back from
3087 # UTC gives the 1:MM:SS standard-time spelling.
3088 self.assertEqual(there_and_back + HOUR, dt)
3089 # Although during was considered to be in daylight
3090 # time, there_and_back is not.
3091 self.assertEqual(there_and_back.dst(), ZERO)
3092 # They're the same times in UTC.
3093 self.assertEqual(there_and_back.astimezone(utc),
3094 dt.astimezone(utc))
3095 else:
3096 # We're not in the redundant hour.
3097 self.assertEqual(dt, there_and_back)
3098
Tim Peters327098a2003-01-20 22:54:38 +00003099 # Because we have a redundant spelling when DST begins, there is
3100 # (unforunately) an hour when DST ends that can't be spelled at all in
3101 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3102 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3103 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3104 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3105 # expressed in local time. Nevertheless, we want conversion back
3106 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003107 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003108 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003109 if dt.date() == dstoff.date() and dt.hour == 0:
3110 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003111 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003112 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3113 nexthour_utc += HOUR
3114 nexthour_tz = nexthour_utc.astimezone(tz)
3115 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003116 else:
Tim Peters327098a2003-01-20 22:54:38 +00003117 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003118
3119 # Check a time that's outside DST.
3120 def checkoutside(self, dt, tz, utc):
3121 self.assertEqual(dt.dst(), ZERO)
3122
3123 # Conversion to our own timezone is always an identity.
3124 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003125
3126 # Converting to UTC and back is an identity too.
3127 asutc = dt.astimezone(utc)
3128 there_and_back = asutc.astimezone(tz)
3129 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003130
Tim Peters1024bf82002-12-30 17:09:40 +00003131 def convert_between_tz_and_utc(self, tz, utc):
3132 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003133 # Because 1:MM on the day DST ends is taken as being standard time,
3134 # there is no spelling in tz for the last hour of daylight time.
3135 # For purposes of the test, the last hour of DST is 0:MM, which is
3136 # taken as being daylight time (and 1:MM is taken as being standard
3137 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003138 dstoff = self.dstoff.replace(tzinfo=tz)
3139 for delta in (timedelta(weeks=13),
3140 DAY,
3141 HOUR,
3142 timedelta(minutes=1),
3143 timedelta(microseconds=1)):
3144
Tim Peters521fc152002-12-31 17:36:56 +00003145 self.checkinside(dston, tz, utc, dston, dstoff)
3146 for during in dston + delta, dstoff - delta:
3147 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003148
Tim Peters521fc152002-12-31 17:36:56 +00003149 self.checkoutside(dstoff, tz, utc)
3150 for outside in dston - delta, dstoff + delta:
3151 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003152
Tim Peters621818b2002-12-29 23:44:49 +00003153 def test_easy(self):
3154 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003155 self.convert_between_tz_and_utc(Eastern, utc_real)
3156 self.convert_between_tz_and_utc(Pacific, utc_real)
3157 self.convert_between_tz_and_utc(Eastern, utc_fake)
3158 self.convert_between_tz_and_utc(Pacific, utc_fake)
3159 # The next is really dancing near the edge. It works because
3160 # Pacific and Eastern are far enough apart that their "problem
3161 # hours" don't overlap.
3162 self.convert_between_tz_and_utc(Eastern, Pacific)
3163 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003164 # OTOH, these fail! Don't enable them. The difficulty is that
3165 # the edge case tests assume that every hour is representable in
3166 # the "utc" class. This is always true for a fixed-offset tzinfo
3167 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3168 # For these adjacent DST-aware time zones, the range of time offsets
3169 # tested ends up creating hours in the one that aren't representable
3170 # in the other. For the same reason, we would see failures in the
3171 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3172 # offset deltas in convert_between_tz_and_utc().
3173 #
3174 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3175 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003176
Tim Petersf3615152003-01-01 21:51:37 +00003177 def test_tricky(self):
3178 # 22:00 on day before daylight starts.
3179 fourback = self.dston - timedelta(hours=4)
3180 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003181 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003182 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3183 # 2", we should get the 3 spelling.
3184 # If we plug 22:00 the day before into Eastern, it "looks like std
3185 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3186 # to 22:00 lands on 2:00, which makes no sense in local time (the
3187 # local clock jumps from 1 to 3). The point here is to make sure we
3188 # get the 3 spelling.
3189 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003190 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003191 self.assertEqual(expected, got)
3192
3193 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3194 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003195 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003196 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3197 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3198 # spelling.
3199 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003200 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003201 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003202
Tim Petersadf64202003-01-04 06:03:15 +00003203 # Now on the day DST ends, we want "repeat an hour" behavior.
3204 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3205 # EST 23:MM 0:MM 1:MM 2:MM
3206 # EDT 0:MM 1:MM 2:MM 3:MM
3207 # wall 0:MM 1:MM 1:MM 2:MM against these
3208 for utc in utc_real, utc_fake:
3209 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003210 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003211 # Convert that to UTC.
3212 first_std_hour -= tz.utcoffset(None)
3213 # Adjust for possibly fake UTC.
3214 asutc = first_std_hour + utc.utcoffset(None)
3215 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3216 # tz=Eastern.
3217 asutcbase = asutc.replace(tzinfo=utc)
3218 for tzhour in (0, 1, 1, 2):
3219 expectedbase = self.dstoff.replace(hour=tzhour)
3220 for minute in 0, 30, 59:
3221 expected = expectedbase.replace(minute=minute)
3222 asutc = asutcbase.replace(minute=minute)
3223 astz = asutc.astimezone(tz)
3224 self.assertEqual(astz.replace(tzinfo=None), expected)
3225 asutcbase += HOUR
3226
3227
Tim Peters710fb152003-01-02 19:35:54 +00003228 def test_bogus_dst(self):
3229 class ok(tzinfo):
3230 def utcoffset(self, dt): return HOUR
3231 def dst(self, dt): return HOUR
3232
3233 now = self.theclass.now().replace(tzinfo=utc_real)
3234 # Doesn't blow up.
3235 now.astimezone(ok())
3236
3237 # Does blow up.
3238 class notok(ok):
3239 def dst(self, dt): return None
3240 self.assertRaises(ValueError, now.astimezone, notok())
3241
Tim Peters52dcce22003-01-23 16:36:11 +00003242 def test_fromutc(self):
3243 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3244 now = datetime.utcnow().replace(tzinfo=utc_real)
3245 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3246 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3247 enow = Eastern.fromutc(now) # doesn't blow up
3248 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3249 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3250 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3251
3252 # Always converts UTC to standard time.
3253 class FauxUSTimeZone(USTimeZone):
3254 def fromutc(self, dt):
3255 return dt + self.stdoffset
3256 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3257
3258 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3259 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3260 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3261
3262 # Check around DST start.
3263 start = self.dston.replace(hour=4, tzinfo=Eastern)
3264 fstart = start.replace(tzinfo=FEastern)
3265 for wall in 23, 0, 1, 3, 4, 5:
3266 expected = start.replace(hour=wall)
3267 if wall == 23:
3268 expected -= timedelta(days=1)
3269 got = Eastern.fromutc(start)
3270 self.assertEqual(expected, got)
3271
3272 expected = fstart + FEastern.stdoffset
3273 got = FEastern.fromutc(fstart)
3274 self.assertEqual(expected, got)
3275
3276 # Ensure astimezone() calls fromutc() too.
3277 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3278 self.assertEqual(expected, got)
3279
3280 start += HOUR
3281 fstart += HOUR
3282
3283 # Check around DST end.
3284 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3285 fstart = start.replace(tzinfo=FEastern)
3286 for wall in 0, 1, 1, 2, 3, 4:
3287 expected = start.replace(hour=wall)
3288 got = Eastern.fromutc(start)
3289 self.assertEqual(expected, got)
3290
3291 expected = fstart + FEastern.stdoffset
3292 got = FEastern.fromutc(fstart)
3293 self.assertEqual(expected, got)
3294
3295 # Ensure astimezone() calls fromutc() too.
3296 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3297 self.assertEqual(expected, got)
3298
3299 start += HOUR
3300 fstart += HOUR
3301
Tim Peters710fb152003-01-02 19:35:54 +00003302
Tim Peters528ca532004-09-16 01:30:50 +00003303#############################################################################
3304# oddballs
3305
3306class Oddballs(unittest.TestCase):
3307
3308 def test_bug_1028306(self):
3309 # Trying to compare a date to a datetime should act like a mixed-
3310 # type comparison, despite that datetime is a subclass of date.
3311 as_date = date.today()
3312 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003313 self.assertTrue(as_date != as_datetime)
3314 self.assertTrue(as_datetime != as_date)
3315 self.assertTrue(not as_date == as_datetime)
3316 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003317 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3318 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3319 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3320 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3321 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3322 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3323 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3324 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3325
3326 # Neverthelss, comparison should work with the base-class (date)
3327 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003328 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003329 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003330 as_different = as_datetime.replace(day= different_day)
3331 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003332
3333 # And date should compare with other subclasses of date. If a
3334 # subclass wants to stop this, it's up to the subclass to do so.
3335 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3336 self.assertEqual(as_date, date_sc)
3337 self.assertEqual(date_sc, as_date)
3338
3339 # Ditto for datetimes.
3340 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3341 as_date.day, 0, 0, 0)
3342 self.assertEqual(as_datetime, datetime_sc)
3343 self.assertEqual(datetime_sc, as_datetime)
3344
Tim Peters2a799bf2002-12-16 20:18:38 +00003345def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003346 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003347
3348if __name__ == "__main__":
3349 test_main()