blob: 8bf8420929c94dab84bb523f116bf3fed13c9222 [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.
Guido van Rossume2a383d2007-01-15 16:59:06 +000025OTHERSTUFF = (10, 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 Petersonc9c0f202009-06-30 23:06:06 +0000142 self.assertTrue(me in [1, 20, [], me])
143 self.assertFalse(me not in [1, 20, [], me])
Tim Peters07534a62003-02-07 22:50:28 +0000144
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000145 self.assertTrue([] in [me, 1, 20, []])
146 self.assertFalse([] not in [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000147
148 def test_harmful_mixed_comparison(self):
149 me = self.theclass(1, 1, 1)
150
151 self.assertRaises(TypeError, lambda: me < ())
152 self.assertRaises(TypeError, lambda: me <= ())
153 self.assertRaises(TypeError, lambda: me > ())
154 self.assertRaises(TypeError, lambda: me >= ())
155
156 self.assertRaises(TypeError, lambda: () < me)
157 self.assertRaises(TypeError, lambda: () <= me)
158 self.assertRaises(TypeError, lambda: () > me)
159 self.assertRaises(TypeError, lambda: () >= me)
160
Tim Peters07534a62003-02-07 22:50:28 +0000161#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000162# timedelta tests
163
Guido van Rossumd8faa362007-04-27 19:54:29 +0000164class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000165
166 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000167
168 def test_constructor(self):
169 eq = self.assertEqual
170 td = timedelta
171
172 # Check keyword args to constructor
173 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
174 milliseconds=0, microseconds=0))
175 eq(td(1), td(days=1))
176 eq(td(0, 1), td(seconds=1))
177 eq(td(0, 0, 1), td(microseconds=1))
178 eq(td(weeks=1), td(days=7))
179 eq(td(days=1), td(hours=24))
180 eq(td(hours=1), td(minutes=60))
181 eq(td(minutes=1), td(seconds=60))
182 eq(td(seconds=1), td(milliseconds=1000))
183 eq(td(milliseconds=1), td(microseconds=1000))
184
185 # Check float args to constructor
186 eq(td(weeks=1.0/7), td(days=1))
187 eq(td(days=1.0/24), td(hours=1))
188 eq(td(hours=1.0/60), td(minutes=1))
189 eq(td(minutes=1.0/60), td(seconds=1))
190 eq(td(seconds=0.001), td(milliseconds=1))
191 eq(td(milliseconds=0.001), td(microseconds=1))
192
193 def test_computations(self):
194 eq = self.assertEqual
195 td = timedelta
196
197 a = td(7) # One week
198 b = td(0, 60) # One minute
199 c = td(0, 0, 1000) # One millisecond
200 eq(a+b+c, td(7, 60, 1000))
201 eq(a-b, td(6, 24*3600 - 60))
202 eq(-a, td(-7))
203 eq(+a, td(7))
204 eq(-b, td(-1, 24*3600 - 60))
205 eq(-c, td(-1, 24*3600 - 1, 999000))
206 eq(abs(a), a)
207 eq(abs(-a), a)
208 eq(td(6, 24*3600), a)
209 eq(td(0, 0, 60*1000000), b)
210 eq(a*10, td(70))
211 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000212 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000213 eq(b*10, td(0, 600))
214 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000215 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000216 eq(c*10, td(0, 0, 10000))
217 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000218 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000219 eq(a*-1, -a)
220 eq(b*-2, -b-b)
221 eq(c*-2, -c+-c)
222 eq(b*(60*24), (b*60)*24)
223 eq(b*(60*24), (60*b)*24)
224 eq(c*1000, td(0, 1))
225 eq(1000*c, td(0, 1))
226 eq(a//7, td(1))
227 eq(b//10, td(0, 6))
228 eq(c//1000, td(0, 0, 1))
229 eq(a//10, td(0, 7*24*360))
230 eq(a//3600000, td(0, 0, 7*24*1000))
231
232 def test_disallowed_computations(self):
233 a = timedelta(42)
234
235 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000236 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000237 self.assertRaises(TypeError, lambda: a+i)
238 self.assertRaises(TypeError, lambda: a-i)
239 self.assertRaises(TypeError, lambda: i+a)
240 self.assertRaises(TypeError, lambda: i-a)
241
242 # Mul/div by float isn't supported.
243 x = 2.3
244 self.assertRaises(TypeError, lambda: a*x)
245 self.assertRaises(TypeError, lambda: x*a)
246 self.assertRaises(TypeError, lambda: a/x)
247 self.assertRaises(TypeError, lambda: x/a)
248 self.assertRaises(TypeError, lambda: a // x)
249 self.assertRaises(TypeError, lambda: x // a)
250
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000251 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000252 # Division by zero doesn't make sense.
Guido van Rossume2a383d2007-01-15 16:59:06 +0000253 for zero in 0, 0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000254 self.assertRaises(TypeError, lambda: zero // a)
255 self.assertRaises(ZeroDivisionError, lambda: a // zero)
256
257 def test_basic_attributes(self):
258 days, seconds, us = 1, 7, 31
259 td = timedelta(days, seconds, us)
260 self.assertEqual(td.days, days)
261 self.assertEqual(td.seconds, seconds)
262 self.assertEqual(td.microseconds, us)
263
264 def test_carries(self):
265 t1 = timedelta(days=100,
266 weeks=-7,
267 hours=-24*(100-49),
268 minutes=-3,
269 seconds=12,
270 microseconds=(3*60 - 12) * 1e6 + 1)
271 t2 = timedelta(microseconds=1)
272 self.assertEqual(t1, t2)
273
274 def test_hash_equality(self):
275 t1 = timedelta(days=100,
276 weeks=-7,
277 hours=-24*(100-49),
278 minutes=-3,
279 seconds=12,
280 microseconds=(3*60 - 12) * 1000000)
281 t2 = timedelta()
282 self.assertEqual(hash(t1), hash(t2))
283
284 t1 += timedelta(weeks=7)
285 t2 += timedelta(days=7*7)
286 self.assertEqual(t1, t2)
287 self.assertEqual(hash(t1), hash(t2))
288
289 d = {t1: 1}
290 d[t2] = 2
291 self.assertEqual(len(d), 1)
292 self.assertEqual(d[t1], 2)
293
294 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000295 args = 12, 34, 56
296 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000297 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000298 green = pickler.dumps(orig, proto)
299 derived = unpickler.loads(green)
300 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000301
302 def test_compare(self):
303 t1 = timedelta(2, 3, 4)
304 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000305 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000306 self.assertTrue(t1 <= t2)
307 self.assertTrue(t1 >= t2)
308 self.assertTrue(not t1 != t2)
309 self.assertTrue(not t1 < t2)
310 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000311
312 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
313 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000314 self.assertTrue(t1 < t2)
315 self.assertTrue(t2 > t1)
316 self.assertTrue(t1 <= t2)
317 self.assertTrue(t2 >= t1)
318 self.assertTrue(t1 != t2)
319 self.assertTrue(t2 != t1)
320 self.assertTrue(not t1 == t2)
321 self.assertTrue(not t2 == t1)
322 self.assertTrue(not t1 > t2)
323 self.assertTrue(not t2 < t1)
324 self.assertTrue(not t1 >= t2)
325 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000326
Tim Peters68124bb2003-02-08 03:46:31 +0000327 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000328 self.assertEqual(t1 == badarg, False)
329 self.assertEqual(t1 != badarg, True)
330 self.assertEqual(badarg == t1, False)
331 self.assertEqual(badarg != t1, True)
332
Tim Peters2a799bf2002-12-16 20:18:38 +0000333 self.assertRaises(TypeError, lambda: t1 <= badarg)
334 self.assertRaises(TypeError, lambda: t1 < badarg)
335 self.assertRaises(TypeError, lambda: t1 > badarg)
336 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000337 self.assertRaises(TypeError, lambda: badarg <= t1)
338 self.assertRaises(TypeError, lambda: badarg < t1)
339 self.assertRaises(TypeError, lambda: badarg > t1)
340 self.assertRaises(TypeError, lambda: badarg >= t1)
341
342 def test_str(self):
343 td = timedelta
344 eq = self.assertEqual
345
346 eq(str(td(1)), "1 day, 0:00:00")
347 eq(str(td(-1)), "-1 day, 0:00:00")
348 eq(str(td(2)), "2 days, 0:00:00")
349 eq(str(td(-2)), "-2 days, 0:00:00")
350
351 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
352 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
353 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
354 "-210 days, 23:12:34")
355
356 eq(str(td(milliseconds=1)), "0:00:00.001000")
357 eq(str(td(microseconds=3)), "0:00:00.000003")
358
359 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
360 microseconds=999999)),
361 "999999999 days, 23:59:59.999999")
362
363 def test_roundtrip(self):
364 for td in (timedelta(days=999999999, hours=23, minutes=59,
365 seconds=59, microseconds=999999),
366 timedelta(days=-999999999),
367 timedelta(days=1, seconds=2, microseconds=3)):
368
369 # Verify td -> string -> td identity.
370 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000371 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000372 s = s[9:]
373 td2 = eval(s)
374 self.assertEqual(td, td2)
375
376 # Verify identity via reconstructing from pieces.
377 td2 = timedelta(td.days, td.seconds, td.microseconds)
378 self.assertEqual(td, td2)
379
380 def test_resolution_info(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000381 self.assertTrue(isinstance(timedelta.min, timedelta))
382 self.assertTrue(isinstance(timedelta.max, timedelta))
383 self.assertTrue(isinstance(timedelta.resolution, timedelta))
384 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000385 self.assertEqual(timedelta.min, timedelta(-999999999))
386 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
387 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
388
389 def test_overflow(self):
390 tiny = timedelta.resolution
391
392 td = timedelta.min + tiny
393 td -= tiny # no problem
394 self.assertRaises(OverflowError, td.__sub__, tiny)
395 self.assertRaises(OverflowError, td.__add__, -tiny)
396
397 td = timedelta.max - tiny
398 td += tiny # no problem
399 self.assertRaises(OverflowError, td.__add__, tiny)
400 self.assertRaises(OverflowError, td.__sub__, -tiny)
401
402 self.assertRaises(OverflowError, lambda: -timedelta.max)
403
404 def test_microsecond_rounding(self):
405 td = timedelta
406 eq = self.assertEqual
407
408 # Single-field rounding.
409 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
410 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
411 eq(td(milliseconds=0.6/1000), td(microseconds=1))
412 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
413
414 # Rounding due to contributions from more than one field.
415 us_per_hour = 3600e6
416 us_per_day = us_per_hour * 24
417 eq(td(days=.4/us_per_day), td(0))
418 eq(td(hours=.2/us_per_hour), td(0))
419 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
420
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 def test_massive_normalization(self):
426 td = timedelta(microseconds=-1)
427 self.assertEqual((td.days, td.seconds, td.microseconds),
428 (-1, 24*3600-1, 999999))
429
430 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000431 self.assertTrue(timedelta(1))
432 self.assertTrue(timedelta(0, 1))
433 self.assertTrue(timedelta(0, 0, 1))
434 self.assertTrue(timedelta(microseconds=1))
435 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000436
Tim Petersb0c854d2003-05-17 15:57:00 +0000437 def test_subclass_timedelta(self):
438
439 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000440 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000441 def from_td(td):
442 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000443
444 def as_hours(self):
445 sum = (self.days * 24 +
446 self.seconds / 3600.0 +
447 self.microseconds / 3600e6)
448 return round(sum)
449
450 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000451 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000452 self.assertEqual(t1.as_hours(), 24)
453
454 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000455 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000456 self.assertEqual(t2.as_hours(), -25)
457
458 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000459 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000460 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000461 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000462 self.assertEqual(t3.days, t4.days)
463 self.assertEqual(t3.seconds, t4.seconds)
464 self.assertEqual(t3.microseconds, t4.microseconds)
465 self.assertEqual(str(t3), str(t4))
466 self.assertEqual(t4.as_hours(), -1)
467
Tim Peters2a799bf2002-12-16 20:18:38 +0000468#############################################################################
469# date tests
470
471class TestDateOnly(unittest.TestCase):
472 # Tests here won't pass if also run on datetime objects, so don't
473 # subclass this to test datetimes too.
474
475 def test_delta_non_days_ignored(self):
476 dt = date(2000, 1, 2)
477 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
478 microseconds=5)
479 days = timedelta(delta.days)
480 self.assertEqual(days, timedelta(1))
481
482 dt2 = dt + delta
483 self.assertEqual(dt2, dt + days)
484
485 dt2 = delta + dt
486 self.assertEqual(dt2, dt + days)
487
488 dt2 = dt - delta
489 self.assertEqual(dt2, dt - days)
490
491 delta = -delta
492 days = timedelta(delta.days)
493 self.assertEqual(days, timedelta(-2))
494
495 dt2 = dt + delta
496 self.assertEqual(dt2, dt + days)
497
498 dt2 = delta + dt
499 self.assertEqual(dt2, dt + days)
500
501 dt2 = dt - delta
502 self.assertEqual(dt2, dt - days)
503
Tim Peters604c0132004-06-07 23:04:33 +0000504class SubclassDate(date):
505 sub_var = 1
506
Guido van Rossumd8faa362007-04-27 19:54:29 +0000507class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000508 # Tests here should pass for both dates and datetimes, except for a
509 # few tests that TestDateTime overrides.
510
511 theclass = date
512
513 def test_basic_attributes(self):
514 dt = self.theclass(2002, 3, 1)
515 self.assertEqual(dt.year, 2002)
516 self.assertEqual(dt.month, 3)
517 self.assertEqual(dt.day, 1)
518
519 def test_roundtrip(self):
520 for dt in (self.theclass(1, 2, 3),
521 self.theclass.today()):
522 # Verify dt -> string -> date identity.
523 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000524 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000525 s = s[9:]
526 dt2 = eval(s)
527 self.assertEqual(dt, dt2)
528
529 # Verify identity via reconstructing from pieces.
530 dt2 = self.theclass(dt.year, dt.month, dt.day)
531 self.assertEqual(dt, dt2)
532
533 def test_ordinal_conversions(self):
534 # Check some fixed values.
535 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
536 (1, 12, 31, 365),
537 (2, 1, 1, 366),
538 # first example from "Calendrical Calculations"
539 (1945, 11, 12, 710347)]:
540 d = self.theclass(y, m, d)
541 self.assertEqual(n, d.toordinal())
542 fromord = self.theclass.fromordinal(n)
543 self.assertEqual(d, fromord)
544 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000545 # if we're checking something fancier than a date, verify
546 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000547 self.assertEqual(fromord.hour, 0)
548 self.assertEqual(fromord.minute, 0)
549 self.assertEqual(fromord.second, 0)
550 self.assertEqual(fromord.microsecond, 0)
551
Tim Peters0bf60bd2003-01-08 20:40:01 +0000552 # Check first and last days of year spottily across the whole
553 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000554 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000555 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
556 d = self.theclass(year, 1, 1)
557 n = d.toordinal()
558 d2 = self.theclass.fromordinal(n)
559 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000560 # Verify that moving back a day gets to the end of year-1.
561 if year > 1:
562 d = self.theclass.fromordinal(n-1)
563 d2 = self.theclass(year-1, 12, 31)
564 self.assertEqual(d, d2)
565 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000566
567 # Test every day in a leap-year and a non-leap year.
568 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
569 for year, isleap in (2000, True), (2002, False):
570 n = self.theclass(year, 1, 1).toordinal()
571 for month, maxday in zip(range(1, 13), dim):
572 if month == 2 and isleap:
573 maxday += 1
574 for day in range(1, maxday+1):
575 d = self.theclass(year, month, day)
576 self.assertEqual(d.toordinal(), n)
577 self.assertEqual(d, self.theclass.fromordinal(n))
578 n += 1
579
580 def test_extreme_ordinals(self):
581 a = self.theclass.min
582 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
583 aord = a.toordinal()
584 b = a.fromordinal(aord)
585 self.assertEqual(a, b)
586
587 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
588
589 b = a + timedelta(days=1)
590 self.assertEqual(b.toordinal(), aord + 1)
591 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
592
593 a = self.theclass.max
594 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
595 aord = a.toordinal()
596 b = a.fromordinal(aord)
597 self.assertEqual(a, b)
598
599 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
600
601 b = a - timedelta(days=1)
602 self.assertEqual(b.toordinal(), aord - 1)
603 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
604
605 def test_bad_constructor_arguments(self):
606 # bad years
607 self.theclass(MINYEAR, 1, 1) # no exception
608 self.theclass(MAXYEAR, 1, 1) # no exception
609 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
610 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
611 # bad months
612 self.theclass(2000, 1, 1) # no exception
613 self.theclass(2000, 12, 1) # no exception
614 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
615 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
616 # bad days
617 self.theclass(2000, 2, 29) # no exception
618 self.theclass(2004, 2, 29) # no exception
619 self.theclass(2400, 2, 29) # no exception
620 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
621 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
622 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
623 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
624 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
625 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
626
627 def test_hash_equality(self):
628 d = self.theclass(2000, 12, 31)
629 # same thing
630 e = self.theclass(2000, 12, 31)
631 self.assertEqual(d, e)
632 self.assertEqual(hash(d), hash(e))
633
634 dic = {d: 1}
635 dic[e] = 2
636 self.assertEqual(len(dic), 1)
637 self.assertEqual(dic[d], 2)
638 self.assertEqual(dic[e], 2)
639
640 d = self.theclass(2001, 1, 1)
641 # same thing
642 e = self.theclass(2001, 1, 1)
643 self.assertEqual(d, e)
644 self.assertEqual(hash(d), hash(e))
645
646 dic = {d: 1}
647 dic[e] = 2
648 self.assertEqual(len(dic), 1)
649 self.assertEqual(dic[d], 2)
650 self.assertEqual(dic[e], 2)
651
652 def test_computations(self):
653 a = self.theclass(2002, 1, 31)
654 b = self.theclass(1956, 1, 31)
655
656 diff = a-b
657 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
658 self.assertEqual(diff.seconds, 0)
659 self.assertEqual(diff.microseconds, 0)
660
661 day = timedelta(1)
662 week = timedelta(7)
663 a = self.theclass(2002, 3, 2)
664 self.assertEqual(a + day, self.theclass(2002, 3, 3))
665 self.assertEqual(day + a, self.theclass(2002, 3, 3))
666 self.assertEqual(a - day, self.theclass(2002, 3, 1))
667 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
668 self.assertEqual(a + week, self.theclass(2002, 3, 9))
669 self.assertEqual(a - week, self.theclass(2002, 2, 23))
670 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
671 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
672 self.assertEqual((a + week) - a, week)
673 self.assertEqual((a + day) - a, day)
674 self.assertEqual((a - week) - a, -week)
675 self.assertEqual((a - day) - a, -day)
676 self.assertEqual(a - (a + week), -week)
677 self.assertEqual(a - (a + day), -day)
678 self.assertEqual(a - (a - week), week)
679 self.assertEqual(a - (a - day), day)
680
681 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000682 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000683 self.assertRaises(TypeError, lambda: a+i)
684 self.assertRaises(TypeError, lambda: a-i)
685 self.assertRaises(TypeError, lambda: i+a)
686 self.assertRaises(TypeError, lambda: i-a)
687
688 # delta - date is senseless.
689 self.assertRaises(TypeError, lambda: day - a)
690 # mixing date and (delta or date) via * or // is senseless
691 self.assertRaises(TypeError, lambda: day * a)
692 self.assertRaises(TypeError, lambda: a * day)
693 self.assertRaises(TypeError, lambda: day // a)
694 self.assertRaises(TypeError, lambda: a // day)
695 self.assertRaises(TypeError, lambda: a * a)
696 self.assertRaises(TypeError, lambda: a // a)
697 # date + date is senseless
698 self.assertRaises(TypeError, lambda: a + a)
699
700 def test_overflow(self):
701 tiny = self.theclass.resolution
702
703 dt = self.theclass.min + tiny
704 dt -= tiny # no problem
705 self.assertRaises(OverflowError, dt.__sub__, tiny)
706 self.assertRaises(OverflowError, dt.__add__, -tiny)
707
708 dt = self.theclass.max - tiny
709 dt += tiny # no problem
710 self.assertRaises(OverflowError, dt.__add__, tiny)
711 self.assertRaises(OverflowError, dt.__sub__, -tiny)
712
713 def test_fromtimestamp(self):
714 import time
715
716 # Try an arbitrary fixed value.
717 year, month, day = 1999, 9, 19
718 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
719 d = self.theclass.fromtimestamp(ts)
720 self.assertEqual(d.year, year)
721 self.assertEqual(d.month, month)
722 self.assertEqual(d.day, day)
723
Tim Peters1b6f7a92004-06-20 02:50:16 +0000724 def test_insane_fromtimestamp(self):
725 # It's possible that some platform maps time_t to double,
726 # and that this test will fail there. This test should
727 # exempt such platforms (provided they return reasonable
728 # results!).
729 for insane in -1e200, 1e200:
730 self.assertRaises(ValueError, self.theclass.fromtimestamp,
731 insane)
732
Tim Peters2a799bf2002-12-16 20:18:38 +0000733 def test_today(self):
734 import time
735
736 # We claim that today() is like fromtimestamp(time.time()), so
737 # prove it.
738 for dummy in range(3):
739 today = self.theclass.today()
740 ts = time.time()
741 todayagain = self.theclass.fromtimestamp(ts)
742 if today == todayagain:
743 break
744 # There are several legit reasons that could fail:
745 # 1. It recently became midnight, between the today() and the
746 # time() calls.
747 # 2. The platform time() has such fine resolution that we'll
748 # never get the same value twice.
749 # 3. The platform time() has poor resolution, and we just
750 # happened to call today() right before a resolution quantum
751 # boundary.
752 # 4. The system clock got fiddled between calls.
753 # In any case, wait a little while and try again.
754 time.sleep(0.1)
755
756 # It worked or it didn't. If it didn't, assume it's reason #2, and
757 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000758 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000759 abs(todayagain - today) < timedelta(seconds=0.5))
760
761 def test_weekday(self):
762 for i in range(7):
763 # March 4, 2002 is a Monday
764 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
765 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
766 # January 2, 1956 is a Monday
767 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
768 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
769
770 def test_isocalendar(self):
771 # Check examples from
772 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
773 for i in range(7):
774 d = self.theclass(2003, 12, 22+i)
775 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
776 d = self.theclass(2003, 12, 29) + timedelta(i)
777 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
778 d = self.theclass(2004, 1, 5+i)
779 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
780 d = self.theclass(2009, 12, 21+i)
781 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
782 d = self.theclass(2009, 12, 28) + timedelta(i)
783 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
784 d = self.theclass(2010, 1, 4+i)
785 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
786
787 def test_iso_long_years(self):
788 # Calculate long ISO years and compare to table from
789 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
790 ISO_LONG_YEARS_TABLE = """
791 4 32 60 88
792 9 37 65 93
793 15 43 71 99
794 20 48 76
795 26 54 82
796
797 105 133 161 189
798 111 139 167 195
799 116 144 172
800 122 150 178
801 128 156 184
802
803 201 229 257 285
804 207 235 263 291
805 212 240 268 296
806 218 246 274
807 224 252 280
808
809 303 331 359 387
810 308 336 364 392
811 314 342 370 398
812 320 348 376
813 325 353 381
814 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000815 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000816 L = []
817 for i in range(400):
818 d = self.theclass(2000+i, 12, 31)
819 d1 = self.theclass(1600+i, 12, 31)
820 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
821 if d.isocalendar()[1] == 53:
822 L.append(i)
823 self.assertEqual(L, iso_long_years)
824
825 def test_isoformat(self):
826 t = self.theclass(2, 3, 2)
827 self.assertEqual(t.isoformat(), "0002-03-02")
828
829 def test_ctime(self):
830 t = self.theclass(2002, 3, 2)
831 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
832
833 def test_strftime(self):
834 t = self.theclass(2005, 3, 2)
835 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000836 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000837 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000838
839 self.assertRaises(TypeError, t.strftime) # needs an arg
840 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
841 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
842
Georg Brandlf78e02b2008-06-10 17:40:04 +0000843 # test that unicode input is allowed (issue 2782)
844 self.assertEqual(t.strftime("%m"), "03")
845
Tim Peters2a799bf2002-12-16 20:18:38 +0000846 # A naive object replaces %z and %Z w/ empty strings.
847 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
848
Benjamin Petersone1cdfd72009-01-18 21:02:37 +0000849 #make sure that invalid format specifiers are handled correctly
850 #self.assertRaises(ValueError, t.strftime, "%e")
851 #self.assertRaises(ValueError, t.strftime, "%")
852 #self.assertRaises(ValueError, t.strftime, "%#")
853
854 #oh well, some systems just ignore those invalid ones.
855 #at least, excercise them to make sure that no crashes
856 #are generated
857 for f in ["%e", "%", "%#"]:
858 try:
859 t.strftime(f)
860 except ValueError:
861 pass
862
863 #check that this standard extension works
864 t.strftime("%f")
865
Georg Brandlf78e02b2008-06-10 17:40:04 +0000866
Eric Smith1ba31142007-09-11 18:06:02 +0000867 def test_format(self):
868 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000869 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000870
871 # check that a derived class's __str__() gets called
872 class A(self.theclass):
873 def __str__(self):
874 return 'A'
875 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000876 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000877
878 # check that a derived class's strftime gets called
879 class B(self.theclass):
880 def strftime(self, format_spec):
881 return 'B'
882 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000883 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000884
885 for fmt in ["m:%m d:%d y:%y",
886 "m:%m d:%d y:%y H:%H M:%M S:%S",
887 "%z %Z",
888 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +0000889 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
890 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
891 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +0000892
Tim Peters2a799bf2002-12-16 20:18:38 +0000893 def test_resolution_info(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000894 self.assertTrue(isinstance(self.theclass.min, self.theclass))
895 self.assertTrue(isinstance(self.theclass.max, self.theclass))
896 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
897 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000898
899 def test_extreme_timedelta(self):
900 big = self.theclass.max - self.theclass.min
901 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
902 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
903 # n == 315537897599999999 ~= 2**58.13
904 justasbig = timedelta(0, 0, n)
905 self.assertEqual(big, justasbig)
906 self.assertEqual(self.theclass.min + big, self.theclass.max)
907 self.assertEqual(self.theclass.max - big, self.theclass.min)
908
909 def test_timetuple(self):
910 for i in range(7):
911 # January 2, 1956 is a Monday (0)
912 d = self.theclass(1956, 1, 2+i)
913 t = d.timetuple()
914 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
915 # February 1, 1956 is a Wednesday (2)
916 d = self.theclass(1956, 2, 1+i)
917 t = d.timetuple()
918 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
919 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
920 # of the year.
921 d = self.theclass(1956, 3, 1+i)
922 t = d.timetuple()
923 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
924 self.assertEqual(t.tm_year, 1956)
925 self.assertEqual(t.tm_mon, 3)
926 self.assertEqual(t.tm_mday, 1+i)
927 self.assertEqual(t.tm_hour, 0)
928 self.assertEqual(t.tm_min, 0)
929 self.assertEqual(t.tm_sec, 0)
930 self.assertEqual(t.tm_wday, (3+i)%7)
931 self.assertEqual(t.tm_yday, 61+i)
932 self.assertEqual(t.tm_isdst, -1)
933
934 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000935 args = 6, 7, 23
936 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000937 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000938 green = pickler.dumps(orig, proto)
939 derived = unpickler.loads(green)
940 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000941
942 def test_compare(self):
943 t1 = self.theclass(2, 3, 4)
944 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000945 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000946 self.assertTrue(t1 <= t2)
947 self.assertTrue(t1 >= t2)
948 self.assertTrue(not t1 != t2)
949 self.assertTrue(not t1 < t2)
950 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000951
952 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
953 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000954 self.assertTrue(t1 < t2)
955 self.assertTrue(t2 > t1)
956 self.assertTrue(t1 <= t2)
957 self.assertTrue(t2 >= t1)
958 self.assertTrue(t1 != t2)
959 self.assertTrue(t2 != t1)
960 self.assertTrue(not t1 == t2)
961 self.assertTrue(not t2 == t1)
962 self.assertTrue(not t1 > t2)
963 self.assertTrue(not t2 < t1)
964 self.assertTrue(not t1 >= t2)
965 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000966
Tim Peters68124bb2003-02-08 03:46:31 +0000967 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000968 self.assertEqual(t1 == badarg, False)
969 self.assertEqual(t1 != badarg, True)
970 self.assertEqual(badarg == t1, False)
971 self.assertEqual(badarg != t1, True)
972
Tim Peters2a799bf2002-12-16 20:18:38 +0000973 self.assertRaises(TypeError, lambda: t1 < badarg)
974 self.assertRaises(TypeError, lambda: t1 > badarg)
975 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000976 self.assertRaises(TypeError, lambda: badarg <= t1)
977 self.assertRaises(TypeError, lambda: badarg < t1)
978 self.assertRaises(TypeError, lambda: badarg > t1)
979 self.assertRaises(TypeError, lambda: badarg >= t1)
980
Tim Peters8d81a012003-01-24 22:36:34 +0000981 def test_mixed_compare(self):
982 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000983
984 # Our class can be compared for equality to other classes
985 self.assertEqual(our == 1, False)
986 self.assertEqual(1 == our, False)
987 self.assertEqual(our != 1, True)
988 self.assertEqual(1 != our, True)
989
990 # But the ordering is undefined
991 self.assertRaises(TypeError, lambda: our < 1)
992 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000993
Guido van Rossum19960592006-08-24 17:29:38 +0000994 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +0000995
Guido van Rossum19960592006-08-24 17:29:38 +0000996 class SomeClass:
997 pass
998
999 their = SomeClass()
1000 self.assertEqual(our == their, False)
1001 self.assertEqual(their == our, False)
1002 self.assertEqual(our != their, True)
1003 self.assertEqual(their != our, True)
1004 self.assertRaises(TypeError, lambda: our < their)
1005 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001006
Guido van Rossum19960592006-08-24 17:29:38 +00001007 # However, if the other class explicitly defines ordering
1008 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001009
Guido van Rossum19960592006-08-24 17:29:38 +00001010 class LargerThanAnything:
1011 def __lt__(self, other):
1012 return False
1013 def __le__(self, other):
1014 return isinstance(other, LargerThanAnything)
1015 def __eq__(self, other):
1016 return isinstance(other, LargerThanAnything)
1017 def __ne__(self, other):
1018 return not isinstance(other, LargerThanAnything)
1019 def __gt__(self, other):
1020 return not isinstance(other, LargerThanAnything)
1021 def __ge__(self, other):
1022 return True
1023
1024 their = LargerThanAnything()
1025 self.assertEqual(our == their, False)
1026 self.assertEqual(their == our, False)
1027 self.assertEqual(our != their, True)
1028 self.assertEqual(their != our, True)
1029 self.assertEqual(our < their, True)
1030 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001031
Tim Peters2a799bf2002-12-16 20:18:38 +00001032 def test_bool(self):
1033 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001034 self.assertTrue(self.theclass.min)
1035 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001036
Guido van Rossum04110fb2007-08-24 16:32:05 +00001037 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001038 # For nasty technical reasons, we can't handle years before 1900.
1039 cls = self.theclass
1040 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1041 for y in 1, 49, 51, 99, 100, 1000, 1899:
1042 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001043
1044 def test_replace(self):
1045 cls = self.theclass
1046 args = [1, 2, 3]
1047 base = cls(*args)
1048 self.assertEqual(base, base.replace())
1049
1050 i = 0
1051 for name, newval in (("year", 2),
1052 ("month", 3),
1053 ("day", 4)):
1054 newargs = args[:]
1055 newargs[i] = newval
1056 expected = cls(*newargs)
1057 got = base.replace(**{name: newval})
1058 self.assertEqual(expected, got)
1059 i += 1
1060
1061 # Out of bounds.
1062 base = cls(2000, 2, 29)
1063 self.assertRaises(ValueError, base.replace, year=2001)
1064
Tim Petersa98924a2003-05-17 05:55:19 +00001065 def test_subclass_date(self):
1066
1067 class C(self.theclass):
1068 theAnswer = 42
1069
1070 def __new__(cls, *args, **kws):
1071 temp = kws.copy()
1072 extra = temp.pop('extra')
1073 result = self.theclass.__new__(cls, *args, **temp)
1074 result.extra = extra
1075 return result
1076
1077 def newmeth(self, start):
1078 return start + self.year + self.month
1079
1080 args = 2003, 4, 14
1081
1082 dt1 = self.theclass(*args)
1083 dt2 = C(*args, **{'extra': 7})
1084
1085 self.assertEqual(dt2.__class__, C)
1086 self.assertEqual(dt2.theAnswer, 42)
1087 self.assertEqual(dt2.extra, 7)
1088 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1089 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1090
Tim Peters604c0132004-06-07 23:04:33 +00001091 def test_pickling_subclass_date(self):
1092
1093 args = 6, 7, 23
1094 orig = SubclassDate(*args)
1095 for pickler, unpickler, proto in pickle_choices:
1096 green = pickler.dumps(orig, proto)
1097 derived = unpickler.loads(green)
1098 self.assertEqual(orig, derived)
1099
Tim Peters3f606292004-03-21 23:38:41 +00001100 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001101 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001102 # This is a low-overhead backdoor. A user can (by intent or
1103 # mistake) pass a string directly, which (if it's the right length)
1104 # will get treated like a pickle, and bypass the normal sanity
1105 # checks in the constructor. This can create insane objects.
1106 # The constructor doesn't want to burn the time to validate all
1107 # fields, but does check the month field. This stops, e.g.,
1108 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001109 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001110 if not issubclass(self.theclass, datetime):
1111 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001112 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001113 self.assertRaises(TypeError, self.theclass,
1114 base[:2] + month_byte + base[3:])
1115 for ord_byte in range(1, 13):
1116 # This shouldn't blow up because of the month byte alone. If
1117 # the implementation changes to do more-careful checking, it may
1118 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001119 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001120
Tim Peters2a799bf2002-12-16 20:18:38 +00001121#############################################################################
1122# datetime tests
1123
Tim Peters604c0132004-06-07 23:04:33 +00001124class SubclassDatetime(datetime):
1125 sub_var = 1
1126
Tim Peters2a799bf2002-12-16 20:18:38 +00001127class TestDateTime(TestDate):
1128
1129 theclass = datetime
1130
1131 def test_basic_attributes(self):
1132 dt = self.theclass(2002, 3, 1, 12, 0)
1133 self.assertEqual(dt.year, 2002)
1134 self.assertEqual(dt.month, 3)
1135 self.assertEqual(dt.day, 1)
1136 self.assertEqual(dt.hour, 12)
1137 self.assertEqual(dt.minute, 0)
1138 self.assertEqual(dt.second, 0)
1139 self.assertEqual(dt.microsecond, 0)
1140
1141 def test_basic_attributes_nonzero(self):
1142 # Make sure all attributes are non-zero so bugs in
1143 # bit-shifting access show up.
1144 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1145 self.assertEqual(dt.year, 2002)
1146 self.assertEqual(dt.month, 3)
1147 self.assertEqual(dt.day, 1)
1148 self.assertEqual(dt.hour, 12)
1149 self.assertEqual(dt.minute, 59)
1150 self.assertEqual(dt.second, 59)
1151 self.assertEqual(dt.microsecond, 8000)
1152
1153 def test_roundtrip(self):
1154 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1155 self.theclass.now()):
1156 # Verify dt -> string -> datetime identity.
1157 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001158 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001159 s = s[9:]
1160 dt2 = eval(s)
1161 self.assertEqual(dt, dt2)
1162
1163 # Verify identity via reconstructing from pieces.
1164 dt2 = self.theclass(dt.year, dt.month, dt.day,
1165 dt.hour, dt.minute, dt.second,
1166 dt.microsecond)
1167 self.assertEqual(dt, dt2)
1168
1169 def test_isoformat(self):
1170 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1171 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1172 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1173 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1174 # str is ISO format with the separator forced to a blank.
1175 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1176
1177 t = self.theclass(2, 3, 2)
1178 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1179 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1180 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1181 # str is ISO format with the separator forced to a blank.
1182 self.assertEqual(str(t), "0002-03-02 00:00:00")
1183
Eric Smith1ba31142007-09-11 18:06:02 +00001184 def test_format(self):
1185 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001186 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001187
1188 # check that a derived class's __str__() gets called
1189 class A(self.theclass):
1190 def __str__(self):
1191 return 'A'
1192 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001193 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001194
1195 # check that a derived class's strftime gets called
1196 class B(self.theclass):
1197 def strftime(self, format_spec):
1198 return 'B'
1199 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001200 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001201
1202 for fmt in ["m:%m d:%d y:%y",
1203 "m:%m d:%d y:%y H:%H M:%M S:%S",
1204 "%z %Z",
1205 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001206 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1207 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1208 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001209
Tim Peters2a799bf2002-12-16 20:18:38 +00001210 def test_more_ctime(self):
1211 # Test fields that TestDate doesn't touch.
1212 import time
1213
1214 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1215 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1216 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1217 # out. The difference is that t.ctime() produces " 2" for the day,
1218 # but platform ctime() produces "02" for the day. According to
1219 # C99, t.ctime() is correct here.
1220 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1221
1222 # So test a case where that difference doesn't matter.
1223 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1224 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1225
1226 def test_tz_independent_comparing(self):
1227 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1228 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1229 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1230 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001231 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001232
1233 # Make sure comparison doesn't forget microseconds, and isn't done
1234 # via comparing a float timestamp (an IEEE double doesn't have enough
1235 # precision to span microsecond resolution across years 1 thru 9999,
1236 # so comparing via timestamp necessarily calls some distinct values
1237 # equal).
1238 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1239 us = timedelta(microseconds=1)
1240 dt2 = dt1 + us
1241 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001242 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001243
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001244 def test_strftime_with_bad_tzname_replace(self):
1245 # verify ok if tzinfo.tzname().replace() returns a non-string
1246 class MyTzInfo(FixedOffset):
1247 def tzname(self, dt):
1248 class MyStr(str):
1249 def replace(self, *args):
1250 return None
1251 return MyStr('name')
1252 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1253 self.assertRaises(TypeError, t.strftime, '%Z')
1254
Tim Peters2a799bf2002-12-16 20:18:38 +00001255 def test_bad_constructor_arguments(self):
1256 # bad years
1257 self.theclass(MINYEAR, 1, 1) # no exception
1258 self.theclass(MAXYEAR, 1, 1) # no exception
1259 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1260 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1261 # bad months
1262 self.theclass(2000, 1, 1) # no exception
1263 self.theclass(2000, 12, 1) # no exception
1264 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1265 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1266 # bad days
1267 self.theclass(2000, 2, 29) # no exception
1268 self.theclass(2004, 2, 29) # no exception
1269 self.theclass(2400, 2, 29) # no exception
1270 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1271 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1272 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1273 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1274 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1275 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1276 # bad hours
1277 self.theclass(2000, 1, 31, 0) # no exception
1278 self.theclass(2000, 1, 31, 23) # no exception
1279 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1280 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1281 # bad minutes
1282 self.theclass(2000, 1, 31, 23, 0) # no exception
1283 self.theclass(2000, 1, 31, 23, 59) # no exception
1284 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1285 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1286 # bad seconds
1287 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1288 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1289 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1290 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1291 # bad microseconds
1292 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1293 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1294 self.assertRaises(ValueError, self.theclass,
1295 2000, 1, 31, 23, 59, 59, -1)
1296 self.assertRaises(ValueError, self.theclass,
1297 2000, 1, 31, 23, 59, 59,
1298 1000000)
1299
1300 def test_hash_equality(self):
1301 d = self.theclass(2000, 12, 31, 23, 30, 17)
1302 e = self.theclass(2000, 12, 31, 23, 30, 17)
1303 self.assertEqual(d, e)
1304 self.assertEqual(hash(d), hash(e))
1305
1306 dic = {d: 1}
1307 dic[e] = 2
1308 self.assertEqual(len(dic), 1)
1309 self.assertEqual(dic[d], 2)
1310 self.assertEqual(dic[e], 2)
1311
1312 d = self.theclass(2001, 1, 1, 0, 5, 17)
1313 e = self.theclass(2001, 1, 1, 0, 5, 17)
1314 self.assertEqual(d, e)
1315 self.assertEqual(hash(d), hash(e))
1316
1317 dic = {d: 1}
1318 dic[e] = 2
1319 self.assertEqual(len(dic), 1)
1320 self.assertEqual(dic[d], 2)
1321 self.assertEqual(dic[e], 2)
1322
1323 def test_computations(self):
1324 a = self.theclass(2002, 1, 31)
1325 b = self.theclass(1956, 1, 31)
1326 diff = a-b
1327 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1328 self.assertEqual(diff.seconds, 0)
1329 self.assertEqual(diff.microseconds, 0)
1330 a = self.theclass(2002, 3, 2, 17, 6)
1331 millisec = timedelta(0, 0, 1000)
1332 hour = timedelta(0, 3600)
1333 day = timedelta(1)
1334 week = timedelta(7)
1335 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1336 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1337 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1338 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1339 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1340 self.assertEqual(a - hour, a + -hour)
1341 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1342 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1343 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1344 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1345 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1346 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1347 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1348 self.assertEqual((a + week) - a, week)
1349 self.assertEqual((a + day) - a, day)
1350 self.assertEqual((a + hour) - a, hour)
1351 self.assertEqual((a + millisec) - a, millisec)
1352 self.assertEqual((a - week) - a, -week)
1353 self.assertEqual((a - day) - a, -day)
1354 self.assertEqual((a - hour) - a, -hour)
1355 self.assertEqual((a - millisec) - a, -millisec)
1356 self.assertEqual(a - (a + week), -week)
1357 self.assertEqual(a - (a + day), -day)
1358 self.assertEqual(a - (a + hour), -hour)
1359 self.assertEqual(a - (a + millisec), -millisec)
1360 self.assertEqual(a - (a - week), week)
1361 self.assertEqual(a - (a - day), day)
1362 self.assertEqual(a - (a - hour), hour)
1363 self.assertEqual(a - (a - millisec), millisec)
1364 self.assertEqual(a + (week + day + hour + millisec),
1365 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1366 self.assertEqual(a + (week + day + hour + millisec),
1367 (((a + week) + day) + hour) + millisec)
1368 self.assertEqual(a - (week + day + hour + millisec),
1369 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1370 self.assertEqual(a - (week + day + hour + millisec),
1371 (((a - week) - day) - hour) - millisec)
1372 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001373 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001374 self.assertRaises(TypeError, lambda: a+i)
1375 self.assertRaises(TypeError, lambda: a-i)
1376 self.assertRaises(TypeError, lambda: i+a)
1377 self.assertRaises(TypeError, lambda: i-a)
1378
1379 # delta - datetime is senseless.
1380 self.assertRaises(TypeError, lambda: day - a)
1381 # mixing datetime and (delta or datetime) via * or // is senseless
1382 self.assertRaises(TypeError, lambda: day * a)
1383 self.assertRaises(TypeError, lambda: a * day)
1384 self.assertRaises(TypeError, lambda: day // a)
1385 self.assertRaises(TypeError, lambda: a // day)
1386 self.assertRaises(TypeError, lambda: a * a)
1387 self.assertRaises(TypeError, lambda: a // a)
1388 # datetime + datetime is senseless
1389 self.assertRaises(TypeError, lambda: a + a)
1390
1391 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001392 args = 6, 7, 23, 20, 59, 1, 64**2
1393 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001394 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001395 green = pickler.dumps(orig, proto)
1396 derived = unpickler.loads(green)
1397 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001398
Guido van Rossum275666f2003-02-07 21:49:01 +00001399 def test_more_pickling(self):
1400 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1401 s = pickle.dumps(a)
1402 b = pickle.loads(s)
1403 self.assertEqual(b.year, 2003)
1404 self.assertEqual(b.month, 2)
1405 self.assertEqual(b.day, 7)
1406
Tim Peters604c0132004-06-07 23:04:33 +00001407 def test_pickling_subclass_datetime(self):
1408 args = 6, 7, 23, 20, 59, 1, 64**2
1409 orig = SubclassDatetime(*args)
1410 for pickler, unpickler, proto in pickle_choices:
1411 green = pickler.dumps(orig, proto)
1412 derived = unpickler.loads(green)
1413 self.assertEqual(orig, derived)
1414
Tim Peters2a799bf2002-12-16 20:18:38 +00001415 def test_more_compare(self):
1416 # The test_compare() inherited from TestDate covers the error cases.
1417 # We just want to test lexicographic ordering on the members datetime
1418 # has that date lacks.
1419 args = [2000, 11, 29, 20, 58, 16, 999998]
1420 t1 = self.theclass(*args)
1421 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001422 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001423 self.assertTrue(t1 <= t2)
1424 self.assertTrue(t1 >= t2)
1425 self.assertTrue(not t1 != t2)
1426 self.assertTrue(not t1 < t2)
1427 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001428
1429 for i in range(len(args)):
1430 newargs = args[:]
1431 newargs[i] = args[i] + 1
1432 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001433 self.assertTrue(t1 < t2)
1434 self.assertTrue(t2 > t1)
1435 self.assertTrue(t1 <= t2)
1436 self.assertTrue(t2 >= t1)
1437 self.assertTrue(t1 != t2)
1438 self.assertTrue(t2 != t1)
1439 self.assertTrue(not t1 == t2)
1440 self.assertTrue(not t2 == t1)
1441 self.assertTrue(not t1 > t2)
1442 self.assertTrue(not t2 < t1)
1443 self.assertTrue(not t1 >= t2)
1444 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001445
1446
1447 # A helper for timestamp constructor tests.
1448 def verify_field_equality(self, expected, got):
1449 self.assertEqual(expected.tm_year, got.year)
1450 self.assertEqual(expected.tm_mon, got.month)
1451 self.assertEqual(expected.tm_mday, got.day)
1452 self.assertEqual(expected.tm_hour, got.hour)
1453 self.assertEqual(expected.tm_min, got.minute)
1454 self.assertEqual(expected.tm_sec, got.second)
1455
1456 def test_fromtimestamp(self):
1457 import time
1458
1459 ts = time.time()
1460 expected = time.localtime(ts)
1461 got = self.theclass.fromtimestamp(ts)
1462 self.verify_field_equality(expected, got)
1463
1464 def test_utcfromtimestamp(self):
1465 import time
1466
1467 ts = time.time()
1468 expected = time.gmtime(ts)
1469 got = self.theclass.utcfromtimestamp(ts)
1470 self.verify_field_equality(expected, got)
1471
Thomas Wouters477c8d52006-05-27 19:21:47 +00001472 def test_microsecond_rounding(self):
1473 # Test whether fromtimestamp "rounds up" floats that are less
1474 # than one microsecond smaller than an integer.
1475 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1476 self.theclass.fromtimestamp(1))
1477
Tim Peters1b6f7a92004-06-20 02:50:16 +00001478 def test_insane_fromtimestamp(self):
1479 # It's possible that some platform maps time_t to double,
1480 # and that this test will fail there. This test should
1481 # exempt such platforms (provided they return reasonable
1482 # results!).
1483 for insane in -1e200, 1e200:
1484 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1485 insane)
1486
1487 def test_insane_utcfromtimestamp(self):
1488 # It's possible that some platform maps time_t to double,
1489 # and that this test will fail there. This test should
1490 # exempt such platforms (provided they return reasonable
1491 # results!).
1492 for insane in -1e200, 1e200:
1493 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1494 insane)
1495
Guido van Rossumd8faa362007-04-27 19:54:29 +00001496 def test_negative_float_fromtimestamp(self):
1497 # Windows doesn't accept negative timestamps
1498 if os.name == "nt":
1499 return
1500 # The result is tz-dependent; at least test that this doesn't
1501 # fail (like it did before bug 1646728 was fixed).
1502 self.theclass.fromtimestamp(-1.05)
1503
1504 def test_negative_float_utcfromtimestamp(self):
1505 # Windows doesn't accept negative timestamps
1506 if os.name == "nt":
1507 return
1508 d = self.theclass.utcfromtimestamp(-1.05)
1509 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1510
Tim Peters2a799bf2002-12-16 20:18:38 +00001511 def test_utcnow(self):
1512 import time
1513
1514 # Call it a success if utcnow() and utcfromtimestamp() are within
1515 # a second of each other.
1516 tolerance = timedelta(seconds=1)
1517 for dummy in range(3):
1518 from_now = self.theclass.utcnow()
1519 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1520 if abs(from_timestamp - from_now) <= tolerance:
1521 break
1522 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001523 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001524
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001525 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001526 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001527
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001528 string = '2004-12-01 13:02:47.197'
1529 format = '%Y-%m-%d %H:%M:%S.%f'
1530 result, frac = _strptime._strptime(string, format)
1531 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001532 got = self.theclass.strptime(string, format)
1533 self.assertEqual(expected, got)
1534
Tim Peters2a799bf2002-12-16 20:18:38 +00001535 def test_more_timetuple(self):
1536 # This tests fields beyond those tested by the TestDate.test_timetuple.
1537 t = self.theclass(2004, 12, 31, 6, 22, 33)
1538 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1539 self.assertEqual(t.timetuple(),
1540 (t.year, t.month, t.day,
1541 t.hour, t.minute, t.second,
1542 t.weekday(),
1543 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1544 -1))
1545 tt = t.timetuple()
1546 self.assertEqual(tt.tm_year, t.year)
1547 self.assertEqual(tt.tm_mon, t.month)
1548 self.assertEqual(tt.tm_mday, t.day)
1549 self.assertEqual(tt.tm_hour, t.hour)
1550 self.assertEqual(tt.tm_min, t.minute)
1551 self.assertEqual(tt.tm_sec, t.second)
1552 self.assertEqual(tt.tm_wday, t.weekday())
1553 self.assertEqual(tt.tm_yday, t.toordinal() -
1554 date(t.year, 1, 1).toordinal() + 1)
1555 self.assertEqual(tt.tm_isdst, -1)
1556
1557 def test_more_strftime(self):
1558 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001559 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1560 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1561 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001562
1563 def test_extract(self):
1564 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1565 self.assertEqual(dt.date(), date(2002, 3, 4))
1566 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1567
1568 def test_combine(self):
1569 d = date(2002, 3, 4)
1570 t = time(18, 45, 3, 1234)
1571 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1572 combine = self.theclass.combine
1573 dt = combine(d, t)
1574 self.assertEqual(dt, expected)
1575
1576 dt = combine(time=t, date=d)
1577 self.assertEqual(dt, expected)
1578
1579 self.assertEqual(d, dt.date())
1580 self.assertEqual(t, dt.time())
1581 self.assertEqual(dt, combine(dt.date(), dt.time()))
1582
1583 self.assertRaises(TypeError, combine) # need an arg
1584 self.assertRaises(TypeError, combine, d) # need two args
1585 self.assertRaises(TypeError, combine, t, d) # args reversed
1586 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1587 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1588
Tim Peters12bf3392002-12-24 05:41:27 +00001589 def test_replace(self):
1590 cls = self.theclass
1591 args = [1, 2, 3, 4, 5, 6, 7]
1592 base = cls(*args)
1593 self.assertEqual(base, base.replace())
1594
1595 i = 0
1596 for name, newval in (("year", 2),
1597 ("month", 3),
1598 ("day", 4),
1599 ("hour", 5),
1600 ("minute", 6),
1601 ("second", 7),
1602 ("microsecond", 8)):
1603 newargs = args[:]
1604 newargs[i] = newval
1605 expected = cls(*newargs)
1606 got = base.replace(**{name: newval})
1607 self.assertEqual(expected, got)
1608 i += 1
1609
1610 # Out of bounds.
1611 base = cls(2000, 2, 29)
1612 self.assertRaises(ValueError, base.replace, year=2001)
1613
Tim Peters80475bb2002-12-25 07:40:55 +00001614 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001615 # Pretty boring! The TZ test is more interesting here. astimezone()
1616 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001617 dt = self.theclass.now()
1618 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001619 self.assertRaises(TypeError, dt.astimezone) # not enough args
1620 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1621 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001622 self.assertRaises(ValueError, dt.astimezone, f) # naive
1623 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001624
Tim Peters52dcce22003-01-23 16:36:11 +00001625 class Bogus(tzinfo):
1626 def utcoffset(self, dt): return None
1627 def dst(self, dt): return timedelta(0)
1628 bog = Bogus()
1629 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1630
1631 class AlsoBogus(tzinfo):
1632 def utcoffset(self, dt): return timedelta(0)
1633 def dst(self, dt): return None
1634 alsobog = AlsoBogus()
1635 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001636
Tim Petersa98924a2003-05-17 05:55:19 +00001637 def test_subclass_datetime(self):
1638
1639 class C(self.theclass):
1640 theAnswer = 42
1641
1642 def __new__(cls, *args, **kws):
1643 temp = kws.copy()
1644 extra = temp.pop('extra')
1645 result = self.theclass.__new__(cls, *args, **temp)
1646 result.extra = extra
1647 return result
1648
1649 def newmeth(self, start):
1650 return start + self.year + self.month + self.second
1651
1652 args = 2003, 4, 14, 12, 13, 41
1653
1654 dt1 = self.theclass(*args)
1655 dt2 = C(*args, **{'extra': 7})
1656
1657 self.assertEqual(dt2.__class__, C)
1658 self.assertEqual(dt2.theAnswer, 42)
1659 self.assertEqual(dt2.extra, 7)
1660 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1661 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1662 dt1.second - 7)
1663
Tim Peters604c0132004-06-07 23:04:33 +00001664class SubclassTime(time):
1665 sub_var = 1
1666
Guido van Rossumd8faa362007-04-27 19:54:29 +00001667class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001668
1669 theclass = time
1670
1671 def test_basic_attributes(self):
1672 t = self.theclass(12, 0)
1673 self.assertEqual(t.hour, 12)
1674 self.assertEqual(t.minute, 0)
1675 self.assertEqual(t.second, 0)
1676 self.assertEqual(t.microsecond, 0)
1677
1678 def test_basic_attributes_nonzero(self):
1679 # Make sure all attributes are non-zero so bugs in
1680 # bit-shifting access show up.
1681 t = self.theclass(12, 59, 59, 8000)
1682 self.assertEqual(t.hour, 12)
1683 self.assertEqual(t.minute, 59)
1684 self.assertEqual(t.second, 59)
1685 self.assertEqual(t.microsecond, 8000)
1686
1687 def test_roundtrip(self):
1688 t = self.theclass(1, 2, 3, 4)
1689
1690 # Verify t -> string -> time identity.
1691 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001692 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001693 s = s[9:]
1694 t2 = eval(s)
1695 self.assertEqual(t, t2)
1696
1697 # Verify identity via reconstructing from pieces.
1698 t2 = self.theclass(t.hour, t.minute, t.second,
1699 t.microsecond)
1700 self.assertEqual(t, t2)
1701
1702 def test_comparing(self):
1703 args = [1, 2, 3, 4]
1704 t1 = self.theclass(*args)
1705 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001706 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001707 self.assertTrue(t1 <= t2)
1708 self.assertTrue(t1 >= t2)
1709 self.assertTrue(not t1 != t2)
1710 self.assertTrue(not t1 < t2)
1711 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001712
1713 for i in range(len(args)):
1714 newargs = args[:]
1715 newargs[i] = args[i] + 1
1716 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001717 self.assertTrue(t1 < t2)
1718 self.assertTrue(t2 > t1)
1719 self.assertTrue(t1 <= t2)
1720 self.assertTrue(t2 >= t1)
1721 self.assertTrue(t1 != t2)
1722 self.assertTrue(t2 != t1)
1723 self.assertTrue(not t1 == t2)
1724 self.assertTrue(not t2 == t1)
1725 self.assertTrue(not t1 > t2)
1726 self.assertTrue(not t2 < t1)
1727 self.assertTrue(not t1 >= t2)
1728 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001729
Tim Peters68124bb2003-02-08 03:46:31 +00001730 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001731 self.assertEqual(t1 == badarg, False)
1732 self.assertEqual(t1 != badarg, True)
1733 self.assertEqual(badarg == t1, False)
1734 self.assertEqual(badarg != t1, True)
1735
Tim Peters2a799bf2002-12-16 20:18:38 +00001736 self.assertRaises(TypeError, lambda: t1 <= badarg)
1737 self.assertRaises(TypeError, lambda: t1 < badarg)
1738 self.assertRaises(TypeError, lambda: t1 > badarg)
1739 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001740 self.assertRaises(TypeError, lambda: badarg <= t1)
1741 self.assertRaises(TypeError, lambda: badarg < t1)
1742 self.assertRaises(TypeError, lambda: badarg > t1)
1743 self.assertRaises(TypeError, lambda: badarg >= t1)
1744
1745 def test_bad_constructor_arguments(self):
1746 # bad hours
1747 self.theclass(0, 0) # no exception
1748 self.theclass(23, 0) # no exception
1749 self.assertRaises(ValueError, self.theclass, -1, 0)
1750 self.assertRaises(ValueError, self.theclass, 24, 0)
1751 # bad minutes
1752 self.theclass(23, 0) # no exception
1753 self.theclass(23, 59) # no exception
1754 self.assertRaises(ValueError, self.theclass, 23, -1)
1755 self.assertRaises(ValueError, self.theclass, 23, 60)
1756 # bad seconds
1757 self.theclass(23, 59, 0) # no exception
1758 self.theclass(23, 59, 59) # no exception
1759 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1760 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1761 # bad microseconds
1762 self.theclass(23, 59, 59, 0) # no exception
1763 self.theclass(23, 59, 59, 999999) # no exception
1764 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1765 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1766
1767 def test_hash_equality(self):
1768 d = self.theclass(23, 30, 17)
1769 e = self.theclass(23, 30, 17)
1770 self.assertEqual(d, e)
1771 self.assertEqual(hash(d), hash(e))
1772
1773 dic = {d: 1}
1774 dic[e] = 2
1775 self.assertEqual(len(dic), 1)
1776 self.assertEqual(dic[d], 2)
1777 self.assertEqual(dic[e], 2)
1778
1779 d = self.theclass(0, 5, 17)
1780 e = self.theclass(0, 5, 17)
1781 self.assertEqual(d, e)
1782 self.assertEqual(hash(d), hash(e))
1783
1784 dic = {d: 1}
1785 dic[e] = 2
1786 self.assertEqual(len(dic), 1)
1787 self.assertEqual(dic[d], 2)
1788 self.assertEqual(dic[e], 2)
1789
1790 def test_isoformat(self):
1791 t = self.theclass(4, 5, 1, 123)
1792 self.assertEqual(t.isoformat(), "04:05:01.000123")
1793 self.assertEqual(t.isoformat(), str(t))
1794
1795 t = self.theclass()
1796 self.assertEqual(t.isoformat(), "00:00:00")
1797 self.assertEqual(t.isoformat(), str(t))
1798
1799 t = self.theclass(microsecond=1)
1800 self.assertEqual(t.isoformat(), "00:00:00.000001")
1801 self.assertEqual(t.isoformat(), str(t))
1802
1803 t = self.theclass(microsecond=10)
1804 self.assertEqual(t.isoformat(), "00:00:00.000010")
1805 self.assertEqual(t.isoformat(), str(t))
1806
1807 t = self.theclass(microsecond=100)
1808 self.assertEqual(t.isoformat(), "00:00:00.000100")
1809 self.assertEqual(t.isoformat(), str(t))
1810
1811 t = self.theclass(microsecond=1000)
1812 self.assertEqual(t.isoformat(), "00:00:00.001000")
1813 self.assertEqual(t.isoformat(), str(t))
1814
1815 t = self.theclass(microsecond=10000)
1816 self.assertEqual(t.isoformat(), "00:00:00.010000")
1817 self.assertEqual(t.isoformat(), str(t))
1818
1819 t = self.theclass(microsecond=100000)
1820 self.assertEqual(t.isoformat(), "00:00:00.100000")
1821 self.assertEqual(t.isoformat(), str(t))
1822
Thomas Wouterscf297e42007-02-23 15:07:44 +00001823 def test_1653736(self):
1824 # verify it doesn't accept extra keyword arguments
1825 t = self.theclass(second=1)
1826 self.assertRaises(TypeError, t.isoformat, foo=3)
1827
Tim Peters2a799bf2002-12-16 20:18:38 +00001828 def test_strftime(self):
1829 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001830 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001831 # A naive object replaces %z and %Z with empty strings.
1832 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1833
Eric Smith1ba31142007-09-11 18:06:02 +00001834 def test_format(self):
1835 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001836 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001837
1838 # check that a derived class's __str__() gets called
1839 class A(self.theclass):
1840 def __str__(self):
1841 return 'A'
1842 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001843 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001844
1845 # check that a derived class's strftime gets called
1846 class B(self.theclass):
1847 def strftime(self, format_spec):
1848 return 'B'
1849 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001850 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001851
1852 for fmt in ['%H %M %S',
1853 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001854 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1855 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1856 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001857
Tim Peters2a799bf2002-12-16 20:18:38 +00001858 def test_str(self):
1859 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1860 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1861 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1862 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1863 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1864
1865 def test_repr(self):
1866 name = 'datetime.' + self.theclass.__name__
1867 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1868 "%s(1, 2, 3, 4)" % name)
1869 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1870 "%s(10, 2, 3, 4000)" % name)
1871 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1872 "%s(0, 2, 3, 400000)" % name)
1873 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1874 "%s(12, 2, 3)" % name)
1875 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1876 "%s(23, 15)" % name)
1877
1878 def test_resolution_info(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001879 self.assertTrue(isinstance(self.theclass.min, self.theclass))
1880 self.assertTrue(isinstance(self.theclass.max, self.theclass))
1881 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
1882 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001883
1884 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001885 args = 20, 59, 16, 64**2
1886 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001887 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001888 green = pickler.dumps(orig, proto)
1889 derived = unpickler.loads(green)
1890 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001891
Tim Peters604c0132004-06-07 23:04:33 +00001892 def test_pickling_subclass_time(self):
1893 args = 20, 59, 16, 64**2
1894 orig = SubclassTime(*args)
1895 for pickler, unpickler, proto in pickle_choices:
1896 green = pickler.dumps(orig, proto)
1897 derived = unpickler.loads(green)
1898 self.assertEqual(orig, derived)
1899
Tim Peters2a799bf2002-12-16 20:18:38 +00001900 def test_bool(self):
1901 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001902 self.assertTrue(cls(1))
1903 self.assertTrue(cls(0, 1))
1904 self.assertTrue(cls(0, 0, 1))
1905 self.assertTrue(cls(0, 0, 0, 1))
1906 self.assertTrue(not cls(0))
1907 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001908
Tim Peters12bf3392002-12-24 05:41:27 +00001909 def test_replace(self):
1910 cls = self.theclass
1911 args = [1, 2, 3, 4]
1912 base = cls(*args)
1913 self.assertEqual(base, base.replace())
1914
1915 i = 0
1916 for name, newval in (("hour", 5),
1917 ("minute", 6),
1918 ("second", 7),
1919 ("microsecond", 8)):
1920 newargs = args[:]
1921 newargs[i] = newval
1922 expected = cls(*newargs)
1923 got = base.replace(**{name: newval})
1924 self.assertEqual(expected, got)
1925 i += 1
1926
1927 # Out of bounds.
1928 base = cls(1)
1929 self.assertRaises(ValueError, base.replace, hour=24)
1930 self.assertRaises(ValueError, base.replace, minute=-1)
1931 self.assertRaises(ValueError, base.replace, second=100)
1932 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1933
Tim Petersa98924a2003-05-17 05:55:19 +00001934 def test_subclass_time(self):
1935
1936 class C(self.theclass):
1937 theAnswer = 42
1938
1939 def __new__(cls, *args, **kws):
1940 temp = kws.copy()
1941 extra = temp.pop('extra')
1942 result = self.theclass.__new__(cls, *args, **temp)
1943 result.extra = extra
1944 return result
1945
1946 def newmeth(self, start):
1947 return start + self.hour + self.second
1948
1949 args = 4, 5, 6
1950
1951 dt1 = self.theclass(*args)
1952 dt2 = C(*args, **{'extra': 7})
1953
1954 self.assertEqual(dt2.__class__, C)
1955 self.assertEqual(dt2.theAnswer, 42)
1956 self.assertEqual(dt2.extra, 7)
1957 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1958 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1959
Armin Rigof4afb212005-11-07 07:15:48 +00001960 def test_backdoor_resistance(self):
1961 # see TestDate.test_backdoor_resistance().
1962 base = '2:59.0'
1963 for hour_byte in ' ', '9', chr(24), '\xff':
1964 self.assertRaises(TypeError, self.theclass,
1965 hour_byte + base[1:])
1966
Tim Peters855fe882002-12-22 03:43:39 +00001967# A mixin for classes with a tzinfo= argument. Subclasses must define
1968# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001969# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001970class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001971
Tim Petersbad8ff02002-12-30 20:52:32 +00001972 def test_argument_passing(self):
1973 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001974 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001975 class introspective(tzinfo):
1976 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001977 def utcoffset(self, dt):
1978 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001979 dst = utcoffset
1980
1981 obj = cls(1, 2, 3, tzinfo=introspective())
1982
Tim Peters0bf60bd2003-01-08 20:40:01 +00001983 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001984 self.assertEqual(obj.tzname(), expected)
1985
Tim Peters0bf60bd2003-01-08 20:40:01 +00001986 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001987 self.assertEqual(obj.utcoffset(), expected)
1988 self.assertEqual(obj.dst(), expected)
1989
Tim Peters855fe882002-12-22 03:43:39 +00001990 def test_bad_tzinfo_classes(self):
1991 cls = self.theclass
1992 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001993
Tim Peters855fe882002-12-22 03:43:39 +00001994 class NiceTry(object):
1995 def __init__(self): pass
1996 def utcoffset(self, dt): pass
1997 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1998
1999 class BetterTry(tzinfo):
2000 def __init__(self): pass
2001 def utcoffset(self, dt): pass
2002 b = BetterTry()
2003 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002004 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002005
2006 def test_utc_offset_out_of_bounds(self):
2007 class Edgy(tzinfo):
2008 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002009 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002010 def utcoffset(self, dt):
2011 return self.offset
2012
2013 cls = self.theclass
2014 for offset, legit in ((-1440, False),
2015 (-1439, True),
2016 (1439, True),
2017 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002018 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002019 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002020 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002021 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002022 else:
2023 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002024 if legit:
2025 aofs = abs(offset)
2026 h, m = divmod(aofs, 60)
2027 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002028 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002029 t = t.timetz()
2030 self.assertEqual(str(t), "01:02:03" + tag)
2031 else:
2032 self.assertRaises(ValueError, str, t)
2033
2034 def test_tzinfo_classes(self):
2035 cls = self.theclass
2036 class C1(tzinfo):
2037 def utcoffset(self, dt): return None
2038 def dst(self, dt): return None
2039 def tzname(self, dt): return None
2040 for t in (cls(1, 1, 1),
2041 cls(1, 1, 1, tzinfo=None),
2042 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002043 self.assertTrue(t.utcoffset() is None)
2044 self.assertTrue(t.dst() is None)
2045 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002046
Tim Peters855fe882002-12-22 03:43:39 +00002047 class C3(tzinfo):
2048 def utcoffset(self, dt): return timedelta(minutes=-1439)
2049 def dst(self, dt): return timedelta(minutes=1439)
2050 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002051 t = cls(1, 1, 1, tzinfo=C3())
2052 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2053 self.assertEqual(t.dst(), timedelta(minutes=1439))
2054 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002055
2056 # Wrong types.
2057 class C4(tzinfo):
2058 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002059 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002060 def tzname(self, dt): return 0
2061 t = cls(1, 1, 1, tzinfo=C4())
2062 self.assertRaises(TypeError, t.utcoffset)
2063 self.assertRaises(TypeError, t.dst)
2064 self.assertRaises(TypeError, t.tzname)
2065
2066 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002067 class C6(tzinfo):
2068 def utcoffset(self, dt): return timedelta(hours=-24)
2069 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002070 t = cls(1, 1, 1, tzinfo=C6())
2071 self.assertRaises(ValueError, t.utcoffset)
2072 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002073
2074 # Not a whole number of minutes.
2075 class C7(tzinfo):
2076 def utcoffset(self, dt): return timedelta(seconds=61)
2077 def dst(self, dt): return timedelta(microseconds=-81)
2078 t = cls(1, 1, 1, tzinfo=C7())
2079 self.assertRaises(ValueError, t.utcoffset)
2080 self.assertRaises(ValueError, t.dst)
2081
Tim Peters4c0db782002-12-26 05:01:19 +00002082 def test_aware_compare(self):
2083 cls = self.theclass
2084
Tim Peters60c76e42002-12-27 00:41:11 +00002085 # Ensure that utcoffset() gets ignored if the comparands have
2086 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002087 class OperandDependentOffset(tzinfo):
2088 def utcoffset(self, t):
2089 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002090 # d0 and d1 equal after adjustment
2091 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002092 else:
Tim Peters397301e2003-01-02 21:28:08 +00002093 # d2 off in the weeds
2094 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002095
2096 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2097 d0 = base.replace(minute=3)
2098 d1 = base.replace(minute=9)
2099 d2 = base.replace(minute=11)
2100 for x in d0, d1, d2:
2101 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002102 for op in lt, le, gt, ge, eq, ne:
2103 got = op(x, y)
2104 expected = op(x.minute, y.minute)
2105 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002106
2107 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002108 # Note that a time can't actually have an operand-depedent offset,
2109 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2110 # so skip this test for time.
2111 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002112 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2113 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2114 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2115 for x in d0, d1, d2:
2116 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002117 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002118 if (x is d0 or x is d1) and (y is d0 or y is d1):
2119 expected = 0
2120 elif x is y is d2:
2121 expected = 0
2122 elif x is d2:
2123 expected = -1
2124 else:
2125 assert y is d2
2126 expected = 1
2127 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002128
Tim Peters855fe882002-12-22 03:43:39 +00002129
Tim Peters0bf60bd2003-01-08 20:40:01 +00002130# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002131class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002132 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002133
2134 def test_empty(self):
2135 t = self.theclass()
2136 self.assertEqual(t.hour, 0)
2137 self.assertEqual(t.minute, 0)
2138 self.assertEqual(t.second, 0)
2139 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002140 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002141
Tim Peters2a799bf2002-12-16 20:18:38 +00002142 def test_zones(self):
2143 est = FixedOffset(-300, "EST", 1)
2144 utc = FixedOffset(0, "UTC", -2)
2145 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002146 t1 = time( 7, 47, tzinfo=est)
2147 t2 = time(12, 47, tzinfo=utc)
2148 t3 = time(13, 47, tzinfo=met)
2149 t4 = time(microsecond=40)
2150 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002151
2152 self.assertEqual(t1.tzinfo, est)
2153 self.assertEqual(t2.tzinfo, utc)
2154 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002155 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002156 self.assertEqual(t5.tzinfo, utc)
2157
Tim Peters855fe882002-12-22 03:43:39 +00002158 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2159 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2160 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002161 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002162 self.assertRaises(TypeError, t1.utcoffset, "no args")
2163
2164 self.assertEqual(t1.tzname(), "EST")
2165 self.assertEqual(t2.tzname(), "UTC")
2166 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002167 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002168 self.assertRaises(TypeError, t1.tzname, "no args")
2169
Tim Peters855fe882002-12-22 03:43:39 +00002170 self.assertEqual(t1.dst(), timedelta(minutes=1))
2171 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2172 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002173 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002174 self.assertRaises(TypeError, t1.dst, "no args")
2175
2176 self.assertEqual(hash(t1), hash(t2))
2177 self.assertEqual(hash(t1), hash(t3))
2178 self.assertEqual(hash(t2), hash(t3))
2179
2180 self.assertEqual(t1, t2)
2181 self.assertEqual(t1, t3)
2182 self.assertEqual(t2, t3)
2183 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2184 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2185 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2186
2187 self.assertEqual(str(t1), "07:47:00-05:00")
2188 self.assertEqual(str(t2), "12:47:00+00:00")
2189 self.assertEqual(str(t3), "13:47:00+01:00")
2190 self.assertEqual(str(t4), "00:00:00.000040")
2191 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2192
2193 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2194 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2195 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2196 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2197 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2198
Tim Peters0bf60bd2003-01-08 20:40:01 +00002199 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002200 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2201 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2202 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2203 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2204 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2205
2206 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2207 "07:47:00 %Z=EST %z=-0500")
2208 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2209 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2210
2211 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002212 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002213 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2214 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2215
Tim Petersb92bb712002-12-21 17:44:07 +00002216 # Check that an invalid tzname result raises an exception.
2217 class Badtzname(tzinfo):
2218 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002219 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002220 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2221 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002222
2223 def test_hash_edge_cases(self):
2224 # Offsets that overflow a basic time.
2225 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2226 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2227 self.assertEqual(hash(t1), hash(t2))
2228
2229 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2230 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2231 self.assertEqual(hash(t1), hash(t2))
2232
Tim Peters2a799bf2002-12-16 20:18:38 +00002233 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002234 # Try one without a tzinfo.
2235 args = 20, 59, 16, 64**2
2236 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002237 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002238 green = pickler.dumps(orig, proto)
2239 derived = unpickler.loads(green)
2240 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002241
2242 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002243 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002244 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002245 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002246 green = pickler.dumps(orig, proto)
2247 derived = unpickler.loads(green)
2248 self.assertEqual(orig, derived)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002249 self.assertTrue(isinstance(derived.tzinfo, PicklableFixedOffset))
Tim Peters96940c92003-01-31 21:55:33 +00002250 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2251 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002252
2253 def test_more_bool(self):
2254 # Test cases with non-None tzinfo.
2255 cls = self.theclass
2256
2257 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002258 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002259
2260 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002261 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002262
2263 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002264 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002265
2266 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002267 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002268
2269 # Mostly ensuring this doesn't overflow internally.
2270 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002271 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002272
2273 # But this should yield a value error -- the utcoffset is bogus.
2274 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2275 self.assertRaises(ValueError, lambda: bool(t))
2276
2277 # Likewise.
2278 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2279 self.assertRaises(ValueError, lambda: bool(t))
2280
Tim Peters12bf3392002-12-24 05:41:27 +00002281 def test_replace(self):
2282 cls = self.theclass
2283 z100 = FixedOffset(100, "+100")
2284 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2285 args = [1, 2, 3, 4, z100]
2286 base = cls(*args)
2287 self.assertEqual(base, base.replace())
2288
2289 i = 0
2290 for name, newval in (("hour", 5),
2291 ("minute", 6),
2292 ("second", 7),
2293 ("microsecond", 8),
2294 ("tzinfo", zm200)):
2295 newargs = args[:]
2296 newargs[i] = newval
2297 expected = cls(*newargs)
2298 got = base.replace(**{name: newval})
2299 self.assertEqual(expected, got)
2300 i += 1
2301
2302 # Ensure we can get rid of a tzinfo.
2303 self.assertEqual(base.tzname(), "+100")
2304 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002305 self.assertTrue(base2.tzinfo is None)
2306 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002307
2308 # Ensure we can add one.
2309 base3 = base2.replace(tzinfo=z100)
2310 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002311 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002312
2313 # Out of bounds.
2314 base = cls(1)
2315 self.assertRaises(ValueError, base.replace, hour=24)
2316 self.assertRaises(ValueError, base.replace, minute=-1)
2317 self.assertRaises(ValueError, base.replace, second=100)
2318 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2319
Tim Peters60c76e42002-12-27 00:41:11 +00002320 def test_mixed_compare(self):
2321 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002322 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002323 self.assertEqual(t1, t2)
2324 t2 = t2.replace(tzinfo=None)
2325 self.assertEqual(t1, t2)
2326 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2327 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002328 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2329 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002330
Tim Peters0bf60bd2003-01-08 20:40:01 +00002331 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002332 class Varies(tzinfo):
2333 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002334 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002335 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002336 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002337 return self.offset
2338
2339 v = Varies()
2340 t1 = t2.replace(tzinfo=v)
2341 t2 = t2.replace(tzinfo=v)
2342 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2343 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2344 self.assertEqual(t1, t2)
2345
2346 # But if they're not identical, it isn't ignored.
2347 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002348 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002349
Tim Petersa98924a2003-05-17 05:55:19 +00002350 def test_subclass_timetz(self):
2351
2352 class C(self.theclass):
2353 theAnswer = 42
2354
2355 def __new__(cls, *args, **kws):
2356 temp = kws.copy()
2357 extra = temp.pop('extra')
2358 result = self.theclass.__new__(cls, *args, **temp)
2359 result.extra = extra
2360 return result
2361
2362 def newmeth(self, start):
2363 return start + self.hour + self.second
2364
2365 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2366
2367 dt1 = self.theclass(*args)
2368 dt2 = C(*args, **{'extra': 7})
2369
2370 self.assertEqual(dt2.__class__, C)
2371 self.assertEqual(dt2.theAnswer, 42)
2372 self.assertEqual(dt2.extra, 7)
2373 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2374 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2375
Tim Peters4c0db782002-12-26 05:01:19 +00002376
Tim Peters0bf60bd2003-01-08 20:40:01 +00002377# Testing datetime objects with a non-None tzinfo.
2378
Guido van Rossumd8faa362007-04-27 19:54:29 +00002379class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002380 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002381
2382 def test_trivial(self):
2383 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2384 self.assertEqual(dt.year, 1)
2385 self.assertEqual(dt.month, 2)
2386 self.assertEqual(dt.day, 3)
2387 self.assertEqual(dt.hour, 4)
2388 self.assertEqual(dt.minute, 5)
2389 self.assertEqual(dt.second, 6)
2390 self.assertEqual(dt.microsecond, 7)
2391 self.assertEqual(dt.tzinfo, None)
2392
2393 def test_even_more_compare(self):
2394 # The test_compare() and test_more_compare() inherited from TestDate
2395 # and TestDateTime covered non-tzinfo cases.
2396
2397 # Smallest possible after UTC adjustment.
2398 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2399 # Largest possible after UTC adjustment.
2400 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2401 tzinfo=FixedOffset(-1439, ""))
2402
2403 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002404 self.assertTrue(t1 < t2)
2405 self.assertTrue(t1 != t2)
2406 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002407
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002408 self.assertEqual(t1, t1)
2409 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002410
2411 # Equal afer adjustment.
2412 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2413 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2414 self.assertEqual(t1, t2)
2415
2416 # Change t1 not to subtract a minute, and t1 should be larger.
2417 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002418 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002419
2420 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2421 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002422 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002423
2424 # Back to the original t1, but make seconds resolve it.
2425 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2426 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002427 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002428
2429 # Likewise, but make microseconds resolve it.
2430 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2431 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002432 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002433
2434 # Make t2 naive and it should fail.
2435 t2 = self.theclass.min
2436 self.assertRaises(TypeError, lambda: t1 == t2)
2437 self.assertEqual(t2, t2)
2438
2439 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2440 class Naive(tzinfo):
2441 def utcoffset(self, dt): return None
2442 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2443 self.assertRaises(TypeError, lambda: t1 == t2)
2444 self.assertEqual(t2, t2)
2445
2446 # OTOH, it's OK to compare two of these mixing the two ways of being
2447 # naive.
2448 t1 = self.theclass(5, 6, 7)
2449 self.assertEqual(t1, t2)
2450
2451 # Try a bogus uctoffset.
2452 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002453 def utcoffset(self, dt):
2454 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002455 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2456 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002457 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002458
Tim Peters2a799bf2002-12-16 20:18:38 +00002459 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002460 # Try one without a tzinfo.
2461 args = 6, 7, 23, 20, 59, 1, 64**2
2462 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002463 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002464 green = pickler.dumps(orig, proto)
2465 derived = unpickler.loads(green)
2466 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002467
2468 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002469 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002470 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002471 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002472 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002473 green = pickler.dumps(orig, proto)
2474 derived = unpickler.loads(green)
2475 self.assertEqual(orig, derived)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002476 self.assertTrue(isinstance(derived.tzinfo,
Tim Peters96940c92003-01-31 21:55:33 +00002477 PicklableFixedOffset))
2478 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2479 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002480
2481 def test_extreme_hashes(self):
2482 # If an attempt is made to hash these via subtracting the offset
2483 # then hashing a datetime object, OverflowError results. The
2484 # Python implementation used to blow up here.
2485 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2486 hash(t)
2487 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2488 tzinfo=FixedOffset(-1439, ""))
2489 hash(t)
2490
2491 # OTOH, an OOB offset should blow up.
2492 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2493 self.assertRaises(ValueError, hash, t)
2494
2495 def test_zones(self):
2496 est = FixedOffset(-300, "EST")
2497 utc = FixedOffset(0, "UTC")
2498 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002499 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2500 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2501 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002502 self.assertEqual(t1.tzinfo, est)
2503 self.assertEqual(t2.tzinfo, utc)
2504 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002505 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2506 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2507 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002508 self.assertEqual(t1.tzname(), "EST")
2509 self.assertEqual(t2.tzname(), "UTC")
2510 self.assertEqual(t3.tzname(), "MET")
2511 self.assertEqual(hash(t1), hash(t2))
2512 self.assertEqual(hash(t1), hash(t3))
2513 self.assertEqual(hash(t2), hash(t3))
2514 self.assertEqual(t1, t2)
2515 self.assertEqual(t1, t3)
2516 self.assertEqual(t2, t3)
2517 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2518 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2519 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002520 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002521 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2522 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2523 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2524
2525 def test_combine(self):
2526 met = FixedOffset(60, "MET")
2527 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002528 tz = time(18, 45, 3, 1234, tzinfo=met)
2529 dt = datetime.combine(d, tz)
2530 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002531 tzinfo=met))
2532
2533 def test_extract(self):
2534 met = FixedOffset(60, "MET")
2535 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2536 self.assertEqual(dt.date(), date(2002, 3, 4))
2537 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002538 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002539
2540 def test_tz_aware_arithmetic(self):
2541 import random
2542
2543 now = self.theclass.now()
2544 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002545 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002546 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002547 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002548 self.assertEqual(nowaware.timetz(), timeaware)
2549
2550 # Can't mix aware and non-aware.
2551 self.assertRaises(TypeError, lambda: now - nowaware)
2552 self.assertRaises(TypeError, lambda: nowaware - now)
2553
Tim Peters0bf60bd2003-01-08 20:40:01 +00002554 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002555 self.assertRaises(TypeError, lambda: now + nowaware)
2556 self.assertRaises(TypeError, lambda: nowaware + now)
2557 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2558
2559 # Subtracting should yield 0.
2560 self.assertEqual(now - now, timedelta(0))
2561 self.assertEqual(nowaware - nowaware, timedelta(0))
2562
2563 # Adding a delta should preserve tzinfo.
2564 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2565 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002566 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002567 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002568 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002569 self.assertEqual(nowawareplus, nowawareplus2)
2570
2571 # that - delta should be what we started with, and that - what we
2572 # started with should be delta.
2573 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002574 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002575 self.assertEqual(nowaware, diff)
2576 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2577 self.assertEqual(nowawareplus - nowaware, delta)
2578
2579 # Make up a random timezone.
2580 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002581 # Attach it to nowawareplus.
2582 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002583 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002584 # Make sure the difference takes the timezone adjustments into account.
2585 got = nowaware - nowawareplus
2586 # Expected: (nowaware base - nowaware offset) -
2587 # (nowawareplus base - nowawareplus offset) =
2588 # (nowaware base - nowawareplus base) +
2589 # (nowawareplus offset - nowaware offset) =
2590 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002591 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002592 self.assertEqual(got, expected)
2593
2594 # Try max possible difference.
2595 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2596 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2597 tzinfo=FixedOffset(-1439, "max"))
2598 maxdiff = max - min
2599 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2600 timedelta(minutes=2*1439))
2601
2602 def test_tzinfo_now(self):
2603 meth = self.theclass.now
2604 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2605 base = meth()
2606 # Try with and without naming the keyword.
2607 off42 = FixedOffset(42, "42")
2608 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002609 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002610 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002611 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002612 # Bad argument with and w/o naming the keyword.
2613 self.assertRaises(TypeError, meth, 16)
2614 self.assertRaises(TypeError, meth, tzinfo=16)
2615 # Bad keyword name.
2616 self.assertRaises(TypeError, meth, tinfo=off42)
2617 # Too many args.
2618 self.assertRaises(TypeError, meth, off42, off42)
2619
Tim Peters10cadce2003-01-23 19:58:02 +00002620 # We don't know which time zone we're in, and don't have a tzinfo
2621 # class to represent it, so seeing whether a tz argument actually
2622 # does a conversion is tricky.
2623 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2624 utc = FixedOffset(0, "utc", 0)
2625 for dummy in range(3):
2626 now = datetime.now(weirdtz)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002627 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002628 utcnow = datetime.utcnow().replace(tzinfo=utc)
2629 now2 = utcnow.astimezone(weirdtz)
2630 if abs(now - now2) < timedelta(seconds=30):
2631 break
2632 # Else the code is broken, or more than 30 seconds passed between
2633 # calls; assuming the latter, just try again.
2634 else:
2635 # Three strikes and we're out.
2636 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2637
Tim Peters2a799bf2002-12-16 20:18:38 +00002638 def test_tzinfo_fromtimestamp(self):
2639 import time
2640 meth = self.theclass.fromtimestamp
2641 ts = time.time()
2642 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2643 base = meth(ts)
2644 # Try with and without naming the keyword.
2645 off42 = FixedOffset(42, "42")
2646 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002647 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002648 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002649 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002650 # Bad argument with and w/o naming the keyword.
2651 self.assertRaises(TypeError, meth, ts, 16)
2652 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2653 # Bad keyword name.
2654 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2655 # Too many args.
2656 self.assertRaises(TypeError, meth, ts, off42, off42)
2657 # Too few args.
2658 self.assertRaises(TypeError, meth)
2659
Tim Peters2a44a8d2003-01-23 20:53:10 +00002660 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002661 timestamp = 1000000000
2662 utcdatetime = datetime.utcfromtimestamp(timestamp)
2663 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2664 # But on some flavor of Mac, it's nowhere near that. So we can't have
2665 # any idea here what time that actually is, we can only test that
2666 # relative changes match.
2667 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2668 tz = FixedOffset(utcoffset, "tz", 0)
2669 expected = utcdatetime + utcoffset
2670 got = datetime.fromtimestamp(timestamp, tz)
2671 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002672
Tim Peters2a799bf2002-12-16 20:18:38 +00002673 def test_tzinfo_utcnow(self):
2674 meth = self.theclass.utcnow
2675 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2676 base = meth()
2677 # Try with and without naming the keyword; for whatever reason,
2678 # utcnow() doesn't accept a tzinfo argument.
2679 off42 = FixedOffset(42, "42")
2680 self.assertRaises(TypeError, meth, off42)
2681 self.assertRaises(TypeError, meth, tzinfo=off42)
2682
2683 def test_tzinfo_utcfromtimestamp(self):
2684 import time
2685 meth = self.theclass.utcfromtimestamp
2686 ts = time.time()
2687 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2688 base = meth(ts)
2689 # Try with and without naming the keyword; for whatever reason,
2690 # utcfromtimestamp() doesn't accept a tzinfo argument.
2691 off42 = FixedOffset(42, "42")
2692 self.assertRaises(TypeError, meth, ts, off42)
2693 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2694
2695 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002696 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002697 # DST flag.
2698 class DST(tzinfo):
2699 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002700 if isinstance(dstvalue, int):
2701 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002702 self.dstvalue = dstvalue
2703 def dst(self, dt):
2704 return self.dstvalue
2705
2706 cls = self.theclass
2707 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2708 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2709 t = d.timetuple()
2710 self.assertEqual(1, t.tm_year)
2711 self.assertEqual(1, t.tm_mon)
2712 self.assertEqual(1, t.tm_mday)
2713 self.assertEqual(10, t.tm_hour)
2714 self.assertEqual(20, t.tm_min)
2715 self.assertEqual(30, t.tm_sec)
2716 self.assertEqual(0, t.tm_wday)
2717 self.assertEqual(1, t.tm_yday)
2718 self.assertEqual(flag, t.tm_isdst)
2719
2720 # dst() returns wrong type.
2721 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2722
2723 # dst() at the edge.
2724 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2725 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2726
2727 # dst() out of range.
2728 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2729 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2730
2731 def test_utctimetuple(self):
2732 class DST(tzinfo):
2733 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002734 if isinstance(dstvalue, int):
2735 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002736 self.dstvalue = dstvalue
2737 def dst(self, dt):
2738 return self.dstvalue
2739
2740 cls = self.theclass
2741 # This can't work: DST didn't implement utcoffset.
2742 self.assertRaises(NotImplementedError,
2743 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2744
2745 class UOFS(DST):
2746 def __init__(self, uofs, dofs=None):
2747 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002748 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002749 def utcoffset(self, dt):
2750 return self.uofs
2751
2752 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2753 # in effect for a UTC time.
2754 for dstvalue in -33, 33, 0, None:
2755 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2756 t = d.utctimetuple()
2757 self.assertEqual(d.year, t.tm_year)
2758 self.assertEqual(d.month, t.tm_mon)
2759 self.assertEqual(d.day, t.tm_mday)
2760 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2761 self.assertEqual(13, t.tm_min)
2762 self.assertEqual(d.second, t.tm_sec)
2763 self.assertEqual(d.weekday(), t.tm_wday)
2764 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2765 t.tm_yday)
2766 self.assertEqual(0, t.tm_isdst)
2767
2768 # At the edges, UTC adjustment can normalize into years out-of-range
2769 # for a datetime object. Ensure that a correct timetuple is
2770 # created anyway.
2771 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2772 # That goes back 1 minute less than a full day.
2773 t = tiny.utctimetuple()
2774 self.assertEqual(t.tm_year, MINYEAR-1)
2775 self.assertEqual(t.tm_mon, 12)
2776 self.assertEqual(t.tm_mday, 31)
2777 self.assertEqual(t.tm_hour, 0)
2778 self.assertEqual(t.tm_min, 1)
2779 self.assertEqual(t.tm_sec, 37)
2780 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2781 self.assertEqual(t.tm_isdst, 0)
2782
2783 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2784 # That goes forward 1 minute less than a full day.
2785 t = huge.utctimetuple()
2786 self.assertEqual(t.tm_year, MAXYEAR+1)
2787 self.assertEqual(t.tm_mon, 1)
2788 self.assertEqual(t.tm_mday, 1)
2789 self.assertEqual(t.tm_hour, 23)
2790 self.assertEqual(t.tm_min, 58)
2791 self.assertEqual(t.tm_sec, 37)
2792 self.assertEqual(t.tm_yday, 1)
2793 self.assertEqual(t.tm_isdst, 0)
2794
2795 def test_tzinfo_isoformat(self):
2796 zero = FixedOffset(0, "+00:00")
2797 plus = FixedOffset(220, "+03:40")
2798 minus = FixedOffset(-231, "-03:51")
2799 unknown = FixedOffset(None, "")
2800
2801 cls = self.theclass
2802 datestr = '0001-02-03'
2803 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002804 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002805 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2806 timestr = '04:05:59' + (us and '.987001' or '')
2807 ofsstr = ofs is not None and d.tzname() or ''
2808 tailstr = timestr + ofsstr
2809 iso = d.isoformat()
2810 self.assertEqual(iso, datestr + 'T' + tailstr)
2811 self.assertEqual(iso, d.isoformat('T'))
2812 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002813 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002814 self.assertEqual(str(d), datestr + ' ' + tailstr)
2815
Tim Peters12bf3392002-12-24 05:41:27 +00002816 def test_replace(self):
2817 cls = self.theclass
2818 z100 = FixedOffset(100, "+100")
2819 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2820 args = [1, 2, 3, 4, 5, 6, 7, z100]
2821 base = cls(*args)
2822 self.assertEqual(base, base.replace())
2823
2824 i = 0
2825 for name, newval in (("year", 2),
2826 ("month", 3),
2827 ("day", 4),
2828 ("hour", 5),
2829 ("minute", 6),
2830 ("second", 7),
2831 ("microsecond", 8),
2832 ("tzinfo", zm200)):
2833 newargs = args[:]
2834 newargs[i] = newval
2835 expected = cls(*newargs)
2836 got = base.replace(**{name: newval})
2837 self.assertEqual(expected, got)
2838 i += 1
2839
2840 # Ensure we can get rid of a tzinfo.
2841 self.assertEqual(base.tzname(), "+100")
2842 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002843 self.assertTrue(base2.tzinfo is None)
2844 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002845
2846 # Ensure we can add one.
2847 base3 = base2.replace(tzinfo=z100)
2848 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002849 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002850
2851 # Out of bounds.
2852 base = cls(2000, 2, 29)
2853 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002854
Tim Peters80475bb2002-12-25 07:40:55 +00002855 def test_more_astimezone(self):
2856 # The inherited test_astimezone covered some trivial and error cases.
2857 fnone = FixedOffset(None, "None")
2858 f44m = FixedOffset(44, "44")
2859 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2860
Tim Peters10cadce2003-01-23 19:58:02 +00002861 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002862 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002863 # Replacing with degenerate tzinfo raises an exception.
2864 self.assertRaises(ValueError, dt.astimezone, fnone)
2865 # Ditto with None tz.
2866 self.assertRaises(TypeError, dt.astimezone, None)
2867 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002868 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002869 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002870 self.assertEqual(x.date(), dt.date())
2871 self.assertEqual(x.time(), dt.time())
2872
2873 # Replacing with different tzinfo does adjust.
2874 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002875 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002876 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2877 expected = dt - dt.utcoffset() # in effect, convert to UTC
2878 expected += fm5h.utcoffset(dt) # and from there to local time
2879 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2880 self.assertEqual(got.date(), expected.date())
2881 self.assertEqual(got.time(), expected.time())
2882 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002883 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002884 self.assertEqual(got, expected)
2885
Tim Peters4c0db782002-12-26 05:01:19 +00002886 def test_aware_subtract(self):
2887 cls = self.theclass
2888
Tim Peters60c76e42002-12-27 00:41:11 +00002889 # Ensure that utcoffset() is ignored when the operands have the
2890 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002891 class OperandDependentOffset(tzinfo):
2892 def utcoffset(self, t):
2893 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002894 # d0 and d1 equal after adjustment
2895 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002896 else:
Tim Peters397301e2003-01-02 21:28:08 +00002897 # d2 off in the weeds
2898 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002899
2900 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2901 d0 = base.replace(minute=3)
2902 d1 = base.replace(minute=9)
2903 d2 = base.replace(minute=11)
2904 for x in d0, d1, d2:
2905 for y in d0, d1, d2:
2906 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002907 expected = timedelta(minutes=x.minute - y.minute)
2908 self.assertEqual(got, expected)
2909
2910 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2911 # ignored.
2912 base = cls(8, 9, 10, 11, 12, 13, 14)
2913 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2914 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2915 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2916 for x in d0, d1, d2:
2917 for y in d0, d1, d2:
2918 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002919 if (x is d0 or x is d1) and (y is d0 or y is d1):
2920 expected = timedelta(0)
2921 elif x is y is d2:
2922 expected = timedelta(0)
2923 elif x is d2:
2924 expected = timedelta(minutes=(11-59)-0)
2925 else:
2926 assert y is d2
2927 expected = timedelta(minutes=0-(11-59))
2928 self.assertEqual(got, expected)
2929
Tim Peters60c76e42002-12-27 00:41:11 +00002930 def test_mixed_compare(self):
2931 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002932 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002933 self.assertEqual(t1, t2)
2934 t2 = t2.replace(tzinfo=None)
2935 self.assertEqual(t1, t2)
2936 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2937 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002938 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2939 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002940
Tim Peters0bf60bd2003-01-08 20:40:01 +00002941 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002942 class Varies(tzinfo):
2943 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002944 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002945 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002946 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002947 return self.offset
2948
2949 v = Varies()
2950 t1 = t2.replace(tzinfo=v)
2951 t2 = t2.replace(tzinfo=v)
2952 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2953 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2954 self.assertEqual(t1, t2)
2955
2956 # But if they're not identical, it isn't ignored.
2957 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002958 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002959
Tim Petersa98924a2003-05-17 05:55:19 +00002960 def test_subclass_datetimetz(self):
2961
2962 class C(self.theclass):
2963 theAnswer = 42
2964
2965 def __new__(cls, *args, **kws):
2966 temp = kws.copy()
2967 extra = temp.pop('extra')
2968 result = self.theclass.__new__(cls, *args, **temp)
2969 result.extra = extra
2970 return result
2971
2972 def newmeth(self, start):
2973 return start + self.hour + self.year
2974
2975 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2976
2977 dt1 = self.theclass(*args)
2978 dt2 = C(*args, **{'extra': 7})
2979
2980 self.assertEqual(dt2.__class__, C)
2981 self.assertEqual(dt2.theAnswer, 42)
2982 self.assertEqual(dt2.extra, 7)
2983 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2984 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2985
Tim Peters621818b2002-12-29 23:44:49 +00002986# Pain to set up DST-aware tzinfo classes.
2987
2988def first_sunday_on_or_after(dt):
2989 days_to_go = 6 - dt.weekday()
2990 if days_to_go:
2991 dt += timedelta(days_to_go)
2992 return dt
2993
2994ZERO = timedelta(0)
2995HOUR = timedelta(hours=1)
2996DAY = timedelta(days=1)
2997# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2998DSTSTART = datetime(1, 4, 1, 2)
2999# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003000# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3001# being standard time on that day, there is no spelling in local time of
3002# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3003DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003004
3005class USTimeZone(tzinfo):
3006
3007 def __init__(self, hours, reprname, stdname, dstname):
3008 self.stdoffset = timedelta(hours=hours)
3009 self.reprname = reprname
3010 self.stdname = stdname
3011 self.dstname = dstname
3012
3013 def __repr__(self):
3014 return self.reprname
3015
3016 def tzname(self, dt):
3017 if self.dst(dt):
3018 return self.dstname
3019 else:
3020 return self.stdname
3021
3022 def utcoffset(self, dt):
3023 return self.stdoffset + self.dst(dt)
3024
3025 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003026 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003027 # An exception instead may be sensible here, in one or more of
3028 # the cases.
3029 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003030 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003031
3032 # Find first Sunday in April.
3033 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3034 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3035
3036 # Find last Sunday in October.
3037 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3038 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3039
Tim Peters621818b2002-12-29 23:44:49 +00003040 # Can't compare naive to aware objects, so strip the timezone from
3041 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003042 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003043 return HOUR
3044 else:
3045 return ZERO
3046
Tim Peters521fc152002-12-31 17:36:56 +00003047Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3048Central = USTimeZone(-6, "Central", "CST", "CDT")
3049Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3050Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003051utc_real = FixedOffset(0, "UTC", 0)
3052# For better test coverage, we want another flavor of UTC that's west of
3053# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003054utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003055
3056class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003057 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003058 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003059 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003060
Tim Peters0bf60bd2003-01-08 20:40:01 +00003061 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003062
Tim Peters521fc152002-12-31 17:36:56 +00003063 # Check a time that's inside DST.
3064 def checkinside(self, dt, tz, utc, dston, dstoff):
3065 self.assertEqual(dt.dst(), HOUR)
3066
3067 # Conversion to our own timezone is always an identity.
3068 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003069
3070 asutc = dt.astimezone(utc)
3071 there_and_back = asutc.astimezone(tz)
3072
3073 # Conversion to UTC and back isn't always an identity here,
3074 # because there are redundant spellings (in local time) of
3075 # UTC time when DST begins: the clock jumps from 1:59:59
3076 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3077 # make sense then. The classes above treat 2:MM:SS as
3078 # daylight time then (it's "after 2am"), really an alias
3079 # for 1:MM:SS standard time. The latter form is what
3080 # conversion back from UTC produces.
3081 if dt.date() == dston.date() and dt.hour == 2:
3082 # We're in the redundant hour, and coming back from
3083 # UTC gives the 1:MM:SS standard-time spelling.
3084 self.assertEqual(there_and_back + HOUR, dt)
3085 # Although during was considered to be in daylight
3086 # time, there_and_back is not.
3087 self.assertEqual(there_and_back.dst(), ZERO)
3088 # They're the same times in UTC.
3089 self.assertEqual(there_and_back.astimezone(utc),
3090 dt.astimezone(utc))
3091 else:
3092 # We're not in the redundant hour.
3093 self.assertEqual(dt, there_and_back)
3094
Tim Peters327098a2003-01-20 22:54:38 +00003095 # Because we have a redundant spelling when DST begins, there is
3096 # (unforunately) an hour when DST ends that can't be spelled at all in
3097 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3098 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3099 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3100 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3101 # expressed in local time. Nevertheless, we want conversion back
3102 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003103 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003104 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003105 if dt.date() == dstoff.date() and dt.hour == 0:
3106 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003107 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003108 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3109 nexthour_utc += HOUR
3110 nexthour_tz = nexthour_utc.astimezone(tz)
3111 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003112 else:
Tim Peters327098a2003-01-20 22:54:38 +00003113 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003114
3115 # Check a time that's outside DST.
3116 def checkoutside(self, dt, tz, utc):
3117 self.assertEqual(dt.dst(), ZERO)
3118
3119 # Conversion to our own timezone is always an identity.
3120 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003121
3122 # Converting to UTC and back is an identity too.
3123 asutc = dt.astimezone(utc)
3124 there_and_back = asutc.astimezone(tz)
3125 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003126
Tim Peters1024bf82002-12-30 17:09:40 +00003127 def convert_between_tz_and_utc(self, tz, utc):
3128 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003129 # Because 1:MM on the day DST ends is taken as being standard time,
3130 # there is no spelling in tz for the last hour of daylight time.
3131 # For purposes of the test, the last hour of DST is 0:MM, which is
3132 # taken as being daylight time (and 1:MM is taken as being standard
3133 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003134 dstoff = self.dstoff.replace(tzinfo=tz)
3135 for delta in (timedelta(weeks=13),
3136 DAY,
3137 HOUR,
3138 timedelta(minutes=1),
3139 timedelta(microseconds=1)):
3140
Tim Peters521fc152002-12-31 17:36:56 +00003141 self.checkinside(dston, tz, utc, dston, dstoff)
3142 for during in dston + delta, dstoff - delta:
3143 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003144
Tim Peters521fc152002-12-31 17:36:56 +00003145 self.checkoutside(dstoff, tz, utc)
3146 for outside in dston - delta, dstoff + delta:
3147 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003148
Tim Peters621818b2002-12-29 23:44:49 +00003149 def test_easy(self):
3150 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003151 self.convert_between_tz_and_utc(Eastern, utc_real)
3152 self.convert_between_tz_and_utc(Pacific, utc_real)
3153 self.convert_between_tz_and_utc(Eastern, utc_fake)
3154 self.convert_between_tz_and_utc(Pacific, utc_fake)
3155 # The next is really dancing near the edge. It works because
3156 # Pacific and Eastern are far enough apart that their "problem
3157 # hours" don't overlap.
3158 self.convert_between_tz_and_utc(Eastern, Pacific)
3159 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003160 # OTOH, these fail! Don't enable them. The difficulty is that
3161 # the edge case tests assume that every hour is representable in
3162 # the "utc" class. This is always true for a fixed-offset tzinfo
3163 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3164 # For these adjacent DST-aware time zones, the range of time offsets
3165 # tested ends up creating hours in the one that aren't representable
3166 # in the other. For the same reason, we would see failures in the
3167 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3168 # offset deltas in convert_between_tz_and_utc().
3169 #
3170 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3171 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003172
Tim Petersf3615152003-01-01 21:51:37 +00003173 def test_tricky(self):
3174 # 22:00 on day before daylight starts.
3175 fourback = self.dston - timedelta(hours=4)
3176 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003177 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003178 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3179 # 2", we should get the 3 spelling.
3180 # If we plug 22:00 the day before into Eastern, it "looks like std
3181 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3182 # to 22:00 lands on 2:00, which makes no sense in local time (the
3183 # local clock jumps from 1 to 3). The point here is to make sure we
3184 # get the 3 spelling.
3185 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003186 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003187 self.assertEqual(expected, got)
3188
3189 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3190 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003191 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003192 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3193 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3194 # spelling.
3195 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003196 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003197 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003198
Tim Petersadf64202003-01-04 06:03:15 +00003199 # Now on the day DST ends, we want "repeat an hour" behavior.
3200 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3201 # EST 23:MM 0:MM 1:MM 2:MM
3202 # EDT 0:MM 1:MM 2:MM 3:MM
3203 # wall 0:MM 1:MM 1:MM 2:MM against these
3204 for utc in utc_real, utc_fake:
3205 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003206 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003207 # Convert that to UTC.
3208 first_std_hour -= tz.utcoffset(None)
3209 # Adjust for possibly fake UTC.
3210 asutc = first_std_hour + utc.utcoffset(None)
3211 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3212 # tz=Eastern.
3213 asutcbase = asutc.replace(tzinfo=utc)
3214 for tzhour in (0, 1, 1, 2):
3215 expectedbase = self.dstoff.replace(hour=tzhour)
3216 for minute in 0, 30, 59:
3217 expected = expectedbase.replace(minute=minute)
3218 asutc = asutcbase.replace(minute=minute)
3219 astz = asutc.astimezone(tz)
3220 self.assertEqual(astz.replace(tzinfo=None), expected)
3221 asutcbase += HOUR
3222
3223
Tim Peters710fb152003-01-02 19:35:54 +00003224 def test_bogus_dst(self):
3225 class ok(tzinfo):
3226 def utcoffset(self, dt): return HOUR
3227 def dst(self, dt): return HOUR
3228
3229 now = self.theclass.now().replace(tzinfo=utc_real)
3230 # Doesn't blow up.
3231 now.astimezone(ok())
3232
3233 # Does blow up.
3234 class notok(ok):
3235 def dst(self, dt): return None
3236 self.assertRaises(ValueError, now.astimezone, notok())
3237
Tim Peters52dcce22003-01-23 16:36:11 +00003238 def test_fromutc(self):
3239 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3240 now = datetime.utcnow().replace(tzinfo=utc_real)
3241 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3242 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3243 enow = Eastern.fromutc(now) # doesn't blow up
3244 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3245 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3246 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3247
3248 # Always converts UTC to standard time.
3249 class FauxUSTimeZone(USTimeZone):
3250 def fromutc(self, dt):
3251 return dt + self.stdoffset
3252 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3253
3254 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3255 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3256 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3257
3258 # Check around DST start.
3259 start = self.dston.replace(hour=4, tzinfo=Eastern)
3260 fstart = start.replace(tzinfo=FEastern)
3261 for wall in 23, 0, 1, 3, 4, 5:
3262 expected = start.replace(hour=wall)
3263 if wall == 23:
3264 expected -= timedelta(days=1)
3265 got = Eastern.fromutc(start)
3266 self.assertEqual(expected, got)
3267
3268 expected = fstart + FEastern.stdoffset
3269 got = FEastern.fromutc(fstart)
3270 self.assertEqual(expected, got)
3271
3272 # Ensure astimezone() calls fromutc() too.
3273 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3274 self.assertEqual(expected, got)
3275
3276 start += HOUR
3277 fstart += HOUR
3278
3279 # Check around DST end.
3280 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3281 fstart = start.replace(tzinfo=FEastern)
3282 for wall in 0, 1, 1, 2, 3, 4:
3283 expected = start.replace(hour=wall)
3284 got = Eastern.fromutc(start)
3285 self.assertEqual(expected, got)
3286
3287 expected = fstart + FEastern.stdoffset
3288 got = FEastern.fromutc(fstart)
3289 self.assertEqual(expected, got)
3290
3291 # Ensure astimezone() calls fromutc() too.
3292 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3293 self.assertEqual(expected, got)
3294
3295 start += HOUR
3296 fstart += HOUR
3297
Tim Peters710fb152003-01-02 19:35:54 +00003298
Tim Peters528ca532004-09-16 01:30:50 +00003299#############################################################################
3300# oddballs
3301
3302class Oddballs(unittest.TestCase):
3303
3304 def test_bug_1028306(self):
3305 # Trying to compare a date to a datetime should act like a mixed-
3306 # type comparison, despite that datetime is a subclass of date.
3307 as_date = date.today()
3308 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003309 self.assertTrue(as_date != as_datetime)
3310 self.assertTrue(as_datetime != as_date)
3311 self.assertTrue(not as_date == as_datetime)
3312 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003313 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3314 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3315 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3316 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3317 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3318 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3319 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3320 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3321
3322 # Neverthelss, comparison should work with the base-class (date)
3323 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003324 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003325 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003326 as_different = as_datetime.replace(day= different_day)
3327 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003328
3329 # And date should compare with other subclasses of date. If a
3330 # subclass wants to stop this, it's up to the subclass to do so.
3331 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3332 self.assertEqual(as_date, date_sc)
3333 self.assertEqual(date_sc, as_date)
3334
3335 # Ditto for datetimes.
3336 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3337 as_date.day, 0, 0, 0)
3338 self.assertEqual(as_datetime, datetime_sc)
3339 self.assertEqual(datetime_sc, as_datetime)
3340
Tim Peters2a799bf2002-12-16 20:18:38 +00003341def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003342 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003343
3344if __name__ == "__main__":
3345 test_main()