blob: 658b390fc1ed4fe4490c61b06fa2020f095c5ec8 [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")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000082 self.assertTrue(isinstance(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")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000091 self.assertTrue(isinstance(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')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000112 self.assertTrue(isinstance(orig, tzinfo))
113 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)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000119 self.assertTrue(isinstance(derived, tzinfo))
120 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):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000385 self.assertTrue(isinstance(timedelta.min, timedelta))
386 self.assertTrue(isinstance(timedelta.max, timedelta))
387 self.assertTrue(isinstance(timedelta.resolution, timedelta))
388 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):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000898 self.assertTrue(isinstance(self.theclass.min, self.theclass))
899 self.assertTrue(isinstance(self.theclass.max, self.theclass))
900 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
901 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):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001884 self.assertTrue(isinstance(self.theclass.min, self.theclass))
1885 self.assertTrue(isinstance(self.theclass.max, self.theclass))
1886 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
1887 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)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002254 self.assertTrue(isinstance(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)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002481 self.assertTrue(isinstance(derived.tzinfo,
Tim Peters96940c92003-01-31 21:55:33 +00002482 PicklableFixedOffset))
2483 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2484 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002485
2486 def test_extreme_hashes(self):
2487 # If an attempt is made to hash these via subtracting the offset
2488 # then hashing a datetime object, OverflowError results. The
2489 # Python implementation used to blow up here.
2490 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2491 hash(t)
2492 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2493 tzinfo=FixedOffset(-1439, ""))
2494 hash(t)
2495
2496 # OTOH, an OOB offset should blow up.
2497 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2498 self.assertRaises(ValueError, hash, t)
2499
2500 def test_zones(self):
2501 est = FixedOffset(-300, "EST")
2502 utc = FixedOffset(0, "UTC")
2503 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002504 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2505 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2506 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002507 self.assertEqual(t1.tzinfo, est)
2508 self.assertEqual(t2.tzinfo, utc)
2509 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002510 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2511 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2512 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002513 self.assertEqual(t1.tzname(), "EST")
2514 self.assertEqual(t2.tzname(), "UTC")
2515 self.assertEqual(t3.tzname(), "MET")
2516 self.assertEqual(hash(t1), hash(t2))
2517 self.assertEqual(hash(t1), hash(t3))
2518 self.assertEqual(hash(t2), hash(t3))
2519 self.assertEqual(t1, t2)
2520 self.assertEqual(t1, t3)
2521 self.assertEqual(t2, t3)
2522 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2523 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2524 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002525 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002526 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2527 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2528 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2529
2530 def test_combine(self):
2531 met = FixedOffset(60, "MET")
2532 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002533 tz = time(18, 45, 3, 1234, tzinfo=met)
2534 dt = datetime.combine(d, tz)
2535 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002536 tzinfo=met))
2537
2538 def test_extract(self):
2539 met = FixedOffset(60, "MET")
2540 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2541 self.assertEqual(dt.date(), date(2002, 3, 4))
2542 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002543 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002544
2545 def test_tz_aware_arithmetic(self):
2546 import random
2547
2548 now = self.theclass.now()
2549 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002550 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002551 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002552 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002553 self.assertEqual(nowaware.timetz(), timeaware)
2554
2555 # Can't mix aware and non-aware.
2556 self.assertRaises(TypeError, lambda: now - nowaware)
2557 self.assertRaises(TypeError, lambda: nowaware - now)
2558
Tim Peters0bf60bd2003-01-08 20:40:01 +00002559 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002560 self.assertRaises(TypeError, lambda: now + nowaware)
2561 self.assertRaises(TypeError, lambda: nowaware + now)
2562 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2563
2564 # Subtracting should yield 0.
2565 self.assertEqual(now - now, timedelta(0))
2566 self.assertEqual(nowaware - nowaware, timedelta(0))
2567
2568 # Adding a delta should preserve tzinfo.
2569 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2570 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002571 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002572 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002573 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002574 self.assertEqual(nowawareplus, nowawareplus2)
2575
2576 # that - delta should be what we started with, and that - what we
2577 # started with should be delta.
2578 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002579 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002580 self.assertEqual(nowaware, diff)
2581 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2582 self.assertEqual(nowawareplus - nowaware, delta)
2583
2584 # Make up a random timezone.
2585 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002586 # Attach it to nowawareplus.
2587 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002588 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002589 # Make sure the difference takes the timezone adjustments into account.
2590 got = nowaware - nowawareplus
2591 # Expected: (nowaware base - nowaware offset) -
2592 # (nowawareplus base - nowawareplus offset) =
2593 # (nowaware base - nowawareplus base) +
2594 # (nowawareplus offset - nowaware offset) =
2595 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002596 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002597 self.assertEqual(got, expected)
2598
2599 # Try max possible difference.
2600 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2601 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2602 tzinfo=FixedOffset(-1439, "max"))
2603 maxdiff = max - min
2604 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2605 timedelta(minutes=2*1439))
2606
2607 def test_tzinfo_now(self):
2608 meth = self.theclass.now
2609 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2610 base = meth()
2611 # Try with and without naming the keyword.
2612 off42 = FixedOffset(42, "42")
2613 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002614 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002615 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002616 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002617 # Bad argument with and w/o naming the keyword.
2618 self.assertRaises(TypeError, meth, 16)
2619 self.assertRaises(TypeError, meth, tzinfo=16)
2620 # Bad keyword name.
2621 self.assertRaises(TypeError, meth, tinfo=off42)
2622 # Too many args.
2623 self.assertRaises(TypeError, meth, off42, off42)
2624
Tim Peters10cadce2003-01-23 19:58:02 +00002625 # We don't know which time zone we're in, and don't have a tzinfo
2626 # class to represent it, so seeing whether a tz argument actually
2627 # does a conversion is tricky.
2628 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2629 utc = FixedOffset(0, "utc", 0)
2630 for dummy in range(3):
2631 now = datetime.now(weirdtz)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002632 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002633 utcnow = datetime.utcnow().replace(tzinfo=utc)
2634 now2 = utcnow.astimezone(weirdtz)
2635 if abs(now - now2) < timedelta(seconds=30):
2636 break
2637 # Else the code is broken, or more than 30 seconds passed between
2638 # calls; assuming the latter, just try again.
2639 else:
2640 # Three strikes and we're out.
2641 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2642
Tim Peters2a799bf2002-12-16 20:18:38 +00002643 def test_tzinfo_fromtimestamp(self):
2644 import time
2645 meth = self.theclass.fromtimestamp
2646 ts = time.time()
2647 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2648 base = meth(ts)
2649 # Try with and without naming the keyword.
2650 off42 = FixedOffset(42, "42")
2651 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002652 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002653 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002654 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002655 # Bad argument with and w/o naming the keyword.
2656 self.assertRaises(TypeError, meth, ts, 16)
2657 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2658 # Bad keyword name.
2659 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2660 # Too many args.
2661 self.assertRaises(TypeError, meth, ts, off42, off42)
2662 # Too few args.
2663 self.assertRaises(TypeError, meth)
2664
Tim Peters2a44a8d2003-01-23 20:53:10 +00002665 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002666 timestamp = 1000000000
2667 utcdatetime = datetime.utcfromtimestamp(timestamp)
2668 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2669 # But on some flavor of Mac, it's nowhere near that. So we can't have
2670 # any idea here what time that actually is, we can only test that
2671 # relative changes match.
2672 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2673 tz = FixedOffset(utcoffset, "tz", 0)
2674 expected = utcdatetime + utcoffset
2675 got = datetime.fromtimestamp(timestamp, tz)
2676 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002677
Tim Peters2a799bf2002-12-16 20:18:38 +00002678 def test_tzinfo_utcnow(self):
2679 meth = self.theclass.utcnow
2680 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2681 base = meth()
2682 # Try with and without naming the keyword; for whatever reason,
2683 # utcnow() doesn't accept a tzinfo argument.
2684 off42 = FixedOffset(42, "42")
2685 self.assertRaises(TypeError, meth, off42)
2686 self.assertRaises(TypeError, meth, tzinfo=off42)
2687
2688 def test_tzinfo_utcfromtimestamp(self):
2689 import time
2690 meth = self.theclass.utcfromtimestamp
2691 ts = time.time()
2692 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2693 base = meth(ts)
2694 # Try with and without naming the keyword; for whatever reason,
2695 # utcfromtimestamp() doesn't accept a tzinfo argument.
2696 off42 = FixedOffset(42, "42")
2697 self.assertRaises(TypeError, meth, ts, off42)
2698 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2699
2700 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002701 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002702 # DST flag.
2703 class DST(tzinfo):
2704 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002705 if isinstance(dstvalue, int):
2706 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002707 self.dstvalue = dstvalue
2708 def dst(self, dt):
2709 return self.dstvalue
2710
2711 cls = self.theclass
2712 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2713 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2714 t = d.timetuple()
2715 self.assertEqual(1, t.tm_year)
2716 self.assertEqual(1, t.tm_mon)
2717 self.assertEqual(1, t.tm_mday)
2718 self.assertEqual(10, t.tm_hour)
2719 self.assertEqual(20, t.tm_min)
2720 self.assertEqual(30, t.tm_sec)
2721 self.assertEqual(0, t.tm_wday)
2722 self.assertEqual(1, t.tm_yday)
2723 self.assertEqual(flag, t.tm_isdst)
2724
2725 # dst() returns wrong type.
2726 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2727
2728 # dst() at the edge.
2729 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2730 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2731
2732 # dst() out of range.
2733 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2734 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2735
2736 def test_utctimetuple(self):
2737 class DST(tzinfo):
2738 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002739 if isinstance(dstvalue, int):
2740 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002741 self.dstvalue = dstvalue
2742 def dst(self, dt):
2743 return self.dstvalue
2744
2745 cls = self.theclass
2746 # This can't work: DST didn't implement utcoffset.
2747 self.assertRaises(NotImplementedError,
2748 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2749
2750 class UOFS(DST):
2751 def __init__(self, uofs, dofs=None):
2752 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002753 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002754 def utcoffset(self, dt):
2755 return self.uofs
2756
2757 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2758 # in effect for a UTC time.
2759 for dstvalue in -33, 33, 0, None:
2760 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2761 t = d.utctimetuple()
2762 self.assertEqual(d.year, t.tm_year)
2763 self.assertEqual(d.month, t.tm_mon)
2764 self.assertEqual(d.day, t.tm_mday)
2765 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2766 self.assertEqual(13, t.tm_min)
2767 self.assertEqual(d.second, t.tm_sec)
2768 self.assertEqual(d.weekday(), t.tm_wday)
2769 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2770 t.tm_yday)
2771 self.assertEqual(0, t.tm_isdst)
2772
2773 # At the edges, UTC adjustment can normalize into years out-of-range
2774 # for a datetime object. Ensure that a correct timetuple is
2775 # created anyway.
2776 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2777 # That goes back 1 minute less than a full day.
2778 t = tiny.utctimetuple()
2779 self.assertEqual(t.tm_year, MINYEAR-1)
2780 self.assertEqual(t.tm_mon, 12)
2781 self.assertEqual(t.tm_mday, 31)
2782 self.assertEqual(t.tm_hour, 0)
2783 self.assertEqual(t.tm_min, 1)
2784 self.assertEqual(t.tm_sec, 37)
2785 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2786 self.assertEqual(t.tm_isdst, 0)
2787
2788 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2789 # That goes forward 1 minute less than a full day.
2790 t = huge.utctimetuple()
2791 self.assertEqual(t.tm_year, MAXYEAR+1)
2792 self.assertEqual(t.tm_mon, 1)
2793 self.assertEqual(t.tm_mday, 1)
2794 self.assertEqual(t.tm_hour, 23)
2795 self.assertEqual(t.tm_min, 58)
2796 self.assertEqual(t.tm_sec, 37)
2797 self.assertEqual(t.tm_yday, 1)
2798 self.assertEqual(t.tm_isdst, 0)
2799
2800 def test_tzinfo_isoformat(self):
2801 zero = FixedOffset(0, "+00:00")
2802 plus = FixedOffset(220, "+03:40")
2803 minus = FixedOffset(-231, "-03:51")
2804 unknown = FixedOffset(None, "")
2805
2806 cls = self.theclass
2807 datestr = '0001-02-03'
2808 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002809 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002810 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2811 timestr = '04:05:59' + (us and '.987001' or '')
2812 ofsstr = ofs is not None and d.tzname() or ''
2813 tailstr = timestr + ofsstr
2814 iso = d.isoformat()
2815 self.assertEqual(iso, datestr + 'T' + tailstr)
2816 self.assertEqual(iso, d.isoformat('T'))
2817 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002818 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002819 self.assertEqual(str(d), datestr + ' ' + tailstr)
2820
Tim Peters12bf3392002-12-24 05:41:27 +00002821 def test_replace(self):
2822 cls = self.theclass
2823 z100 = FixedOffset(100, "+100")
2824 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2825 args = [1, 2, 3, 4, 5, 6, 7, z100]
2826 base = cls(*args)
2827 self.assertEqual(base, base.replace())
2828
2829 i = 0
2830 for name, newval in (("year", 2),
2831 ("month", 3),
2832 ("day", 4),
2833 ("hour", 5),
2834 ("minute", 6),
2835 ("second", 7),
2836 ("microsecond", 8),
2837 ("tzinfo", zm200)):
2838 newargs = args[:]
2839 newargs[i] = newval
2840 expected = cls(*newargs)
2841 got = base.replace(**{name: newval})
2842 self.assertEqual(expected, got)
2843 i += 1
2844
2845 # Ensure we can get rid of a tzinfo.
2846 self.assertEqual(base.tzname(), "+100")
2847 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002848 self.assertTrue(base2.tzinfo is None)
2849 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002850
2851 # Ensure we can add one.
2852 base3 = base2.replace(tzinfo=z100)
2853 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002854 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002855
2856 # Out of bounds.
2857 base = cls(2000, 2, 29)
2858 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002859
Tim Peters80475bb2002-12-25 07:40:55 +00002860 def test_more_astimezone(self):
2861 # The inherited test_astimezone covered some trivial and error cases.
2862 fnone = FixedOffset(None, "None")
2863 f44m = FixedOffset(44, "44")
2864 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2865
Tim Peters10cadce2003-01-23 19:58:02 +00002866 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002867 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002868 # Replacing with degenerate tzinfo raises an exception.
2869 self.assertRaises(ValueError, dt.astimezone, fnone)
2870 # Ditto with None tz.
2871 self.assertRaises(TypeError, dt.astimezone, None)
2872 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002873 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002874 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002875 self.assertEqual(x.date(), dt.date())
2876 self.assertEqual(x.time(), dt.time())
2877
2878 # Replacing with different tzinfo does adjust.
2879 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002880 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002881 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2882 expected = dt - dt.utcoffset() # in effect, convert to UTC
2883 expected += fm5h.utcoffset(dt) # and from there to local time
2884 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2885 self.assertEqual(got.date(), expected.date())
2886 self.assertEqual(got.time(), expected.time())
2887 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002888 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002889 self.assertEqual(got, expected)
2890
Tim Peters4c0db782002-12-26 05:01:19 +00002891 def test_aware_subtract(self):
2892 cls = self.theclass
2893
Tim Peters60c76e42002-12-27 00:41:11 +00002894 # Ensure that utcoffset() is ignored when the operands have the
2895 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002896 class OperandDependentOffset(tzinfo):
2897 def utcoffset(self, t):
2898 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002899 # d0 and d1 equal after adjustment
2900 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002901 else:
Tim Peters397301e2003-01-02 21:28:08 +00002902 # d2 off in the weeds
2903 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002904
2905 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2906 d0 = base.replace(minute=3)
2907 d1 = base.replace(minute=9)
2908 d2 = base.replace(minute=11)
2909 for x in d0, d1, d2:
2910 for y in d0, d1, d2:
2911 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002912 expected = timedelta(minutes=x.minute - y.minute)
2913 self.assertEqual(got, expected)
2914
2915 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2916 # ignored.
2917 base = cls(8, 9, 10, 11, 12, 13, 14)
2918 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2919 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2920 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2921 for x in d0, d1, d2:
2922 for y in d0, d1, d2:
2923 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002924 if (x is d0 or x is d1) and (y is d0 or y is d1):
2925 expected = timedelta(0)
2926 elif x is y is d2:
2927 expected = timedelta(0)
2928 elif x is d2:
2929 expected = timedelta(minutes=(11-59)-0)
2930 else:
2931 assert y is d2
2932 expected = timedelta(minutes=0-(11-59))
2933 self.assertEqual(got, expected)
2934
Tim Peters60c76e42002-12-27 00:41:11 +00002935 def test_mixed_compare(self):
2936 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002937 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002938 self.assertEqual(t1, t2)
2939 t2 = t2.replace(tzinfo=None)
2940 self.assertEqual(t1, t2)
2941 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2942 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002943 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2944 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002945
Tim Peters0bf60bd2003-01-08 20:40:01 +00002946 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002947 class Varies(tzinfo):
2948 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002949 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002950 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002951 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002952 return self.offset
2953
2954 v = Varies()
2955 t1 = t2.replace(tzinfo=v)
2956 t2 = t2.replace(tzinfo=v)
2957 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2958 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2959 self.assertEqual(t1, t2)
2960
2961 # But if they're not identical, it isn't ignored.
2962 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002963 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002964
Tim Petersa98924a2003-05-17 05:55:19 +00002965 def test_subclass_datetimetz(self):
2966
2967 class C(self.theclass):
2968 theAnswer = 42
2969
2970 def __new__(cls, *args, **kws):
2971 temp = kws.copy()
2972 extra = temp.pop('extra')
2973 result = self.theclass.__new__(cls, *args, **temp)
2974 result.extra = extra
2975 return result
2976
2977 def newmeth(self, start):
2978 return start + self.hour + self.year
2979
2980 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2981
2982 dt1 = self.theclass(*args)
2983 dt2 = C(*args, **{'extra': 7})
2984
2985 self.assertEqual(dt2.__class__, C)
2986 self.assertEqual(dt2.theAnswer, 42)
2987 self.assertEqual(dt2.extra, 7)
2988 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2989 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2990
Tim Peters621818b2002-12-29 23:44:49 +00002991# Pain to set up DST-aware tzinfo classes.
2992
2993def first_sunday_on_or_after(dt):
2994 days_to_go = 6 - dt.weekday()
2995 if days_to_go:
2996 dt += timedelta(days_to_go)
2997 return dt
2998
2999ZERO = timedelta(0)
3000HOUR = timedelta(hours=1)
3001DAY = timedelta(days=1)
3002# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3003DSTSTART = datetime(1, 4, 1, 2)
3004# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003005# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3006# being standard time on that day, there is no spelling in local time of
3007# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3008DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003009
3010class USTimeZone(tzinfo):
3011
3012 def __init__(self, hours, reprname, stdname, dstname):
3013 self.stdoffset = timedelta(hours=hours)
3014 self.reprname = reprname
3015 self.stdname = stdname
3016 self.dstname = dstname
3017
3018 def __repr__(self):
3019 return self.reprname
3020
3021 def tzname(self, dt):
3022 if self.dst(dt):
3023 return self.dstname
3024 else:
3025 return self.stdname
3026
3027 def utcoffset(self, dt):
3028 return self.stdoffset + self.dst(dt)
3029
3030 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003031 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003032 # An exception instead may be sensible here, in one or more of
3033 # the cases.
3034 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003035 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003036
3037 # Find first Sunday in April.
3038 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3039 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3040
3041 # Find last Sunday in October.
3042 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3043 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3044
Tim Peters621818b2002-12-29 23:44:49 +00003045 # Can't compare naive to aware objects, so strip the timezone from
3046 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003047 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003048 return HOUR
3049 else:
3050 return ZERO
3051
Tim Peters521fc152002-12-31 17:36:56 +00003052Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3053Central = USTimeZone(-6, "Central", "CST", "CDT")
3054Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3055Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003056utc_real = FixedOffset(0, "UTC", 0)
3057# For better test coverage, we want another flavor of UTC that's west of
3058# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003059utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003060
3061class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003062 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003063 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003064 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003065
Tim Peters0bf60bd2003-01-08 20:40:01 +00003066 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003067
Tim Peters521fc152002-12-31 17:36:56 +00003068 # Check a time that's inside DST.
3069 def checkinside(self, dt, tz, utc, dston, dstoff):
3070 self.assertEqual(dt.dst(), HOUR)
3071
3072 # Conversion to our own timezone is always an identity.
3073 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003074
3075 asutc = dt.astimezone(utc)
3076 there_and_back = asutc.astimezone(tz)
3077
3078 # Conversion to UTC and back isn't always an identity here,
3079 # because there are redundant spellings (in local time) of
3080 # UTC time when DST begins: the clock jumps from 1:59:59
3081 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3082 # make sense then. The classes above treat 2:MM:SS as
3083 # daylight time then (it's "after 2am"), really an alias
3084 # for 1:MM:SS standard time. The latter form is what
3085 # conversion back from UTC produces.
3086 if dt.date() == dston.date() and dt.hour == 2:
3087 # We're in the redundant hour, and coming back from
3088 # UTC gives the 1:MM:SS standard-time spelling.
3089 self.assertEqual(there_and_back + HOUR, dt)
3090 # Although during was considered to be in daylight
3091 # time, there_and_back is not.
3092 self.assertEqual(there_and_back.dst(), ZERO)
3093 # They're the same times in UTC.
3094 self.assertEqual(there_and_back.astimezone(utc),
3095 dt.astimezone(utc))
3096 else:
3097 # We're not in the redundant hour.
3098 self.assertEqual(dt, there_and_back)
3099
Tim Peters327098a2003-01-20 22:54:38 +00003100 # Because we have a redundant spelling when DST begins, there is
3101 # (unforunately) an hour when DST ends that can't be spelled at all in
3102 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3103 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3104 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3105 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3106 # expressed in local time. Nevertheless, we want conversion back
3107 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003108 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003109 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003110 if dt.date() == dstoff.date() and dt.hour == 0:
3111 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003112 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003113 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3114 nexthour_utc += HOUR
3115 nexthour_tz = nexthour_utc.astimezone(tz)
3116 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003117 else:
Tim Peters327098a2003-01-20 22:54:38 +00003118 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003119
3120 # Check a time that's outside DST.
3121 def checkoutside(self, dt, tz, utc):
3122 self.assertEqual(dt.dst(), ZERO)
3123
3124 # Conversion to our own timezone is always an identity.
3125 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003126
3127 # Converting to UTC and back is an identity too.
3128 asutc = dt.astimezone(utc)
3129 there_and_back = asutc.astimezone(tz)
3130 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003131
Tim Peters1024bf82002-12-30 17:09:40 +00003132 def convert_between_tz_and_utc(self, tz, utc):
3133 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003134 # Because 1:MM on the day DST ends is taken as being standard time,
3135 # there is no spelling in tz for the last hour of daylight time.
3136 # For purposes of the test, the last hour of DST is 0:MM, which is
3137 # taken as being daylight time (and 1:MM is taken as being standard
3138 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003139 dstoff = self.dstoff.replace(tzinfo=tz)
3140 for delta in (timedelta(weeks=13),
3141 DAY,
3142 HOUR,
3143 timedelta(minutes=1),
3144 timedelta(microseconds=1)):
3145
Tim Peters521fc152002-12-31 17:36:56 +00003146 self.checkinside(dston, tz, utc, dston, dstoff)
3147 for during in dston + delta, dstoff - delta:
3148 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003149
Tim Peters521fc152002-12-31 17:36:56 +00003150 self.checkoutside(dstoff, tz, utc)
3151 for outside in dston - delta, dstoff + delta:
3152 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003153
Tim Peters621818b2002-12-29 23:44:49 +00003154 def test_easy(self):
3155 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003156 self.convert_between_tz_and_utc(Eastern, utc_real)
3157 self.convert_between_tz_and_utc(Pacific, utc_real)
3158 self.convert_between_tz_and_utc(Eastern, utc_fake)
3159 self.convert_between_tz_and_utc(Pacific, utc_fake)
3160 # The next is really dancing near the edge. It works because
3161 # Pacific and Eastern are far enough apart that their "problem
3162 # hours" don't overlap.
3163 self.convert_between_tz_and_utc(Eastern, Pacific)
3164 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003165 # OTOH, these fail! Don't enable them. The difficulty is that
3166 # the edge case tests assume that every hour is representable in
3167 # the "utc" class. This is always true for a fixed-offset tzinfo
3168 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3169 # For these adjacent DST-aware time zones, the range of time offsets
3170 # tested ends up creating hours in the one that aren't representable
3171 # in the other. For the same reason, we would see failures in the
3172 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3173 # offset deltas in convert_between_tz_and_utc().
3174 #
3175 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3176 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003177
Tim Petersf3615152003-01-01 21:51:37 +00003178 def test_tricky(self):
3179 # 22:00 on day before daylight starts.
3180 fourback = self.dston - timedelta(hours=4)
3181 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003182 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003183 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3184 # 2", we should get the 3 spelling.
3185 # If we plug 22:00 the day before into Eastern, it "looks like std
3186 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3187 # to 22:00 lands on 2:00, which makes no sense in local time (the
3188 # local clock jumps from 1 to 3). The point here is to make sure we
3189 # get the 3 spelling.
3190 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003191 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003192 self.assertEqual(expected, got)
3193
3194 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3195 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003196 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003197 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3198 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3199 # spelling.
3200 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003201 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003202 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003203
Tim Petersadf64202003-01-04 06:03:15 +00003204 # Now on the day DST ends, we want "repeat an hour" behavior.
3205 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3206 # EST 23:MM 0:MM 1:MM 2:MM
3207 # EDT 0:MM 1:MM 2:MM 3:MM
3208 # wall 0:MM 1:MM 1:MM 2:MM against these
3209 for utc in utc_real, utc_fake:
3210 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003211 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003212 # Convert that to UTC.
3213 first_std_hour -= tz.utcoffset(None)
3214 # Adjust for possibly fake UTC.
3215 asutc = first_std_hour + utc.utcoffset(None)
3216 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3217 # tz=Eastern.
3218 asutcbase = asutc.replace(tzinfo=utc)
3219 for tzhour in (0, 1, 1, 2):
3220 expectedbase = self.dstoff.replace(hour=tzhour)
3221 for minute in 0, 30, 59:
3222 expected = expectedbase.replace(minute=minute)
3223 asutc = asutcbase.replace(minute=minute)
3224 astz = asutc.astimezone(tz)
3225 self.assertEqual(astz.replace(tzinfo=None), expected)
3226 asutcbase += HOUR
3227
3228
Tim Peters710fb152003-01-02 19:35:54 +00003229 def test_bogus_dst(self):
3230 class ok(tzinfo):
3231 def utcoffset(self, dt): return HOUR
3232 def dst(self, dt): return HOUR
3233
3234 now = self.theclass.now().replace(tzinfo=utc_real)
3235 # Doesn't blow up.
3236 now.astimezone(ok())
3237
3238 # Does blow up.
3239 class notok(ok):
3240 def dst(self, dt): return None
3241 self.assertRaises(ValueError, now.astimezone, notok())
3242
Tim Peters52dcce22003-01-23 16:36:11 +00003243 def test_fromutc(self):
3244 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3245 now = datetime.utcnow().replace(tzinfo=utc_real)
3246 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3247 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3248 enow = Eastern.fromutc(now) # doesn't blow up
3249 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3250 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3251 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3252
3253 # Always converts UTC to standard time.
3254 class FauxUSTimeZone(USTimeZone):
3255 def fromutc(self, dt):
3256 return dt + self.stdoffset
3257 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3258
3259 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3260 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3261 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3262
3263 # Check around DST start.
3264 start = self.dston.replace(hour=4, tzinfo=Eastern)
3265 fstart = start.replace(tzinfo=FEastern)
3266 for wall in 23, 0, 1, 3, 4, 5:
3267 expected = start.replace(hour=wall)
3268 if wall == 23:
3269 expected -= timedelta(days=1)
3270 got = Eastern.fromutc(start)
3271 self.assertEqual(expected, got)
3272
3273 expected = fstart + FEastern.stdoffset
3274 got = FEastern.fromutc(fstart)
3275 self.assertEqual(expected, got)
3276
3277 # Ensure astimezone() calls fromutc() too.
3278 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3279 self.assertEqual(expected, got)
3280
3281 start += HOUR
3282 fstart += HOUR
3283
3284 # Check around DST end.
3285 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3286 fstart = start.replace(tzinfo=FEastern)
3287 for wall in 0, 1, 1, 2, 3, 4:
3288 expected = start.replace(hour=wall)
3289 got = Eastern.fromutc(start)
3290 self.assertEqual(expected, got)
3291
3292 expected = fstart + FEastern.stdoffset
3293 got = FEastern.fromutc(fstart)
3294 self.assertEqual(expected, got)
3295
3296 # Ensure astimezone() calls fromutc() too.
3297 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3298 self.assertEqual(expected, got)
3299
3300 start += HOUR
3301 fstart += HOUR
3302
Tim Peters710fb152003-01-02 19:35:54 +00003303
Tim Peters528ca532004-09-16 01:30:50 +00003304#############################################################################
3305# oddballs
3306
3307class Oddballs(unittest.TestCase):
3308
3309 def test_bug_1028306(self):
3310 # Trying to compare a date to a datetime should act like a mixed-
3311 # type comparison, despite that datetime is a subclass of date.
3312 as_date = date.today()
3313 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003314 self.assertTrue(as_date != as_datetime)
3315 self.assertTrue(as_datetime != as_date)
3316 self.assertTrue(not as_date == as_datetime)
3317 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003318 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3319 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3320 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3321 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3322 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3323 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3324 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3325 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3326
3327 # Neverthelss, comparison should work with the base-class (date)
3328 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003329 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003330 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003331 as_different = as_datetime.replace(day= different_day)
3332 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003333
3334 # And date should compare with other subclasses of date. If a
3335 # subclass wants to stop this, it's up to the subclass to do so.
3336 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3337 self.assertEqual(as_date, date_sc)
3338 self.assertEqual(date_sc, as_date)
3339
3340 # Ditto for datetimes.
3341 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3342 as_date.day, 0, 0, 0)
3343 self.assertEqual(as_datetime, datetime_sc)
3344 self.assertEqual(datetime_sc, as_datetime)
3345
Tim Peters2a799bf2002-12-16 20:18:38 +00003346def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003347 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003348
3349if __name__ == "__main__":
3350 test_main()