blob: d85217ddc9b0a912a3137376223c72aeb010bb04 [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
Antoine Pitroube6859d2009-11-25 23:02:32 +0000264 def test_total_seconds(self):
265 td = timedelta(days=365)
266 self.assertEqual(td.total_seconds(), 31536000.0)
267 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
268 td = timedelta(seconds=total_seconds)
269 self.assertEqual(td.total_seconds(), total_seconds)
270
Tim Peters2a799bf2002-12-16 20:18:38 +0000271 def test_carries(self):
272 t1 = timedelta(days=100,
273 weeks=-7,
274 hours=-24*(100-49),
275 minutes=-3,
276 seconds=12,
277 microseconds=(3*60 - 12) * 1e6 + 1)
278 t2 = timedelta(microseconds=1)
279 self.assertEqual(t1, t2)
280
281 def test_hash_equality(self):
282 t1 = timedelta(days=100,
283 weeks=-7,
284 hours=-24*(100-49),
285 minutes=-3,
286 seconds=12,
287 microseconds=(3*60 - 12) * 1000000)
288 t2 = timedelta()
289 self.assertEqual(hash(t1), hash(t2))
290
291 t1 += timedelta(weeks=7)
292 t2 += timedelta(days=7*7)
293 self.assertEqual(t1, t2)
294 self.assertEqual(hash(t1), hash(t2))
295
296 d = {t1: 1}
297 d[t2] = 2
298 self.assertEqual(len(d), 1)
299 self.assertEqual(d[t1], 2)
300
301 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000302 args = 12, 34, 56
303 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000304 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000305 green = pickler.dumps(orig, proto)
306 derived = unpickler.loads(green)
307 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000308
309 def test_compare(self):
310 t1 = timedelta(2, 3, 4)
311 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000312 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000313 self.assertTrue(t1 <= t2)
314 self.assertTrue(t1 >= t2)
315 self.assertTrue(not t1 != t2)
316 self.assertTrue(not t1 < t2)
317 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000318
319 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
320 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000321 self.assertTrue(t1 < t2)
322 self.assertTrue(t2 > t1)
323 self.assertTrue(t1 <= t2)
324 self.assertTrue(t2 >= t1)
325 self.assertTrue(t1 != t2)
326 self.assertTrue(t2 != t1)
327 self.assertTrue(not t1 == t2)
328 self.assertTrue(not t2 == t1)
329 self.assertTrue(not t1 > t2)
330 self.assertTrue(not t2 < t1)
331 self.assertTrue(not t1 >= t2)
332 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000333
Tim Peters68124bb2003-02-08 03:46:31 +0000334 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000335 self.assertEqual(t1 == badarg, False)
336 self.assertEqual(t1 != badarg, True)
337 self.assertEqual(badarg == t1, False)
338 self.assertEqual(badarg != t1, True)
339
Tim Peters2a799bf2002-12-16 20:18:38 +0000340 self.assertRaises(TypeError, lambda: t1 <= badarg)
341 self.assertRaises(TypeError, lambda: t1 < badarg)
342 self.assertRaises(TypeError, lambda: t1 > badarg)
343 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000344 self.assertRaises(TypeError, lambda: badarg <= t1)
345 self.assertRaises(TypeError, lambda: badarg < t1)
346 self.assertRaises(TypeError, lambda: badarg > t1)
347 self.assertRaises(TypeError, lambda: badarg >= t1)
348
349 def test_str(self):
350 td = timedelta
351 eq = self.assertEqual
352
353 eq(str(td(1)), "1 day, 0:00:00")
354 eq(str(td(-1)), "-1 day, 0:00:00")
355 eq(str(td(2)), "2 days, 0:00:00")
356 eq(str(td(-2)), "-2 days, 0:00:00")
357
358 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
359 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
360 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
361 "-210 days, 23:12:34")
362
363 eq(str(td(milliseconds=1)), "0:00:00.001000")
364 eq(str(td(microseconds=3)), "0:00:00.000003")
365
366 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
367 microseconds=999999)),
368 "999999999 days, 23:59:59.999999")
369
370 def test_roundtrip(self):
371 for td in (timedelta(days=999999999, hours=23, minutes=59,
372 seconds=59, microseconds=999999),
373 timedelta(days=-999999999),
374 timedelta(days=1, seconds=2, microseconds=3)):
375
376 # Verify td -> string -> td identity.
377 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000378 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000379 s = s[9:]
380 td2 = eval(s)
381 self.assertEqual(td, td2)
382
383 # Verify identity via reconstructing from pieces.
384 td2 = timedelta(td.days, td.seconds, td.microseconds)
385 self.assertEqual(td, td2)
386
387 def test_resolution_info(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000388 self.assertTrue(isinstance(timedelta.min, timedelta))
389 self.assertTrue(isinstance(timedelta.max, timedelta))
390 self.assertTrue(isinstance(timedelta.resolution, timedelta))
391 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000392 self.assertEqual(timedelta.min, timedelta(-999999999))
393 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
394 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
395
396 def test_overflow(self):
397 tiny = timedelta.resolution
398
399 td = timedelta.min + tiny
400 td -= tiny # no problem
401 self.assertRaises(OverflowError, td.__sub__, tiny)
402 self.assertRaises(OverflowError, td.__add__, -tiny)
403
404 td = timedelta.max - tiny
405 td += tiny # no problem
406 self.assertRaises(OverflowError, td.__add__, tiny)
407 self.assertRaises(OverflowError, td.__sub__, -tiny)
408
409 self.assertRaises(OverflowError, lambda: -timedelta.max)
410
411 def test_microsecond_rounding(self):
412 td = timedelta
413 eq = self.assertEqual
414
415 # Single-field rounding.
416 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
417 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
418 eq(td(milliseconds=0.6/1000), td(microseconds=1))
419 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
420
421 # Rounding due to contributions from more than one field.
422 us_per_hour = 3600e6
423 us_per_day = us_per_hour * 24
424 eq(td(days=.4/us_per_day), td(0))
425 eq(td(hours=.2/us_per_hour), td(0))
426 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
427
428 eq(td(days=-.4/us_per_day), td(0))
429 eq(td(hours=-.2/us_per_hour), td(0))
430 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
431
432 def test_massive_normalization(self):
433 td = timedelta(microseconds=-1)
434 self.assertEqual((td.days, td.seconds, td.microseconds),
435 (-1, 24*3600-1, 999999))
436
437 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000438 self.assertTrue(timedelta(1))
439 self.assertTrue(timedelta(0, 1))
440 self.assertTrue(timedelta(0, 0, 1))
441 self.assertTrue(timedelta(microseconds=1))
442 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000443
Tim Petersb0c854d2003-05-17 15:57:00 +0000444 def test_subclass_timedelta(self):
445
446 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000447 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000448 def from_td(td):
449 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000450
451 def as_hours(self):
452 sum = (self.days * 24 +
453 self.seconds / 3600.0 +
454 self.microseconds / 3600e6)
455 return round(sum)
456
457 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000458 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000459 self.assertEqual(t1.as_hours(), 24)
460
461 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000462 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000463 self.assertEqual(t2.as_hours(), -25)
464
465 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000466 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000467 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000468 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000469 self.assertEqual(t3.days, t4.days)
470 self.assertEqual(t3.seconds, t4.seconds)
471 self.assertEqual(t3.microseconds, t4.microseconds)
472 self.assertEqual(str(t3), str(t4))
473 self.assertEqual(t4.as_hours(), -1)
474
Tim Peters2a799bf2002-12-16 20:18:38 +0000475#############################################################################
476# date tests
477
478class TestDateOnly(unittest.TestCase):
479 # Tests here won't pass if also run on datetime objects, so don't
480 # subclass this to test datetimes too.
481
482 def test_delta_non_days_ignored(self):
483 dt = date(2000, 1, 2)
484 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
485 microseconds=5)
486 days = timedelta(delta.days)
487 self.assertEqual(days, timedelta(1))
488
489 dt2 = dt + delta
490 self.assertEqual(dt2, dt + days)
491
492 dt2 = delta + dt
493 self.assertEqual(dt2, dt + days)
494
495 dt2 = dt - delta
496 self.assertEqual(dt2, dt - days)
497
498 delta = -delta
499 days = timedelta(delta.days)
500 self.assertEqual(days, timedelta(-2))
501
502 dt2 = dt + delta
503 self.assertEqual(dt2, dt + days)
504
505 dt2 = delta + dt
506 self.assertEqual(dt2, dt + days)
507
508 dt2 = dt - delta
509 self.assertEqual(dt2, dt - days)
510
Tim Peters604c0132004-06-07 23:04:33 +0000511class SubclassDate(date):
512 sub_var = 1
513
Guido van Rossumd8faa362007-04-27 19:54:29 +0000514class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000515 # Tests here should pass for both dates and datetimes, except for a
516 # few tests that TestDateTime overrides.
517
518 theclass = date
519
520 def test_basic_attributes(self):
521 dt = self.theclass(2002, 3, 1)
522 self.assertEqual(dt.year, 2002)
523 self.assertEqual(dt.month, 3)
524 self.assertEqual(dt.day, 1)
525
526 def test_roundtrip(self):
527 for dt in (self.theclass(1, 2, 3),
528 self.theclass.today()):
529 # Verify dt -> string -> date identity.
530 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000531 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000532 s = s[9:]
533 dt2 = eval(s)
534 self.assertEqual(dt, dt2)
535
536 # Verify identity via reconstructing from pieces.
537 dt2 = self.theclass(dt.year, dt.month, dt.day)
538 self.assertEqual(dt, dt2)
539
540 def test_ordinal_conversions(self):
541 # Check some fixed values.
542 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
543 (1, 12, 31, 365),
544 (2, 1, 1, 366),
545 # first example from "Calendrical Calculations"
546 (1945, 11, 12, 710347)]:
547 d = self.theclass(y, m, d)
548 self.assertEqual(n, d.toordinal())
549 fromord = self.theclass.fromordinal(n)
550 self.assertEqual(d, fromord)
551 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000552 # if we're checking something fancier than a date, verify
553 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000554 self.assertEqual(fromord.hour, 0)
555 self.assertEqual(fromord.minute, 0)
556 self.assertEqual(fromord.second, 0)
557 self.assertEqual(fromord.microsecond, 0)
558
Tim Peters0bf60bd2003-01-08 20:40:01 +0000559 # Check first and last days of year spottily across the whole
560 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000561 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000562 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
563 d = self.theclass(year, 1, 1)
564 n = d.toordinal()
565 d2 = self.theclass.fromordinal(n)
566 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000567 # Verify that moving back a day gets to the end of year-1.
568 if year > 1:
569 d = self.theclass.fromordinal(n-1)
570 d2 = self.theclass(year-1, 12, 31)
571 self.assertEqual(d, d2)
572 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000573
574 # Test every day in a leap-year and a non-leap year.
575 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
576 for year, isleap in (2000, True), (2002, False):
577 n = self.theclass(year, 1, 1).toordinal()
578 for month, maxday in zip(range(1, 13), dim):
579 if month == 2 and isleap:
580 maxday += 1
581 for day in range(1, maxday+1):
582 d = self.theclass(year, month, day)
583 self.assertEqual(d.toordinal(), n)
584 self.assertEqual(d, self.theclass.fromordinal(n))
585 n += 1
586
587 def test_extreme_ordinals(self):
588 a = self.theclass.min
589 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
590 aord = a.toordinal()
591 b = a.fromordinal(aord)
592 self.assertEqual(a, b)
593
594 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
595
596 b = a + timedelta(days=1)
597 self.assertEqual(b.toordinal(), aord + 1)
598 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
599
600 a = self.theclass.max
601 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
602 aord = a.toordinal()
603 b = a.fromordinal(aord)
604 self.assertEqual(a, b)
605
606 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
607
608 b = a - timedelta(days=1)
609 self.assertEqual(b.toordinal(), aord - 1)
610 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
611
612 def test_bad_constructor_arguments(self):
613 # bad years
614 self.theclass(MINYEAR, 1, 1) # no exception
615 self.theclass(MAXYEAR, 1, 1) # no exception
616 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
617 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
618 # bad months
619 self.theclass(2000, 1, 1) # no exception
620 self.theclass(2000, 12, 1) # no exception
621 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
622 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
623 # bad days
624 self.theclass(2000, 2, 29) # no exception
625 self.theclass(2004, 2, 29) # no exception
626 self.theclass(2400, 2, 29) # no exception
627 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
628 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
629 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
630 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
631 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
632 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
633
634 def test_hash_equality(self):
635 d = self.theclass(2000, 12, 31)
636 # same thing
637 e = self.theclass(2000, 12, 31)
638 self.assertEqual(d, e)
639 self.assertEqual(hash(d), hash(e))
640
641 dic = {d: 1}
642 dic[e] = 2
643 self.assertEqual(len(dic), 1)
644 self.assertEqual(dic[d], 2)
645 self.assertEqual(dic[e], 2)
646
647 d = self.theclass(2001, 1, 1)
648 # same thing
649 e = self.theclass(2001, 1, 1)
650 self.assertEqual(d, e)
651 self.assertEqual(hash(d), hash(e))
652
653 dic = {d: 1}
654 dic[e] = 2
655 self.assertEqual(len(dic), 1)
656 self.assertEqual(dic[d], 2)
657 self.assertEqual(dic[e], 2)
658
659 def test_computations(self):
660 a = self.theclass(2002, 1, 31)
661 b = self.theclass(1956, 1, 31)
662
663 diff = a-b
664 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
665 self.assertEqual(diff.seconds, 0)
666 self.assertEqual(diff.microseconds, 0)
667
668 day = timedelta(1)
669 week = timedelta(7)
670 a = self.theclass(2002, 3, 2)
671 self.assertEqual(a + day, self.theclass(2002, 3, 3))
672 self.assertEqual(day + a, self.theclass(2002, 3, 3))
673 self.assertEqual(a - day, self.theclass(2002, 3, 1))
674 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
675 self.assertEqual(a + week, self.theclass(2002, 3, 9))
676 self.assertEqual(a - week, self.theclass(2002, 2, 23))
677 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
678 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
679 self.assertEqual((a + week) - a, week)
680 self.assertEqual((a + day) - a, day)
681 self.assertEqual((a - week) - a, -week)
682 self.assertEqual((a - day) - a, -day)
683 self.assertEqual(a - (a + week), -week)
684 self.assertEqual(a - (a + day), -day)
685 self.assertEqual(a - (a - week), week)
686 self.assertEqual(a - (a - day), day)
687
688 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000689 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000690 self.assertRaises(TypeError, lambda: a+i)
691 self.assertRaises(TypeError, lambda: a-i)
692 self.assertRaises(TypeError, lambda: i+a)
693 self.assertRaises(TypeError, lambda: i-a)
694
695 # delta - date is senseless.
696 self.assertRaises(TypeError, lambda: day - a)
697 # mixing date and (delta or date) via * or // is senseless
698 self.assertRaises(TypeError, lambda: day * a)
699 self.assertRaises(TypeError, lambda: a * day)
700 self.assertRaises(TypeError, lambda: day // a)
701 self.assertRaises(TypeError, lambda: a // day)
702 self.assertRaises(TypeError, lambda: a * a)
703 self.assertRaises(TypeError, lambda: a // a)
704 # date + date is senseless
705 self.assertRaises(TypeError, lambda: a + a)
706
707 def test_overflow(self):
708 tiny = self.theclass.resolution
709
710 dt = self.theclass.min + tiny
711 dt -= tiny # no problem
712 self.assertRaises(OverflowError, dt.__sub__, tiny)
713 self.assertRaises(OverflowError, dt.__add__, -tiny)
714
715 dt = self.theclass.max - tiny
716 dt += tiny # no problem
717 self.assertRaises(OverflowError, dt.__add__, tiny)
718 self.assertRaises(OverflowError, dt.__sub__, -tiny)
719
720 def test_fromtimestamp(self):
721 import time
722
723 # Try an arbitrary fixed value.
724 year, month, day = 1999, 9, 19
725 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
726 d = self.theclass.fromtimestamp(ts)
727 self.assertEqual(d.year, year)
728 self.assertEqual(d.month, month)
729 self.assertEqual(d.day, day)
730
Tim Peters1b6f7a92004-06-20 02:50:16 +0000731 def test_insane_fromtimestamp(self):
732 # It's possible that some platform maps time_t to double,
733 # and that this test will fail there. This test should
734 # exempt such platforms (provided they return reasonable
735 # results!).
736 for insane in -1e200, 1e200:
737 self.assertRaises(ValueError, self.theclass.fromtimestamp,
738 insane)
739
Tim Peters2a799bf2002-12-16 20:18:38 +0000740 def test_today(self):
741 import time
742
743 # We claim that today() is like fromtimestamp(time.time()), so
744 # prove it.
745 for dummy in range(3):
746 today = self.theclass.today()
747 ts = time.time()
748 todayagain = self.theclass.fromtimestamp(ts)
749 if today == todayagain:
750 break
751 # There are several legit reasons that could fail:
752 # 1. It recently became midnight, between the today() and the
753 # time() calls.
754 # 2. The platform time() has such fine resolution that we'll
755 # never get the same value twice.
756 # 3. The platform time() has poor resolution, and we just
757 # happened to call today() right before a resolution quantum
758 # boundary.
759 # 4. The system clock got fiddled between calls.
760 # In any case, wait a little while and try again.
761 time.sleep(0.1)
762
763 # It worked or it didn't. If it didn't, assume it's reason #2, and
764 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000765 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000766 abs(todayagain - today) < timedelta(seconds=0.5))
767
768 def test_weekday(self):
769 for i in range(7):
770 # March 4, 2002 is a Monday
771 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
772 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
773 # January 2, 1956 is a Monday
774 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
775 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
776
777 def test_isocalendar(self):
778 # Check examples from
779 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
780 for i in range(7):
781 d = self.theclass(2003, 12, 22+i)
782 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
783 d = self.theclass(2003, 12, 29) + timedelta(i)
784 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
785 d = self.theclass(2004, 1, 5+i)
786 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
787 d = self.theclass(2009, 12, 21+i)
788 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
789 d = self.theclass(2009, 12, 28) + timedelta(i)
790 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
791 d = self.theclass(2010, 1, 4+i)
792 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
793
794 def test_iso_long_years(self):
795 # Calculate long ISO years and compare to table from
796 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
797 ISO_LONG_YEARS_TABLE = """
798 4 32 60 88
799 9 37 65 93
800 15 43 71 99
801 20 48 76
802 26 54 82
803
804 105 133 161 189
805 111 139 167 195
806 116 144 172
807 122 150 178
808 128 156 184
809
810 201 229 257 285
811 207 235 263 291
812 212 240 268 296
813 218 246 274
814 224 252 280
815
816 303 331 359 387
817 308 336 364 392
818 314 342 370 398
819 320 348 376
820 325 353 381
821 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000822 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000823 L = []
824 for i in range(400):
825 d = self.theclass(2000+i, 12, 31)
826 d1 = self.theclass(1600+i, 12, 31)
827 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
828 if d.isocalendar()[1] == 53:
829 L.append(i)
830 self.assertEqual(L, iso_long_years)
831
832 def test_isoformat(self):
833 t = self.theclass(2, 3, 2)
834 self.assertEqual(t.isoformat(), "0002-03-02")
835
836 def test_ctime(self):
837 t = self.theclass(2002, 3, 2)
838 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
839
840 def test_strftime(self):
841 t = self.theclass(2005, 3, 2)
842 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000843 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000844 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000845
846 self.assertRaises(TypeError, t.strftime) # needs an arg
847 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
848 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
849
Georg Brandlf78e02b2008-06-10 17:40:04 +0000850 # test that unicode input is allowed (issue 2782)
851 self.assertEqual(t.strftime("%m"), "03")
852
Tim Peters2a799bf2002-12-16 20:18:38 +0000853 # A naive object replaces %z and %Z w/ empty strings.
854 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
855
Benjamin Petersone1cdfd72009-01-18 21:02:37 +0000856 #make sure that invalid format specifiers are handled correctly
857 #self.assertRaises(ValueError, t.strftime, "%e")
858 #self.assertRaises(ValueError, t.strftime, "%")
859 #self.assertRaises(ValueError, t.strftime, "%#")
860
861 #oh well, some systems just ignore those invalid ones.
862 #at least, excercise them to make sure that no crashes
863 #are generated
864 for f in ["%e", "%", "%#"]:
865 try:
866 t.strftime(f)
867 except ValueError:
868 pass
869
870 #check that this standard extension works
871 t.strftime("%f")
872
Georg Brandlf78e02b2008-06-10 17:40:04 +0000873
Eric Smith1ba31142007-09-11 18:06:02 +0000874 def test_format(self):
875 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000876 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000877
878 # check that a derived class's __str__() gets called
879 class A(self.theclass):
880 def __str__(self):
881 return 'A'
882 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000883 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000884
885 # check that a derived class's strftime gets called
886 class B(self.theclass):
887 def strftime(self, format_spec):
888 return 'B'
889 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000890 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000891
892 for fmt in ["m:%m d:%d y:%y",
893 "m:%m d:%d y:%y H:%H M:%M S:%S",
894 "%z %Z",
895 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +0000896 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
897 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
898 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +0000899
Tim Peters2a799bf2002-12-16 20:18:38 +0000900 def test_resolution_info(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000901 self.assertTrue(isinstance(self.theclass.min, self.theclass))
902 self.assertTrue(isinstance(self.theclass.max, self.theclass))
903 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
904 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000905
906 def test_extreme_timedelta(self):
907 big = self.theclass.max - self.theclass.min
908 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
909 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
910 # n == 315537897599999999 ~= 2**58.13
911 justasbig = timedelta(0, 0, n)
912 self.assertEqual(big, justasbig)
913 self.assertEqual(self.theclass.min + big, self.theclass.max)
914 self.assertEqual(self.theclass.max - big, self.theclass.min)
915
916 def test_timetuple(self):
917 for i in range(7):
918 # January 2, 1956 is a Monday (0)
919 d = self.theclass(1956, 1, 2+i)
920 t = d.timetuple()
921 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
922 # February 1, 1956 is a Wednesday (2)
923 d = self.theclass(1956, 2, 1+i)
924 t = d.timetuple()
925 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
926 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
927 # of the year.
928 d = self.theclass(1956, 3, 1+i)
929 t = d.timetuple()
930 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
931 self.assertEqual(t.tm_year, 1956)
932 self.assertEqual(t.tm_mon, 3)
933 self.assertEqual(t.tm_mday, 1+i)
934 self.assertEqual(t.tm_hour, 0)
935 self.assertEqual(t.tm_min, 0)
936 self.assertEqual(t.tm_sec, 0)
937 self.assertEqual(t.tm_wday, (3+i)%7)
938 self.assertEqual(t.tm_yday, 61+i)
939 self.assertEqual(t.tm_isdst, -1)
940
941 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000942 args = 6, 7, 23
943 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000944 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000945 green = pickler.dumps(orig, proto)
946 derived = unpickler.loads(green)
947 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000948
949 def test_compare(self):
950 t1 = self.theclass(2, 3, 4)
951 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000952 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000953 self.assertTrue(t1 <= t2)
954 self.assertTrue(t1 >= t2)
955 self.assertTrue(not t1 != t2)
956 self.assertTrue(not t1 < t2)
957 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000958
959 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
960 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000961 self.assertTrue(t1 < t2)
962 self.assertTrue(t2 > t1)
963 self.assertTrue(t1 <= t2)
964 self.assertTrue(t2 >= t1)
965 self.assertTrue(t1 != t2)
966 self.assertTrue(t2 != t1)
967 self.assertTrue(not t1 == t2)
968 self.assertTrue(not t2 == t1)
969 self.assertTrue(not t1 > t2)
970 self.assertTrue(not t2 < t1)
971 self.assertTrue(not t1 >= t2)
972 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000973
Tim Peters68124bb2003-02-08 03:46:31 +0000974 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000975 self.assertEqual(t1 == badarg, False)
976 self.assertEqual(t1 != badarg, True)
977 self.assertEqual(badarg == t1, False)
978 self.assertEqual(badarg != t1, True)
979
Tim Peters2a799bf2002-12-16 20:18:38 +0000980 self.assertRaises(TypeError, lambda: t1 < badarg)
981 self.assertRaises(TypeError, lambda: t1 > badarg)
982 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000983 self.assertRaises(TypeError, lambda: badarg <= t1)
984 self.assertRaises(TypeError, lambda: badarg < t1)
985 self.assertRaises(TypeError, lambda: badarg > t1)
986 self.assertRaises(TypeError, lambda: badarg >= t1)
987
Tim Peters8d81a012003-01-24 22:36:34 +0000988 def test_mixed_compare(self):
989 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000990
991 # Our class can be compared for equality to other classes
992 self.assertEqual(our == 1, False)
993 self.assertEqual(1 == our, False)
994 self.assertEqual(our != 1, True)
995 self.assertEqual(1 != our, True)
996
997 # But the ordering is undefined
998 self.assertRaises(TypeError, lambda: our < 1)
999 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001000
Guido van Rossum19960592006-08-24 17:29:38 +00001001 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001002
Guido van Rossum19960592006-08-24 17:29:38 +00001003 class SomeClass:
1004 pass
1005
1006 their = SomeClass()
1007 self.assertEqual(our == their, False)
1008 self.assertEqual(their == our, False)
1009 self.assertEqual(our != their, True)
1010 self.assertEqual(their != our, True)
1011 self.assertRaises(TypeError, lambda: our < their)
1012 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001013
Guido van Rossum19960592006-08-24 17:29:38 +00001014 # However, if the other class explicitly defines ordering
1015 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001016
Guido van Rossum19960592006-08-24 17:29:38 +00001017 class LargerThanAnything:
1018 def __lt__(self, other):
1019 return False
1020 def __le__(self, other):
1021 return isinstance(other, LargerThanAnything)
1022 def __eq__(self, other):
1023 return isinstance(other, LargerThanAnything)
1024 def __ne__(self, other):
1025 return not isinstance(other, LargerThanAnything)
1026 def __gt__(self, other):
1027 return not isinstance(other, LargerThanAnything)
1028 def __ge__(self, other):
1029 return True
1030
1031 their = LargerThanAnything()
1032 self.assertEqual(our == their, False)
1033 self.assertEqual(their == our, False)
1034 self.assertEqual(our != their, True)
1035 self.assertEqual(their != our, True)
1036 self.assertEqual(our < their, True)
1037 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001038
Tim Peters2a799bf2002-12-16 20:18:38 +00001039 def test_bool(self):
1040 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001041 self.assertTrue(self.theclass.min)
1042 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001043
Guido van Rossum04110fb2007-08-24 16:32:05 +00001044 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001045 # For nasty technical reasons, we can't handle years before 1900.
1046 cls = self.theclass
1047 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1048 for y in 1, 49, 51, 99, 100, 1000, 1899:
1049 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001050
1051 def test_replace(self):
1052 cls = self.theclass
1053 args = [1, 2, 3]
1054 base = cls(*args)
1055 self.assertEqual(base, base.replace())
1056
1057 i = 0
1058 for name, newval in (("year", 2),
1059 ("month", 3),
1060 ("day", 4)):
1061 newargs = args[:]
1062 newargs[i] = newval
1063 expected = cls(*newargs)
1064 got = base.replace(**{name: newval})
1065 self.assertEqual(expected, got)
1066 i += 1
1067
1068 # Out of bounds.
1069 base = cls(2000, 2, 29)
1070 self.assertRaises(ValueError, base.replace, year=2001)
1071
Tim Petersa98924a2003-05-17 05:55:19 +00001072 def test_subclass_date(self):
1073
1074 class C(self.theclass):
1075 theAnswer = 42
1076
1077 def __new__(cls, *args, **kws):
1078 temp = kws.copy()
1079 extra = temp.pop('extra')
1080 result = self.theclass.__new__(cls, *args, **temp)
1081 result.extra = extra
1082 return result
1083
1084 def newmeth(self, start):
1085 return start + self.year + self.month
1086
1087 args = 2003, 4, 14
1088
1089 dt1 = self.theclass(*args)
1090 dt2 = C(*args, **{'extra': 7})
1091
1092 self.assertEqual(dt2.__class__, C)
1093 self.assertEqual(dt2.theAnswer, 42)
1094 self.assertEqual(dt2.extra, 7)
1095 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1096 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1097
Tim Peters604c0132004-06-07 23:04:33 +00001098 def test_pickling_subclass_date(self):
1099
1100 args = 6, 7, 23
1101 orig = SubclassDate(*args)
1102 for pickler, unpickler, proto in pickle_choices:
1103 green = pickler.dumps(orig, proto)
1104 derived = unpickler.loads(green)
1105 self.assertEqual(orig, derived)
1106
Tim Peters3f606292004-03-21 23:38:41 +00001107 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001108 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001109 # This is a low-overhead backdoor. A user can (by intent or
1110 # mistake) pass a string directly, which (if it's the right length)
1111 # will get treated like a pickle, and bypass the normal sanity
1112 # checks in the constructor. This can create insane objects.
1113 # The constructor doesn't want to burn the time to validate all
1114 # fields, but does check the month field. This stops, e.g.,
1115 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001116 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001117 if not issubclass(self.theclass, datetime):
1118 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001119 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001120 self.assertRaises(TypeError, self.theclass,
1121 base[:2] + month_byte + base[3:])
1122 for ord_byte in range(1, 13):
1123 # This shouldn't blow up because of the month byte alone. If
1124 # the implementation changes to do more-careful checking, it may
1125 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001126 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001127
Tim Peters2a799bf2002-12-16 20:18:38 +00001128#############################################################################
1129# datetime tests
1130
Tim Peters604c0132004-06-07 23:04:33 +00001131class SubclassDatetime(datetime):
1132 sub_var = 1
1133
Tim Peters2a799bf2002-12-16 20:18:38 +00001134class TestDateTime(TestDate):
1135
1136 theclass = datetime
1137
1138 def test_basic_attributes(self):
1139 dt = self.theclass(2002, 3, 1, 12, 0)
1140 self.assertEqual(dt.year, 2002)
1141 self.assertEqual(dt.month, 3)
1142 self.assertEqual(dt.day, 1)
1143 self.assertEqual(dt.hour, 12)
1144 self.assertEqual(dt.minute, 0)
1145 self.assertEqual(dt.second, 0)
1146 self.assertEqual(dt.microsecond, 0)
1147
1148 def test_basic_attributes_nonzero(self):
1149 # Make sure all attributes are non-zero so bugs in
1150 # bit-shifting access show up.
1151 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1152 self.assertEqual(dt.year, 2002)
1153 self.assertEqual(dt.month, 3)
1154 self.assertEqual(dt.day, 1)
1155 self.assertEqual(dt.hour, 12)
1156 self.assertEqual(dt.minute, 59)
1157 self.assertEqual(dt.second, 59)
1158 self.assertEqual(dt.microsecond, 8000)
1159
1160 def test_roundtrip(self):
1161 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1162 self.theclass.now()):
1163 # Verify dt -> string -> datetime identity.
1164 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001165 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001166 s = s[9:]
1167 dt2 = eval(s)
1168 self.assertEqual(dt, dt2)
1169
1170 # Verify identity via reconstructing from pieces.
1171 dt2 = self.theclass(dt.year, dt.month, dt.day,
1172 dt.hour, dt.minute, dt.second,
1173 dt.microsecond)
1174 self.assertEqual(dt, dt2)
1175
1176 def test_isoformat(self):
1177 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1178 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1179 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1180 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1181 # str is ISO format with the separator forced to a blank.
1182 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1183
1184 t = self.theclass(2, 3, 2)
1185 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1186 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1187 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1188 # str is ISO format with the separator forced to a blank.
1189 self.assertEqual(str(t), "0002-03-02 00:00:00")
1190
Eric Smith1ba31142007-09-11 18:06:02 +00001191 def test_format(self):
1192 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001193 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001194
1195 # check that a derived class's __str__() gets called
1196 class A(self.theclass):
1197 def __str__(self):
1198 return 'A'
1199 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001200 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001201
1202 # check that a derived class's strftime gets called
1203 class B(self.theclass):
1204 def strftime(self, format_spec):
1205 return 'B'
1206 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001207 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001208
1209 for fmt in ["m:%m d:%d y:%y",
1210 "m:%m d:%d y:%y H:%H M:%M S:%S",
1211 "%z %Z",
1212 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001213 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1214 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1215 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001216
Tim Peters2a799bf2002-12-16 20:18:38 +00001217 def test_more_ctime(self):
1218 # Test fields that TestDate doesn't touch.
1219 import time
1220
1221 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1222 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1223 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1224 # out. The difference is that t.ctime() produces " 2" for the day,
1225 # but platform ctime() produces "02" for the day. According to
1226 # C99, t.ctime() is correct here.
1227 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1228
1229 # So test a case where that difference doesn't matter.
1230 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1231 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1232
1233 def test_tz_independent_comparing(self):
1234 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1235 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1236 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1237 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001238 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001239
1240 # Make sure comparison doesn't forget microseconds, and isn't done
1241 # via comparing a float timestamp (an IEEE double doesn't have enough
1242 # precision to span microsecond resolution across years 1 thru 9999,
1243 # so comparing via timestamp necessarily calls some distinct values
1244 # equal).
1245 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1246 us = timedelta(microseconds=1)
1247 dt2 = dt1 + us
1248 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001249 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001250
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001251 def test_strftime_with_bad_tzname_replace(self):
1252 # verify ok if tzinfo.tzname().replace() returns a non-string
1253 class MyTzInfo(FixedOffset):
1254 def tzname(self, dt):
1255 class MyStr(str):
1256 def replace(self, *args):
1257 return None
1258 return MyStr('name')
1259 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1260 self.assertRaises(TypeError, t.strftime, '%Z')
1261
Tim Peters2a799bf2002-12-16 20:18:38 +00001262 def test_bad_constructor_arguments(self):
1263 # bad years
1264 self.theclass(MINYEAR, 1, 1) # no exception
1265 self.theclass(MAXYEAR, 1, 1) # no exception
1266 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1267 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1268 # bad months
1269 self.theclass(2000, 1, 1) # no exception
1270 self.theclass(2000, 12, 1) # no exception
1271 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1272 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1273 # bad days
1274 self.theclass(2000, 2, 29) # no exception
1275 self.theclass(2004, 2, 29) # no exception
1276 self.theclass(2400, 2, 29) # no exception
1277 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1278 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1279 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1280 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1281 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1282 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1283 # bad hours
1284 self.theclass(2000, 1, 31, 0) # no exception
1285 self.theclass(2000, 1, 31, 23) # no exception
1286 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1287 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1288 # bad minutes
1289 self.theclass(2000, 1, 31, 23, 0) # no exception
1290 self.theclass(2000, 1, 31, 23, 59) # no exception
1291 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1292 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1293 # bad seconds
1294 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1295 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1296 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1297 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1298 # bad microseconds
1299 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1300 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1301 self.assertRaises(ValueError, self.theclass,
1302 2000, 1, 31, 23, 59, 59, -1)
1303 self.assertRaises(ValueError, self.theclass,
1304 2000, 1, 31, 23, 59, 59,
1305 1000000)
1306
1307 def test_hash_equality(self):
1308 d = self.theclass(2000, 12, 31, 23, 30, 17)
1309 e = self.theclass(2000, 12, 31, 23, 30, 17)
1310 self.assertEqual(d, e)
1311 self.assertEqual(hash(d), hash(e))
1312
1313 dic = {d: 1}
1314 dic[e] = 2
1315 self.assertEqual(len(dic), 1)
1316 self.assertEqual(dic[d], 2)
1317 self.assertEqual(dic[e], 2)
1318
1319 d = self.theclass(2001, 1, 1, 0, 5, 17)
1320 e = self.theclass(2001, 1, 1, 0, 5, 17)
1321 self.assertEqual(d, e)
1322 self.assertEqual(hash(d), hash(e))
1323
1324 dic = {d: 1}
1325 dic[e] = 2
1326 self.assertEqual(len(dic), 1)
1327 self.assertEqual(dic[d], 2)
1328 self.assertEqual(dic[e], 2)
1329
1330 def test_computations(self):
1331 a = self.theclass(2002, 1, 31)
1332 b = self.theclass(1956, 1, 31)
1333 diff = a-b
1334 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1335 self.assertEqual(diff.seconds, 0)
1336 self.assertEqual(diff.microseconds, 0)
1337 a = self.theclass(2002, 3, 2, 17, 6)
1338 millisec = timedelta(0, 0, 1000)
1339 hour = timedelta(0, 3600)
1340 day = timedelta(1)
1341 week = timedelta(7)
1342 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1343 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1344 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1345 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1346 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1347 self.assertEqual(a - hour, a + -hour)
1348 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1349 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1350 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1351 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1352 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1353 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1354 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1355 self.assertEqual((a + week) - a, week)
1356 self.assertEqual((a + day) - a, day)
1357 self.assertEqual((a + hour) - a, hour)
1358 self.assertEqual((a + millisec) - a, millisec)
1359 self.assertEqual((a - week) - a, -week)
1360 self.assertEqual((a - day) - a, -day)
1361 self.assertEqual((a - hour) - a, -hour)
1362 self.assertEqual((a - millisec) - a, -millisec)
1363 self.assertEqual(a - (a + week), -week)
1364 self.assertEqual(a - (a + day), -day)
1365 self.assertEqual(a - (a + hour), -hour)
1366 self.assertEqual(a - (a + millisec), -millisec)
1367 self.assertEqual(a - (a - week), week)
1368 self.assertEqual(a - (a - day), day)
1369 self.assertEqual(a - (a - hour), hour)
1370 self.assertEqual(a - (a - millisec), millisec)
1371 self.assertEqual(a + (week + day + hour + millisec),
1372 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1373 self.assertEqual(a + (week + day + hour + millisec),
1374 (((a + week) + day) + hour) + millisec)
1375 self.assertEqual(a - (week + day + hour + millisec),
1376 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1377 self.assertEqual(a - (week + day + hour + millisec),
1378 (((a - week) - day) - hour) - millisec)
1379 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001380 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001381 self.assertRaises(TypeError, lambda: a+i)
1382 self.assertRaises(TypeError, lambda: a-i)
1383 self.assertRaises(TypeError, lambda: i+a)
1384 self.assertRaises(TypeError, lambda: i-a)
1385
1386 # delta - datetime is senseless.
1387 self.assertRaises(TypeError, lambda: day - a)
1388 # mixing datetime and (delta or datetime) via * or // is senseless
1389 self.assertRaises(TypeError, lambda: day * a)
1390 self.assertRaises(TypeError, lambda: a * day)
1391 self.assertRaises(TypeError, lambda: day // a)
1392 self.assertRaises(TypeError, lambda: a // day)
1393 self.assertRaises(TypeError, lambda: a * a)
1394 self.assertRaises(TypeError, lambda: a // a)
1395 # datetime + datetime is senseless
1396 self.assertRaises(TypeError, lambda: a + a)
1397
1398 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001399 args = 6, 7, 23, 20, 59, 1, 64**2
1400 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001401 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001402 green = pickler.dumps(orig, proto)
1403 derived = unpickler.loads(green)
1404 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001405
Guido van Rossum275666f2003-02-07 21:49:01 +00001406 def test_more_pickling(self):
1407 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1408 s = pickle.dumps(a)
1409 b = pickle.loads(s)
1410 self.assertEqual(b.year, 2003)
1411 self.assertEqual(b.month, 2)
1412 self.assertEqual(b.day, 7)
1413
Tim Peters604c0132004-06-07 23:04:33 +00001414 def test_pickling_subclass_datetime(self):
1415 args = 6, 7, 23, 20, 59, 1, 64**2
1416 orig = SubclassDatetime(*args)
1417 for pickler, unpickler, proto in pickle_choices:
1418 green = pickler.dumps(orig, proto)
1419 derived = unpickler.loads(green)
1420 self.assertEqual(orig, derived)
1421
Tim Peters2a799bf2002-12-16 20:18:38 +00001422 def test_more_compare(self):
1423 # The test_compare() inherited from TestDate covers the error cases.
1424 # We just want to test lexicographic ordering on the members datetime
1425 # has that date lacks.
1426 args = [2000, 11, 29, 20, 58, 16, 999998]
1427 t1 = self.theclass(*args)
1428 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001429 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001430 self.assertTrue(t1 <= t2)
1431 self.assertTrue(t1 >= t2)
1432 self.assertTrue(not t1 != t2)
1433 self.assertTrue(not t1 < t2)
1434 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001435
1436 for i in range(len(args)):
1437 newargs = args[:]
1438 newargs[i] = args[i] + 1
1439 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001440 self.assertTrue(t1 < t2)
1441 self.assertTrue(t2 > t1)
1442 self.assertTrue(t1 <= t2)
1443 self.assertTrue(t2 >= t1)
1444 self.assertTrue(t1 != t2)
1445 self.assertTrue(t2 != t1)
1446 self.assertTrue(not t1 == t2)
1447 self.assertTrue(not t2 == t1)
1448 self.assertTrue(not t1 > t2)
1449 self.assertTrue(not t2 < t1)
1450 self.assertTrue(not t1 >= t2)
1451 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001452
1453
1454 # A helper for timestamp constructor tests.
1455 def verify_field_equality(self, expected, got):
1456 self.assertEqual(expected.tm_year, got.year)
1457 self.assertEqual(expected.tm_mon, got.month)
1458 self.assertEqual(expected.tm_mday, got.day)
1459 self.assertEqual(expected.tm_hour, got.hour)
1460 self.assertEqual(expected.tm_min, got.minute)
1461 self.assertEqual(expected.tm_sec, got.second)
1462
1463 def test_fromtimestamp(self):
1464 import time
1465
1466 ts = time.time()
1467 expected = time.localtime(ts)
1468 got = self.theclass.fromtimestamp(ts)
1469 self.verify_field_equality(expected, got)
1470
1471 def test_utcfromtimestamp(self):
1472 import time
1473
1474 ts = time.time()
1475 expected = time.gmtime(ts)
1476 got = self.theclass.utcfromtimestamp(ts)
1477 self.verify_field_equality(expected, got)
1478
Thomas Wouters477c8d52006-05-27 19:21:47 +00001479 def test_microsecond_rounding(self):
1480 # Test whether fromtimestamp "rounds up" floats that are less
1481 # than one microsecond smaller than an integer.
1482 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1483 self.theclass.fromtimestamp(1))
1484
Tim Peters1b6f7a92004-06-20 02:50:16 +00001485 def test_insane_fromtimestamp(self):
1486 # It's possible that some platform maps time_t to double,
1487 # and that this test will fail there. This test should
1488 # exempt such platforms (provided they return reasonable
1489 # results!).
1490 for insane in -1e200, 1e200:
1491 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1492 insane)
1493
1494 def test_insane_utcfromtimestamp(self):
1495 # It's possible that some platform maps time_t to double,
1496 # and that this test will fail there. This test should
1497 # exempt such platforms (provided they return reasonable
1498 # results!).
1499 for insane in -1e200, 1e200:
1500 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1501 insane)
1502
Guido van Rossumd8faa362007-04-27 19:54:29 +00001503 def test_negative_float_fromtimestamp(self):
1504 # Windows doesn't accept negative timestamps
1505 if os.name == "nt":
1506 return
1507 # The result is tz-dependent; at least test that this doesn't
1508 # fail (like it did before bug 1646728 was fixed).
1509 self.theclass.fromtimestamp(-1.05)
1510
1511 def test_negative_float_utcfromtimestamp(self):
1512 # Windows doesn't accept negative timestamps
1513 if os.name == "nt":
1514 return
1515 d = self.theclass.utcfromtimestamp(-1.05)
1516 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1517
Tim Peters2a799bf2002-12-16 20:18:38 +00001518 def test_utcnow(self):
1519 import time
1520
1521 # Call it a success if utcnow() and utcfromtimestamp() are within
1522 # a second of each other.
1523 tolerance = timedelta(seconds=1)
1524 for dummy in range(3):
1525 from_now = self.theclass.utcnow()
1526 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1527 if abs(from_timestamp - from_now) <= tolerance:
1528 break
1529 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001530 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001531
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001532 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001533 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001534
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001535 string = '2004-12-01 13:02:47.197'
1536 format = '%Y-%m-%d %H:%M:%S.%f'
1537 result, frac = _strptime._strptime(string, format)
1538 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001539 got = self.theclass.strptime(string, format)
1540 self.assertEqual(expected, got)
1541
Tim Peters2a799bf2002-12-16 20:18:38 +00001542 def test_more_timetuple(self):
1543 # This tests fields beyond those tested by the TestDate.test_timetuple.
1544 t = self.theclass(2004, 12, 31, 6, 22, 33)
1545 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1546 self.assertEqual(t.timetuple(),
1547 (t.year, t.month, t.day,
1548 t.hour, t.minute, t.second,
1549 t.weekday(),
1550 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1551 -1))
1552 tt = t.timetuple()
1553 self.assertEqual(tt.tm_year, t.year)
1554 self.assertEqual(tt.tm_mon, t.month)
1555 self.assertEqual(tt.tm_mday, t.day)
1556 self.assertEqual(tt.tm_hour, t.hour)
1557 self.assertEqual(tt.tm_min, t.minute)
1558 self.assertEqual(tt.tm_sec, t.second)
1559 self.assertEqual(tt.tm_wday, t.weekday())
1560 self.assertEqual(tt.tm_yday, t.toordinal() -
1561 date(t.year, 1, 1).toordinal() + 1)
1562 self.assertEqual(tt.tm_isdst, -1)
1563
1564 def test_more_strftime(self):
1565 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001566 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1567 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1568 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001569
1570 def test_extract(self):
1571 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1572 self.assertEqual(dt.date(), date(2002, 3, 4))
1573 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1574
1575 def test_combine(self):
1576 d = date(2002, 3, 4)
1577 t = time(18, 45, 3, 1234)
1578 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1579 combine = self.theclass.combine
1580 dt = combine(d, t)
1581 self.assertEqual(dt, expected)
1582
1583 dt = combine(time=t, date=d)
1584 self.assertEqual(dt, expected)
1585
1586 self.assertEqual(d, dt.date())
1587 self.assertEqual(t, dt.time())
1588 self.assertEqual(dt, combine(dt.date(), dt.time()))
1589
1590 self.assertRaises(TypeError, combine) # need an arg
1591 self.assertRaises(TypeError, combine, d) # need two args
1592 self.assertRaises(TypeError, combine, t, d) # args reversed
1593 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1594 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1595
Tim Peters12bf3392002-12-24 05:41:27 +00001596 def test_replace(self):
1597 cls = self.theclass
1598 args = [1, 2, 3, 4, 5, 6, 7]
1599 base = cls(*args)
1600 self.assertEqual(base, base.replace())
1601
1602 i = 0
1603 for name, newval in (("year", 2),
1604 ("month", 3),
1605 ("day", 4),
1606 ("hour", 5),
1607 ("minute", 6),
1608 ("second", 7),
1609 ("microsecond", 8)):
1610 newargs = args[:]
1611 newargs[i] = newval
1612 expected = cls(*newargs)
1613 got = base.replace(**{name: newval})
1614 self.assertEqual(expected, got)
1615 i += 1
1616
1617 # Out of bounds.
1618 base = cls(2000, 2, 29)
1619 self.assertRaises(ValueError, base.replace, year=2001)
1620
Tim Peters80475bb2002-12-25 07:40:55 +00001621 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001622 # Pretty boring! The TZ test is more interesting here. astimezone()
1623 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001624 dt = self.theclass.now()
1625 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001626 self.assertRaises(TypeError, dt.astimezone) # not enough args
1627 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1628 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001629 self.assertRaises(ValueError, dt.astimezone, f) # naive
1630 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001631
Tim Peters52dcce22003-01-23 16:36:11 +00001632 class Bogus(tzinfo):
1633 def utcoffset(self, dt): return None
1634 def dst(self, dt): return timedelta(0)
1635 bog = Bogus()
1636 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1637
1638 class AlsoBogus(tzinfo):
1639 def utcoffset(self, dt): return timedelta(0)
1640 def dst(self, dt): return None
1641 alsobog = AlsoBogus()
1642 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001643
Tim Petersa98924a2003-05-17 05:55:19 +00001644 def test_subclass_datetime(self):
1645
1646 class C(self.theclass):
1647 theAnswer = 42
1648
1649 def __new__(cls, *args, **kws):
1650 temp = kws.copy()
1651 extra = temp.pop('extra')
1652 result = self.theclass.__new__(cls, *args, **temp)
1653 result.extra = extra
1654 return result
1655
1656 def newmeth(self, start):
1657 return start + self.year + self.month + self.second
1658
1659 args = 2003, 4, 14, 12, 13, 41
1660
1661 dt1 = self.theclass(*args)
1662 dt2 = C(*args, **{'extra': 7})
1663
1664 self.assertEqual(dt2.__class__, C)
1665 self.assertEqual(dt2.theAnswer, 42)
1666 self.assertEqual(dt2.extra, 7)
1667 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1668 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1669 dt1.second - 7)
1670
Tim Peters604c0132004-06-07 23:04:33 +00001671class SubclassTime(time):
1672 sub_var = 1
1673
Guido van Rossumd8faa362007-04-27 19:54:29 +00001674class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001675
1676 theclass = time
1677
1678 def test_basic_attributes(self):
1679 t = self.theclass(12, 0)
1680 self.assertEqual(t.hour, 12)
1681 self.assertEqual(t.minute, 0)
1682 self.assertEqual(t.second, 0)
1683 self.assertEqual(t.microsecond, 0)
1684
1685 def test_basic_attributes_nonzero(self):
1686 # Make sure all attributes are non-zero so bugs in
1687 # bit-shifting access show up.
1688 t = self.theclass(12, 59, 59, 8000)
1689 self.assertEqual(t.hour, 12)
1690 self.assertEqual(t.minute, 59)
1691 self.assertEqual(t.second, 59)
1692 self.assertEqual(t.microsecond, 8000)
1693
1694 def test_roundtrip(self):
1695 t = self.theclass(1, 2, 3, 4)
1696
1697 # Verify t -> string -> time identity.
1698 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001699 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001700 s = s[9:]
1701 t2 = eval(s)
1702 self.assertEqual(t, t2)
1703
1704 # Verify identity via reconstructing from pieces.
1705 t2 = self.theclass(t.hour, t.minute, t.second,
1706 t.microsecond)
1707 self.assertEqual(t, t2)
1708
1709 def test_comparing(self):
1710 args = [1, 2, 3, 4]
1711 t1 = self.theclass(*args)
1712 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001713 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001714 self.assertTrue(t1 <= t2)
1715 self.assertTrue(t1 >= t2)
1716 self.assertTrue(not t1 != t2)
1717 self.assertTrue(not t1 < t2)
1718 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001719
1720 for i in range(len(args)):
1721 newargs = args[:]
1722 newargs[i] = args[i] + 1
1723 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001724 self.assertTrue(t1 < t2)
1725 self.assertTrue(t2 > t1)
1726 self.assertTrue(t1 <= t2)
1727 self.assertTrue(t2 >= t1)
1728 self.assertTrue(t1 != t2)
1729 self.assertTrue(t2 != t1)
1730 self.assertTrue(not t1 == t2)
1731 self.assertTrue(not t2 == t1)
1732 self.assertTrue(not t1 > t2)
1733 self.assertTrue(not t2 < t1)
1734 self.assertTrue(not t1 >= t2)
1735 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001736
Tim Peters68124bb2003-02-08 03:46:31 +00001737 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001738 self.assertEqual(t1 == badarg, False)
1739 self.assertEqual(t1 != badarg, True)
1740 self.assertEqual(badarg == t1, False)
1741 self.assertEqual(badarg != t1, True)
1742
Tim Peters2a799bf2002-12-16 20:18:38 +00001743 self.assertRaises(TypeError, lambda: t1 <= badarg)
1744 self.assertRaises(TypeError, lambda: t1 < badarg)
1745 self.assertRaises(TypeError, lambda: t1 > badarg)
1746 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001747 self.assertRaises(TypeError, lambda: badarg <= t1)
1748 self.assertRaises(TypeError, lambda: badarg < t1)
1749 self.assertRaises(TypeError, lambda: badarg > t1)
1750 self.assertRaises(TypeError, lambda: badarg >= t1)
1751
1752 def test_bad_constructor_arguments(self):
1753 # bad hours
1754 self.theclass(0, 0) # no exception
1755 self.theclass(23, 0) # no exception
1756 self.assertRaises(ValueError, self.theclass, -1, 0)
1757 self.assertRaises(ValueError, self.theclass, 24, 0)
1758 # bad minutes
1759 self.theclass(23, 0) # no exception
1760 self.theclass(23, 59) # no exception
1761 self.assertRaises(ValueError, self.theclass, 23, -1)
1762 self.assertRaises(ValueError, self.theclass, 23, 60)
1763 # bad seconds
1764 self.theclass(23, 59, 0) # no exception
1765 self.theclass(23, 59, 59) # no exception
1766 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1767 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1768 # bad microseconds
1769 self.theclass(23, 59, 59, 0) # no exception
1770 self.theclass(23, 59, 59, 999999) # no exception
1771 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1772 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1773
1774 def test_hash_equality(self):
1775 d = self.theclass(23, 30, 17)
1776 e = self.theclass(23, 30, 17)
1777 self.assertEqual(d, e)
1778 self.assertEqual(hash(d), hash(e))
1779
1780 dic = {d: 1}
1781 dic[e] = 2
1782 self.assertEqual(len(dic), 1)
1783 self.assertEqual(dic[d], 2)
1784 self.assertEqual(dic[e], 2)
1785
1786 d = self.theclass(0, 5, 17)
1787 e = self.theclass(0, 5, 17)
1788 self.assertEqual(d, e)
1789 self.assertEqual(hash(d), hash(e))
1790
1791 dic = {d: 1}
1792 dic[e] = 2
1793 self.assertEqual(len(dic), 1)
1794 self.assertEqual(dic[d], 2)
1795 self.assertEqual(dic[e], 2)
1796
1797 def test_isoformat(self):
1798 t = self.theclass(4, 5, 1, 123)
1799 self.assertEqual(t.isoformat(), "04:05:01.000123")
1800 self.assertEqual(t.isoformat(), str(t))
1801
1802 t = self.theclass()
1803 self.assertEqual(t.isoformat(), "00:00:00")
1804 self.assertEqual(t.isoformat(), str(t))
1805
1806 t = self.theclass(microsecond=1)
1807 self.assertEqual(t.isoformat(), "00:00:00.000001")
1808 self.assertEqual(t.isoformat(), str(t))
1809
1810 t = self.theclass(microsecond=10)
1811 self.assertEqual(t.isoformat(), "00:00:00.000010")
1812 self.assertEqual(t.isoformat(), str(t))
1813
1814 t = self.theclass(microsecond=100)
1815 self.assertEqual(t.isoformat(), "00:00:00.000100")
1816 self.assertEqual(t.isoformat(), str(t))
1817
1818 t = self.theclass(microsecond=1000)
1819 self.assertEqual(t.isoformat(), "00:00:00.001000")
1820 self.assertEqual(t.isoformat(), str(t))
1821
1822 t = self.theclass(microsecond=10000)
1823 self.assertEqual(t.isoformat(), "00:00:00.010000")
1824 self.assertEqual(t.isoformat(), str(t))
1825
1826 t = self.theclass(microsecond=100000)
1827 self.assertEqual(t.isoformat(), "00:00:00.100000")
1828 self.assertEqual(t.isoformat(), str(t))
1829
Thomas Wouterscf297e42007-02-23 15:07:44 +00001830 def test_1653736(self):
1831 # verify it doesn't accept extra keyword arguments
1832 t = self.theclass(second=1)
1833 self.assertRaises(TypeError, t.isoformat, foo=3)
1834
Tim Peters2a799bf2002-12-16 20:18:38 +00001835 def test_strftime(self):
1836 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001837 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001838 # A naive object replaces %z and %Z with empty strings.
1839 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1840
Eric Smith1ba31142007-09-11 18:06:02 +00001841 def test_format(self):
1842 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001843 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001844
1845 # check that a derived class's __str__() gets called
1846 class A(self.theclass):
1847 def __str__(self):
1848 return 'A'
1849 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001850 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001851
1852 # check that a derived class's strftime gets called
1853 class B(self.theclass):
1854 def strftime(self, format_spec):
1855 return 'B'
1856 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001857 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001858
1859 for fmt in ['%H %M %S',
1860 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001861 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1862 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1863 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001864
Tim Peters2a799bf2002-12-16 20:18:38 +00001865 def test_str(self):
1866 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1867 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1868 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1869 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1870 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1871
1872 def test_repr(self):
1873 name = 'datetime.' + self.theclass.__name__
1874 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1875 "%s(1, 2, 3, 4)" % name)
1876 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1877 "%s(10, 2, 3, 4000)" % name)
1878 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1879 "%s(0, 2, 3, 400000)" % name)
1880 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1881 "%s(12, 2, 3)" % name)
1882 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1883 "%s(23, 15)" % name)
1884
1885 def test_resolution_info(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001886 self.assertTrue(isinstance(self.theclass.min, self.theclass))
1887 self.assertTrue(isinstance(self.theclass.max, self.theclass))
1888 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
1889 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001890
1891 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001892 args = 20, 59, 16, 64**2
1893 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001894 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001895 green = pickler.dumps(orig, proto)
1896 derived = unpickler.loads(green)
1897 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001898
Tim Peters604c0132004-06-07 23:04:33 +00001899 def test_pickling_subclass_time(self):
1900 args = 20, 59, 16, 64**2
1901 orig = SubclassTime(*args)
1902 for pickler, unpickler, proto in pickle_choices:
1903 green = pickler.dumps(orig, proto)
1904 derived = unpickler.loads(green)
1905 self.assertEqual(orig, derived)
1906
Tim Peters2a799bf2002-12-16 20:18:38 +00001907 def test_bool(self):
1908 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001909 self.assertTrue(cls(1))
1910 self.assertTrue(cls(0, 1))
1911 self.assertTrue(cls(0, 0, 1))
1912 self.assertTrue(cls(0, 0, 0, 1))
1913 self.assertTrue(not cls(0))
1914 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001915
Tim Peters12bf3392002-12-24 05:41:27 +00001916 def test_replace(self):
1917 cls = self.theclass
1918 args = [1, 2, 3, 4]
1919 base = cls(*args)
1920 self.assertEqual(base, base.replace())
1921
1922 i = 0
1923 for name, newval in (("hour", 5),
1924 ("minute", 6),
1925 ("second", 7),
1926 ("microsecond", 8)):
1927 newargs = args[:]
1928 newargs[i] = newval
1929 expected = cls(*newargs)
1930 got = base.replace(**{name: newval})
1931 self.assertEqual(expected, got)
1932 i += 1
1933
1934 # Out of bounds.
1935 base = cls(1)
1936 self.assertRaises(ValueError, base.replace, hour=24)
1937 self.assertRaises(ValueError, base.replace, minute=-1)
1938 self.assertRaises(ValueError, base.replace, second=100)
1939 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1940
Tim Petersa98924a2003-05-17 05:55:19 +00001941 def test_subclass_time(self):
1942
1943 class C(self.theclass):
1944 theAnswer = 42
1945
1946 def __new__(cls, *args, **kws):
1947 temp = kws.copy()
1948 extra = temp.pop('extra')
1949 result = self.theclass.__new__(cls, *args, **temp)
1950 result.extra = extra
1951 return result
1952
1953 def newmeth(self, start):
1954 return start + self.hour + self.second
1955
1956 args = 4, 5, 6
1957
1958 dt1 = self.theclass(*args)
1959 dt2 = C(*args, **{'extra': 7})
1960
1961 self.assertEqual(dt2.__class__, C)
1962 self.assertEqual(dt2.theAnswer, 42)
1963 self.assertEqual(dt2.extra, 7)
1964 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1965 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1966
Armin Rigof4afb212005-11-07 07:15:48 +00001967 def test_backdoor_resistance(self):
1968 # see TestDate.test_backdoor_resistance().
1969 base = '2:59.0'
1970 for hour_byte in ' ', '9', chr(24), '\xff':
1971 self.assertRaises(TypeError, self.theclass,
1972 hour_byte + base[1:])
1973
Tim Peters855fe882002-12-22 03:43:39 +00001974# A mixin for classes with a tzinfo= argument. Subclasses must define
1975# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001976# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001977class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001978
Tim Petersbad8ff02002-12-30 20:52:32 +00001979 def test_argument_passing(self):
1980 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001981 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001982 class introspective(tzinfo):
1983 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001984 def utcoffset(self, dt):
1985 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001986 dst = utcoffset
1987
1988 obj = cls(1, 2, 3, tzinfo=introspective())
1989
Tim Peters0bf60bd2003-01-08 20:40:01 +00001990 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001991 self.assertEqual(obj.tzname(), expected)
1992
Tim Peters0bf60bd2003-01-08 20:40:01 +00001993 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001994 self.assertEqual(obj.utcoffset(), expected)
1995 self.assertEqual(obj.dst(), expected)
1996
Tim Peters855fe882002-12-22 03:43:39 +00001997 def test_bad_tzinfo_classes(self):
1998 cls = self.theclass
1999 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002000
Tim Peters855fe882002-12-22 03:43:39 +00002001 class NiceTry(object):
2002 def __init__(self): pass
2003 def utcoffset(self, dt): pass
2004 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2005
2006 class BetterTry(tzinfo):
2007 def __init__(self): pass
2008 def utcoffset(self, dt): pass
2009 b = BetterTry()
2010 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002011 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002012
2013 def test_utc_offset_out_of_bounds(self):
2014 class Edgy(tzinfo):
2015 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002016 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002017 def utcoffset(self, dt):
2018 return self.offset
2019
2020 cls = self.theclass
2021 for offset, legit in ((-1440, False),
2022 (-1439, True),
2023 (1439, True),
2024 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002025 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002026 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002027 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002028 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002029 else:
2030 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002031 if legit:
2032 aofs = abs(offset)
2033 h, m = divmod(aofs, 60)
2034 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002035 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002036 t = t.timetz()
2037 self.assertEqual(str(t), "01:02:03" + tag)
2038 else:
2039 self.assertRaises(ValueError, str, t)
2040
2041 def test_tzinfo_classes(self):
2042 cls = self.theclass
2043 class C1(tzinfo):
2044 def utcoffset(self, dt): return None
2045 def dst(self, dt): return None
2046 def tzname(self, dt): return None
2047 for t in (cls(1, 1, 1),
2048 cls(1, 1, 1, tzinfo=None),
2049 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002050 self.assertTrue(t.utcoffset() is None)
2051 self.assertTrue(t.dst() is None)
2052 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002053
Tim Peters855fe882002-12-22 03:43:39 +00002054 class C3(tzinfo):
2055 def utcoffset(self, dt): return timedelta(minutes=-1439)
2056 def dst(self, dt): return timedelta(minutes=1439)
2057 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002058 t = cls(1, 1, 1, tzinfo=C3())
2059 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2060 self.assertEqual(t.dst(), timedelta(minutes=1439))
2061 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002062
2063 # Wrong types.
2064 class C4(tzinfo):
2065 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002066 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002067 def tzname(self, dt): return 0
2068 t = cls(1, 1, 1, tzinfo=C4())
2069 self.assertRaises(TypeError, t.utcoffset)
2070 self.assertRaises(TypeError, t.dst)
2071 self.assertRaises(TypeError, t.tzname)
2072
2073 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002074 class C6(tzinfo):
2075 def utcoffset(self, dt): return timedelta(hours=-24)
2076 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002077 t = cls(1, 1, 1, tzinfo=C6())
2078 self.assertRaises(ValueError, t.utcoffset)
2079 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002080
2081 # Not a whole number of minutes.
2082 class C7(tzinfo):
2083 def utcoffset(self, dt): return timedelta(seconds=61)
2084 def dst(self, dt): return timedelta(microseconds=-81)
2085 t = cls(1, 1, 1, tzinfo=C7())
2086 self.assertRaises(ValueError, t.utcoffset)
2087 self.assertRaises(ValueError, t.dst)
2088
Tim Peters4c0db782002-12-26 05:01:19 +00002089 def test_aware_compare(self):
2090 cls = self.theclass
2091
Tim Peters60c76e42002-12-27 00:41:11 +00002092 # Ensure that utcoffset() gets ignored if the comparands have
2093 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002094 class OperandDependentOffset(tzinfo):
2095 def utcoffset(self, t):
2096 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002097 # d0 and d1 equal after adjustment
2098 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002099 else:
Tim Peters397301e2003-01-02 21:28:08 +00002100 # d2 off in the weeds
2101 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002102
2103 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2104 d0 = base.replace(minute=3)
2105 d1 = base.replace(minute=9)
2106 d2 = base.replace(minute=11)
2107 for x in d0, d1, d2:
2108 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002109 for op in lt, le, gt, ge, eq, ne:
2110 got = op(x, y)
2111 expected = op(x.minute, y.minute)
2112 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002113
2114 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002115 # Note that a time can't actually have an operand-depedent offset,
2116 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2117 # so skip this test for time.
2118 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002119 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2120 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2121 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2122 for x in d0, d1, d2:
2123 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002124 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002125 if (x is d0 or x is d1) and (y is d0 or y is d1):
2126 expected = 0
2127 elif x is y is d2:
2128 expected = 0
2129 elif x is d2:
2130 expected = -1
2131 else:
2132 assert y is d2
2133 expected = 1
2134 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002135
Tim Peters855fe882002-12-22 03:43:39 +00002136
Tim Peters0bf60bd2003-01-08 20:40:01 +00002137# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002138class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002139 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002140
2141 def test_empty(self):
2142 t = self.theclass()
2143 self.assertEqual(t.hour, 0)
2144 self.assertEqual(t.minute, 0)
2145 self.assertEqual(t.second, 0)
2146 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002147 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002148
Tim Peters2a799bf2002-12-16 20:18:38 +00002149 def test_zones(self):
2150 est = FixedOffset(-300, "EST", 1)
2151 utc = FixedOffset(0, "UTC", -2)
2152 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002153 t1 = time( 7, 47, tzinfo=est)
2154 t2 = time(12, 47, tzinfo=utc)
2155 t3 = time(13, 47, tzinfo=met)
2156 t4 = time(microsecond=40)
2157 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002158
2159 self.assertEqual(t1.tzinfo, est)
2160 self.assertEqual(t2.tzinfo, utc)
2161 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002162 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002163 self.assertEqual(t5.tzinfo, utc)
2164
Tim Peters855fe882002-12-22 03:43:39 +00002165 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2166 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2167 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002168 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002169 self.assertRaises(TypeError, t1.utcoffset, "no args")
2170
2171 self.assertEqual(t1.tzname(), "EST")
2172 self.assertEqual(t2.tzname(), "UTC")
2173 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002174 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002175 self.assertRaises(TypeError, t1.tzname, "no args")
2176
Tim Peters855fe882002-12-22 03:43:39 +00002177 self.assertEqual(t1.dst(), timedelta(minutes=1))
2178 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2179 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002180 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002181 self.assertRaises(TypeError, t1.dst, "no args")
2182
2183 self.assertEqual(hash(t1), hash(t2))
2184 self.assertEqual(hash(t1), hash(t3))
2185 self.assertEqual(hash(t2), hash(t3))
2186
2187 self.assertEqual(t1, t2)
2188 self.assertEqual(t1, t3)
2189 self.assertEqual(t2, t3)
2190 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2191 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2192 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2193
2194 self.assertEqual(str(t1), "07:47:00-05:00")
2195 self.assertEqual(str(t2), "12:47:00+00:00")
2196 self.assertEqual(str(t3), "13:47:00+01:00")
2197 self.assertEqual(str(t4), "00:00:00.000040")
2198 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2199
2200 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2201 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2202 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2203 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2204 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2205
Tim Peters0bf60bd2003-01-08 20:40:01 +00002206 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002207 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2208 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2209 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2210 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2211 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2212
2213 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2214 "07:47:00 %Z=EST %z=-0500")
2215 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2216 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2217
2218 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002219 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002220 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2221 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2222
Tim Petersb92bb712002-12-21 17:44:07 +00002223 # Check that an invalid tzname result raises an exception.
2224 class Badtzname(tzinfo):
2225 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002226 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002227 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2228 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002229
2230 def test_hash_edge_cases(self):
2231 # Offsets that overflow a basic time.
2232 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2233 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2234 self.assertEqual(hash(t1), hash(t2))
2235
2236 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2237 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2238 self.assertEqual(hash(t1), hash(t2))
2239
Tim Peters2a799bf2002-12-16 20:18:38 +00002240 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002241 # Try one without a tzinfo.
2242 args = 20, 59, 16, 64**2
2243 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002244 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002245 green = pickler.dumps(orig, proto)
2246 derived = unpickler.loads(green)
2247 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002248
2249 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002250 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002251 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002252 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002253 green = pickler.dumps(orig, proto)
2254 derived = unpickler.loads(green)
2255 self.assertEqual(orig, derived)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002256 self.assertTrue(isinstance(derived.tzinfo, PicklableFixedOffset))
Tim Peters96940c92003-01-31 21:55:33 +00002257 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2258 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002259
2260 def test_more_bool(self):
2261 # Test cases with non-None tzinfo.
2262 cls = self.theclass
2263
2264 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002265 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002266
2267 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002268 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002269
2270 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002271 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002272
2273 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002274 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002275
2276 # Mostly ensuring this doesn't overflow internally.
2277 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002278 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002279
2280 # But this should yield a value error -- the utcoffset is bogus.
2281 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2282 self.assertRaises(ValueError, lambda: bool(t))
2283
2284 # Likewise.
2285 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2286 self.assertRaises(ValueError, lambda: bool(t))
2287
Tim Peters12bf3392002-12-24 05:41:27 +00002288 def test_replace(self):
2289 cls = self.theclass
2290 z100 = FixedOffset(100, "+100")
2291 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2292 args = [1, 2, 3, 4, z100]
2293 base = cls(*args)
2294 self.assertEqual(base, base.replace())
2295
2296 i = 0
2297 for name, newval in (("hour", 5),
2298 ("minute", 6),
2299 ("second", 7),
2300 ("microsecond", 8),
2301 ("tzinfo", zm200)):
2302 newargs = args[:]
2303 newargs[i] = newval
2304 expected = cls(*newargs)
2305 got = base.replace(**{name: newval})
2306 self.assertEqual(expected, got)
2307 i += 1
2308
2309 # Ensure we can get rid of a tzinfo.
2310 self.assertEqual(base.tzname(), "+100")
2311 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002312 self.assertTrue(base2.tzinfo is None)
2313 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002314
2315 # Ensure we can add one.
2316 base3 = base2.replace(tzinfo=z100)
2317 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002318 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002319
2320 # Out of bounds.
2321 base = cls(1)
2322 self.assertRaises(ValueError, base.replace, hour=24)
2323 self.assertRaises(ValueError, base.replace, minute=-1)
2324 self.assertRaises(ValueError, base.replace, second=100)
2325 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2326
Tim Peters60c76e42002-12-27 00:41:11 +00002327 def test_mixed_compare(self):
2328 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002329 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002330 self.assertEqual(t1, t2)
2331 t2 = t2.replace(tzinfo=None)
2332 self.assertEqual(t1, t2)
2333 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2334 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002335 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2336 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002337
Tim Peters0bf60bd2003-01-08 20:40:01 +00002338 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002339 class Varies(tzinfo):
2340 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002341 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002342 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002343 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002344 return self.offset
2345
2346 v = Varies()
2347 t1 = t2.replace(tzinfo=v)
2348 t2 = t2.replace(tzinfo=v)
2349 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2350 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2351 self.assertEqual(t1, t2)
2352
2353 # But if they're not identical, it isn't ignored.
2354 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002355 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002356
Tim Petersa98924a2003-05-17 05:55:19 +00002357 def test_subclass_timetz(self):
2358
2359 class C(self.theclass):
2360 theAnswer = 42
2361
2362 def __new__(cls, *args, **kws):
2363 temp = kws.copy()
2364 extra = temp.pop('extra')
2365 result = self.theclass.__new__(cls, *args, **temp)
2366 result.extra = extra
2367 return result
2368
2369 def newmeth(self, start):
2370 return start + self.hour + self.second
2371
2372 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2373
2374 dt1 = self.theclass(*args)
2375 dt2 = C(*args, **{'extra': 7})
2376
2377 self.assertEqual(dt2.__class__, C)
2378 self.assertEqual(dt2.theAnswer, 42)
2379 self.assertEqual(dt2.extra, 7)
2380 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2381 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2382
Tim Peters4c0db782002-12-26 05:01:19 +00002383
Tim Peters0bf60bd2003-01-08 20:40:01 +00002384# Testing datetime objects with a non-None tzinfo.
2385
Guido van Rossumd8faa362007-04-27 19:54:29 +00002386class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002387 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002388
2389 def test_trivial(self):
2390 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2391 self.assertEqual(dt.year, 1)
2392 self.assertEqual(dt.month, 2)
2393 self.assertEqual(dt.day, 3)
2394 self.assertEqual(dt.hour, 4)
2395 self.assertEqual(dt.minute, 5)
2396 self.assertEqual(dt.second, 6)
2397 self.assertEqual(dt.microsecond, 7)
2398 self.assertEqual(dt.tzinfo, None)
2399
2400 def test_even_more_compare(self):
2401 # The test_compare() and test_more_compare() inherited from TestDate
2402 # and TestDateTime covered non-tzinfo cases.
2403
2404 # Smallest possible after UTC adjustment.
2405 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2406 # Largest possible after UTC adjustment.
2407 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2408 tzinfo=FixedOffset(-1439, ""))
2409
2410 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002411 self.assertTrue(t1 < t2)
2412 self.assertTrue(t1 != t2)
2413 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002414
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002415 self.assertEqual(t1, t1)
2416 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002417
2418 # Equal afer adjustment.
2419 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2420 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2421 self.assertEqual(t1, t2)
2422
2423 # Change t1 not to subtract a minute, and t1 should be larger.
2424 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002425 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002426
2427 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2428 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002429 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002430
2431 # Back to the original t1, but make seconds resolve it.
2432 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2433 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002434 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002435
2436 # Likewise, but make microseconds resolve it.
2437 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2438 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002439 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002440
2441 # Make t2 naive and it should fail.
2442 t2 = self.theclass.min
2443 self.assertRaises(TypeError, lambda: t1 == t2)
2444 self.assertEqual(t2, t2)
2445
2446 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2447 class Naive(tzinfo):
2448 def utcoffset(self, dt): return None
2449 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2450 self.assertRaises(TypeError, lambda: t1 == t2)
2451 self.assertEqual(t2, t2)
2452
2453 # OTOH, it's OK to compare two of these mixing the two ways of being
2454 # naive.
2455 t1 = self.theclass(5, 6, 7)
2456 self.assertEqual(t1, t2)
2457
2458 # Try a bogus uctoffset.
2459 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002460 def utcoffset(self, dt):
2461 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002462 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2463 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002464 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002465
Tim Peters2a799bf2002-12-16 20:18:38 +00002466 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002467 # Try one without a tzinfo.
2468 args = 6, 7, 23, 20, 59, 1, 64**2
2469 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002470 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002471 green = pickler.dumps(orig, proto)
2472 derived = unpickler.loads(green)
2473 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002474
2475 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002476 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002477 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002478 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002479 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002480 green = pickler.dumps(orig, proto)
2481 derived = unpickler.loads(green)
2482 self.assertEqual(orig, derived)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002483 self.assertTrue(isinstance(derived.tzinfo,
Tim Peters96940c92003-01-31 21:55:33 +00002484 PicklableFixedOffset))
2485 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2486 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002487
2488 def test_extreme_hashes(self):
2489 # If an attempt is made to hash these via subtracting the offset
2490 # then hashing a datetime object, OverflowError results. The
2491 # Python implementation used to blow up here.
2492 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2493 hash(t)
2494 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2495 tzinfo=FixedOffset(-1439, ""))
2496 hash(t)
2497
2498 # OTOH, an OOB offset should blow up.
2499 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2500 self.assertRaises(ValueError, hash, t)
2501
2502 def test_zones(self):
2503 est = FixedOffset(-300, "EST")
2504 utc = FixedOffset(0, "UTC")
2505 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002506 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2507 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2508 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002509 self.assertEqual(t1.tzinfo, est)
2510 self.assertEqual(t2.tzinfo, utc)
2511 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002512 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2513 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2514 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002515 self.assertEqual(t1.tzname(), "EST")
2516 self.assertEqual(t2.tzname(), "UTC")
2517 self.assertEqual(t3.tzname(), "MET")
2518 self.assertEqual(hash(t1), hash(t2))
2519 self.assertEqual(hash(t1), hash(t3))
2520 self.assertEqual(hash(t2), hash(t3))
2521 self.assertEqual(t1, t2)
2522 self.assertEqual(t1, t3)
2523 self.assertEqual(t2, t3)
2524 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2525 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2526 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002527 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002528 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2529 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2530 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2531
2532 def test_combine(self):
2533 met = FixedOffset(60, "MET")
2534 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002535 tz = time(18, 45, 3, 1234, tzinfo=met)
2536 dt = datetime.combine(d, tz)
2537 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002538 tzinfo=met))
2539
2540 def test_extract(self):
2541 met = FixedOffset(60, "MET")
2542 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2543 self.assertEqual(dt.date(), date(2002, 3, 4))
2544 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002545 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002546
2547 def test_tz_aware_arithmetic(self):
2548 import random
2549
2550 now = self.theclass.now()
2551 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002552 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002553 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002554 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002555 self.assertEqual(nowaware.timetz(), timeaware)
2556
2557 # Can't mix aware and non-aware.
2558 self.assertRaises(TypeError, lambda: now - nowaware)
2559 self.assertRaises(TypeError, lambda: nowaware - now)
2560
Tim Peters0bf60bd2003-01-08 20:40:01 +00002561 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002562 self.assertRaises(TypeError, lambda: now + nowaware)
2563 self.assertRaises(TypeError, lambda: nowaware + now)
2564 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2565
2566 # Subtracting should yield 0.
2567 self.assertEqual(now - now, timedelta(0))
2568 self.assertEqual(nowaware - nowaware, timedelta(0))
2569
2570 # Adding a delta should preserve tzinfo.
2571 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2572 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002573 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002574 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002575 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002576 self.assertEqual(nowawareplus, nowawareplus2)
2577
2578 # that - delta should be what we started with, and that - what we
2579 # started with should be delta.
2580 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002581 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002582 self.assertEqual(nowaware, diff)
2583 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2584 self.assertEqual(nowawareplus - nowaware, delta)
2585
2586 # Make up a random timezone.
2587 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002588 # Attach it to nowawareplus.
2589 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002590 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002591 # Make sure the difference takes the timezone adjustments into account.
2592 got = nowaware - nowawareplus
2593 # Expected: (nowaware base - nowaware offset) -
2594 # (nowawareplus base - nowawareplus offset) =
2595 # (nowaware base - nowawareplus base) +
2596 # (nowawareplus offset - nowaware offset) =
2597 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002598 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002599 self.assertEqual(got, expected)
2600
2601 # Try max possible difference.
2602 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2603 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2604 tzinfo=FixedOffset(-1439, "max"))
2605 maxdiff = max - min
2606 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2607 timedelta(minutes=2*1439))
2608
2609 def test_tzinfo_now(self):
2610 meth = self.theclass.now
2611 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2612 base = meth()
2613 # Try with and without naming the keyword.
2614 off42 = FixedOffset(42, "42")
2615 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002616 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002617 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002618 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002619 # Bad argument with and w/o naming the keyword.
2620 self.assertRaises(TypeError, meth, 16)
2621 self.assertRaises(TypeError, meth, tzinfo=16)
2622 # Bad keyword name.
2623 self.assertRaises(TypeError, meth, tinfo=off42)
2624 # Too many args.
2625 self.assertRaises(TypeError, meth, off42, off42)
2626
Tim Peters10cadce2003-01-23 19:58:02 +00002627 # We don't know which time zone we're in, and don't have a tzinfo
2628 # class to represent it, so seeing whether a tz argument actually
2629 # does a conversion is tricky.
2630 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2631 utc = FixedOffset(0, "utc", 0)
2632 for dummy in range(3):
2633 now = datetime.now(weirdtz)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002634 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002635 utcnow = datetime.utcnow().replace(tzinfo=utc)
2636 now2 = utcnow.astimezone(weirdtz)
2637 if abs(now - now2) < timedelta(seconds=30):
2638 break
2639 # Else the code is broken, or more than 30 seconds passed between
2640 # calls; assuming the latter, just try again.
2641 else:
2642 # Three strikes and we're out.
2643 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2644
Tim Peters2a799bf2002-12-16 20:18:38 +00002645 def test_tzinfo_fromtimestamp(self):
2646 import time
2647 meth = self.theclass.fromtimestamp
2648 ts = time.time()
2649 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2650 base = meth(ts)
2651 # Try with and without naming the keyword.
2652 off42 = FixedOffset(42, "42")
2653 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002654 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002655 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002656 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002657 # Bad argument with and w/o naming the keyword.
2658 self.assertRaises(TypeError, meth, ts, 16)
2659 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2660 # Bad keyword name.
2661 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2662 # Too many args.
2663 self.assertRaises(TypeError, meth, ts, off42, off42)
2664 # Too few args.
2665 self.assertRaises(TypeError, meth)
2666
Tim Peters2a44a8d2003-01-23 20:53:10 +00002667 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002668 timestamp = 1000000000
2669 utcdatetime = datetime.utcfromtimestamp(timestamp)
2670 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2671 # But on some flavor of Mac, it's nowhere near that. So we can't have
2672 # any idea here what time that actually is, we can only test that
2673 # relative changes match.
2674 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2675 tz = FixedOffset(utcoffset, "tz", 0)
2676 expected = utcdatetime + utcoffset
2677 got = datetime.fromtimestamp(timestamp, tz)
2678 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002679
Tim Peters2a799bf2002-12-16 20:18:38 +00002680 def test_tzinfo_utcnow(self):
2681 meth = self.theclass.utcnow
2682 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2683 base = meth()
2684 # Try with and without naming the keyword; for whatever reason,
2685 # utcnow() doesn't accept a tzinfo argument.
2686 off42 = FixedOffset(42, "42")
2687 self.assertRaises(TypeError, meth, off42)
2688 self.assertRaises(TypeError, meth, tzinfo=off42)
2689
2690 def test_tzinfo_utcfromtimestamp(self):
2691 import time
2692 meth = self.theclass.utcfromtimestamp
2693 ts = time.time()
2694 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2695 base = meth(ts)
2696 # Try with and without naming the keyword; for whatever reason,
2697 # utcfromtimestamp() doesn't accept a tzinfo argument.
2698 off42 = FixedOffset(42, "42")
2699 self.assertRaises(TypeError, meth, ts, off42)
2700 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2701
2702 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002703 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002704 # DST flag.
2705 class DST(tzinfo):
2706 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002707 if isinstance(dstvalue, int):
2708 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002709 self.dstvalue = dstvalue
2710 def dst(self, dt):
2711 return self.dstvalue
2712
2713 cls = self.theclass
2714 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2715 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2716 t = d.timetuple()
2717 self.assertEqual(1, t.tm_year)
2718 self.assertEqual(1, t.tm_mon)
2719 self.assertEqual(1, t.tm_mday)
2720 self.assertEqual(10, t.tm_hour)
2721 self.assertEqual(20, t.tm_min)
2722 self.assertEqual(30, t.tm_sec)
2723 self.assertEqual(0, t.tm_wday)
2724 self.assertEqual(1, t.tm_yday)
2725 self.assertEqual(flag, t.tm_isdst)
2726
2727 # dst() returns wrong type.
2728 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2729
2730 # dst() at the edge.
2731 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2732 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2733
2734 # dst() out of range.
2735 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2736 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2737
2738 def test_utctimetuple(self):
2739 class DST(tzinfo):
2740 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002741 if isinstance(dstvalue, int):
2742 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002743 self.dstvalue = dstvalue
2744 def dst(self, dt):
2745 return self.dstvalue
2746
2747 cls = self.theclass
2748 # This can't work: DST didn't implement utcoffset.
2749 self.assertRaises(NotImplementedError,
2750 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2751
2752 class UOFS(DST):
2753 def __init__(self, uofs, dofs=None):
2754 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002755 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002756 def utcoffset(self, dt):
2757 return self.uofs
2758
2759 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2760 # in effect for a UTC time.
2761 for dstvalue in -33, 33, 0, None:
2762 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2763 t = d.utctimetuple()
2764 self.assertEqual(d.year, t.tm_year)
2765 self.assertEqual(d.month, t.tm_mon)
2766 self.assertEqual(d.day, t.tm_mday)
2767 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2768 self.assertEqual(13, t.tm_min)
2769 self.assertEqual(d.second, t.tm_sec)
2770 self.assertEqual(d.weekday(), t.tm_wday)
2771 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2772 t.tm_yday)
2773 self.assertEqual(0, t.tm_isdst)
2774
2775 # At the edges, UTC adjustment can normalize into years out-of-range
2776 # for a datetime object. Ensure that a correct timetuple is
2777 # created anyway.
2778 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2779 # That goes back 1 minute less than a full day.
2780 t = tiny.utctimetuple()
2781 self.assertEqual(t.tm_year, MINYEAR-1)
2782 self.assertEqual(t.tm_mon, 12)
2783 self.assertEqual(t.tm_mday, 31)
2784 self.assertEqual(t.tm_hour, 0)
2785 self.assertEqual(t.tm_min, 1)
2786 self.assertEqual(t.tm_sec, 37)
2787 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2788 self.assertEqual(t.tm_isdst, 0)
2789
2790 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2791 # That goes forward 1 minute less than a full day.
2792 t = huge.utctimetuple()
2793 self.assertEqual(t.tm_year, MAXYEAR+1)
2794 self.assertEqual(t.tm_mon, 1)
2795 self.assertEqual(t.tm_mday, 1)
2796 self.assertEqual(t.tm_hour, 23)
2797 self.assertEqual(t.tm_min, 58)
2798 self.assertEqual(t.tm_sec, 37)
2799 self.assertEqual(t.tm_yday, 1)
2800 self.assertEqual(t.tm_isdst, 0)
2801
2802 def test_tzinfo_isoformat(self):
2803 zero = FixedOffset(0, "+00:00")
2804 plus = FixedOffset(220, "+03:40")
2805 minus = FixedOffset(-231, "-03:51")
2806 unknown = FixedOffset(None, "")
2807
2808 cls = self.theclass
2809 datestr = '0001-02-03'
2810 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002811 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002812 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2813 timestr = '04:05:59' + (us and '.987001' or '')
2814 ofsstr = ofs is not None and d.tzname() or ''
2815 tailstr = timestr + ofsstr
2816 iso = d.isoformat()
2817 self.assertEqual(iso, datestr + 'T' + tailstr)
2818 self.assertEqual(iso, d.isoformat('T'))
2819 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002820 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002821 self.assertEqual(str(d), datestr + ' ' + tailstr)
2822
Tim Peters12bf3392002-12-24 05:41:27 +00002823 def test_replace(self):
2824 cls = self.theclass
2825 z100 = FixedOffset(100, "+100")
2826 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2827 args = [1, 2, 3, 4, 5, 6, 7, z100]
2828 base = cls(*args)
2829 self.assertEqual(base, base.replace())
2830
2831 i = 0
2832 for name, newval in (("year", 2),
2833 ("month", 3),
2834 ("day", 4),
2835 ("hour", 5),
2836 ("minute", 6),
2837 ("second", 7),
2838 ("microsecond", 8),
2839 ("tzinfo", zm200)):
2840 newargs = args[:]
2841 newargs[i] = newval
2842 expected = cls(*newargs)
2843 got = base.replace(**{name: newval})
2844 self.assertEqual(expected, got)
2845 i += 1
2846
2847 # Ensure we can get rid of a tzinfo.
2848 self.assertEqual(base.tzname(), "+100")
2849 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002850 self.assertTrue(base2.tzinfo is None)
2851 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002852
2853 # Ensure we can add one.
2854 base3 = base2.replace(tzinfo=z100)
2855 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002856 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002857
2858 # Out of bounds.
2859 base = cls(2000, 2, 29)
2860 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002861
Tim Peters80475bb2002-12-25 07:40:55 +00002862 def test_more_astimezone(self):
2863 # The inherited test_astimezone covered some trivial and error cases.
2864 fnone = FixedOffset(None, "None")
2865 f44m = FixedOffset(44, "44")
2866 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2867
Tim Peters10cadce2003-01-23 19:58:02 +00002868 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002869 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002870 # Replacing with degenerate tzinfo raises an exception.
2871 self.assertRaises(ValueError, dt.astimezone, fnone)
2872 # Ditto with None tz.
2873 self.assertRaises(TypeError, dt.astimezone, None)
2874 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002875 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002876 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002877 self.assertEqual(x.date(), dt.date())
2878 self.assertEqual(x.time(), dt.time())
2879
2880 # Replacing with different tzinfo does adjust.
2881 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002882 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002883 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2884 expected = dt - dt.utcoffset() # in effect, convert to UTC
2885 expected += fm5h.utcoffset(dt) # and from there to local time
2886 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2887 self.assertEqual(got.date(), expected.date())
2888 self.assertEqual(got.time(), expected.time())
2889 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002890 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002891 self.assertEqual(got, expected)
2892
Tim Peters4c0db782002-12-26 05:01:19 +00002893 def test_aware_subtract(self):
2894 cls = self.theclass
2895
Tim Peters60c76e42002-12-27 00:41:11 +00002896 # Ensure that utcoffset() is ignored when the operands have the
2897 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002898 class OperandDependentOffset(tzinfo):
2899 def utcoffset(self, t):
2900 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002901 # d0 and d1 equal after adjustment
2902 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002903 else:
Tim Peters397301e2003-01-02 21:28:08 +00002904 # d2 off in the weeds
2905 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002906
2907 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2908 d0 = base.replace(minute=3)
2909 d1 = base.replace(minute=9)
2910 d2 = base.replace(minute=11)
2911 for x in d0, d1, d2:
2912 for y in d0, d1, d2:
2913 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002914 expected = timedelta(minutes=x.minute - y.minute)
2915 self.assertEqual(got, expected)
2916
2917 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2918 # ignored.
2919 base = cls(8, 9, 10, 11, 12, 13, 14)
2920 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2921 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2922 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2923 for x in d0, d1, d2:
2924 for y in d0, d1, d2:
2925 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002926 if (x is d0 or x is d1) and (y is d0 or y is d1):
2927 expected = timedelta(0)
2928 elif x is y is d2:
2929 expected = timedelta(0)
2930 elif x is d2:
2931 expected = timedelta(minutes=(11-59)-0)
2932 else:
2933 assert y is d2
2934 expected = timedelta(minutes=0-(11-59))
2935 self.assertEqual(got, expected)
2936
Tim Peters60c76e42002-12-27 00:41:11 +00002937 def test_mixed_compare(self):
2938 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002939 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002940 self.assertEqual(t1, t2)
2941 t2 = t2.replace(tzinfo=None)
2942 self.assertEqual(t1, t2)
2943 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2944 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002945 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2946 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002947
Tim Peters0bf60bd2003-01-08 20:40:01 +00002948 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002949 class Varies(tzinfo):
2950 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002951 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002952 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002953 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002954 return self.offset
2955
2956 v = Varies()
2957 t1 = t2.replace(tzinfo=v)
2958 t2 = t2.replace(tzinfo=v)
2959 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2960 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2961 self.assertEqual(t1, t2)
2962
2963 # But if they're not identical, it isn't ignored.
2964 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002965 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002966
Tim Petersa98924a2003-05-17 05:55:19 +00002967 def test_subclass_datetimetz(self):
2968
2969 class C(self.theclass):
2970 theAnswer = 42
2971
2972 def __new__(cls, *args, **kws):
2973 temp = kws.copy()
2974 extra = temp.pop('extra')
2975 result = self.theclass.__new__(cls, *args, **temp)
2976 result.extra = extra
2977 return result
2978
2979 def newmeth(self, start):
2980 return start + self.hour + self.year
2981
2982 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2983
2984 dt1 = self.theclass(*args)
2985 dt2 = C(*args, **{'extra': 7})
2986
2987 self.assertEqual(dt2.__class__, C)
2988 self.assertEqual(dt2.theAnswer, 42)
2989 self.assertEqual(dt2.extra, 7)
2990 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2991 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2992
Tim Peters621818b2002-12-29 23:44:49 +00002993# Pain to set up DST-aware tzinfo classes.
2994
2995def first_sunday_on_or_after(dt):
2996 days_to_go = 6 - dt.weekday()
2997 if days_to_go:
2998 dt += timedelta(days_to_go)
2999 return dt
3000
3001ZERO = timedelta(0)
3002HOUR = timedelta(hours=1)
3003DAY = timedelta(days=1)
3004# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3005DSTSTART = datetime(1, 4, 1, 2)
3006# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003007# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3008# being standard time on that day, there is no spelling in local time of
3009# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3010DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003011
3012class USTimeZone(tzinfo):
3013
3014 def __init__(self, hours, reprname, stdname, dstname):
3015 self.stdoffset = timedelta(hours=hours)
3016 self.reprname = reprname
3017 self.stdname = stdname
3018 self.dstname = dstname
3019
3020 def __repr__(self):
3021 return self.reprname
3022
3023 def tzname(self, dt):
3024 if self.dst(dt):
3025 return self.dstname
3026 else:
3027 return self.stdname
3028
3029 def utcoffset(self, dt):
3030 return self.stdoffset + self.dst(dt)
3031
3032 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003033 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003034 # An exception instead may be sensible here, in one or more of
3035 # the cases.
3036 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003037 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003038
3039 # Find first Sunday in April.
3040 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3041 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3042
3043 # Find last Sunday in October.
3044 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3045 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3046
Tim Peters621818b2002-12-29 23:44:49 +00003047 # Can't compare naive to aware objects, so strip the timezone from
3048 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003049 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003050 return HOUR
3051 else:
3052 return ZERO
3053
Tim Peters521fc152002-12-31 17:36:56 +00003054Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3055Central = USTimeZone(-6, "Central", "CST", "CDT")
3056Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3057Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003058utc_real = FixedOffset(0, "UTC", 0)
3059# For better test coverage, we want another flavor of UTC that's west of
3060# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003061utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003062
3063class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003064 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003065 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003066 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003067
Tim Peters0bf60bd2003-01-08 20:40:01 +00003068 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003069
Tim Peters521fc152002-12-31 17:36:56 +00003070 # Check a time that's inside DST.
3071 def checkinside(self, dt, tz, utc, dston, dstoff):
3072 self.assertEqual(dt.dst(), HOUR)
3073
3074 # Conversion to our own timezone is always an identity.
3075 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003076
3077 asutc = dt.astimezone(utc)
3078 there_and_back = asutc.astimezone(tz)
3079
3080 # Conversion to UTC and back isn't always an identity here,
3081 # because there are redundant spellings (in local time) of
3082 # UTC time when DST begins: the clock jumps from 1:59:59
3083 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3084 # make sense then. The classes above treat 2:MM:SS as
3085 # daylight time then (it's "after 2am"), really an alias
3086 # for 1:MM:SS standard time. The latter form is what
3087 # conversion back from UTC produces.
3088 if dt.date() == dston.date() and dt.hour == 2:
3089 # We're in the redundant hour, and coming back from
3090 # UTC gives the 1:MM:SS standard-time spelling.
3091 self.assertEqual(there_and_back + HOUR, dt)
3092 # Although during was considered to be in daylight
3093 # time, there_and_back is not.
3094 self.assertEqual(there_and_back.dst(), ZERO)
3095 # They're the same times in UTC.
3096 self.assertEqual(there_and_back.astimezone(utc),
3097 dt.astimezone(utc))
3098 else:
3099 # We're not in the redundant hour.
3100 self.assertEqual(dt, there_and_back)
3101
Tim Peters327098a2003-01-20 22:54:38 +00003102 # Because we have a redundant spelling when DST begins, there is
3103 # (unforunately) an hour when DST ends that can't be spelled at all in
3104 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3105 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3106 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3107 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3108 # expressed in local time. Nevertheless, we want conversion back
3109 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003110 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003111 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003112 if dt.date() == dstoff.date() and dt.hour == 0:
3113 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003114 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003115 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3116 nexthour_utc += HOUR
3117 nexthour_tz = nexthour_utc.astimezone(tz)
3118 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003119 else:
Tim Peters327098a2003-01-20 22:54:38 +00003120 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003121
3122 # Check a time that's outside DST.
3123 def checkoutside(self, dt, tz, utc):
3124 self.assertEqual(dt.dst(), ZERO)
3125
3126 # Conversion to our own timezone is always an identity.
3127 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003128
3129 # Converting to UTC and back is an identity too.
3130 asutc = dt.astimezone(utc)
3131 there_and_back = asutc.astimezone(tz)
3132 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003133
Tim Peters1024bf82002-12-30 17:09:40 +00003134 def convert_between_tz_and_utc(self, tz, utc):
3135 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003136 # Because 1:MM on the day DST ends is taken as being standard time,
3137 # there is no spelling in tz for the last hour of daylight time.
3138 # For purposes of the test, the last hour of DST is 0:MM, which is
3139 # taken as being daylight time (and 1:MM is taken as being standard
3140 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003141 dstoff = self.dstoff.replace(tzinfo=tz)
3142 for delta in (timedelta(weeks=13),
3143 DAY,
3144 HOUR,
3145 timedelta(minutes=1),
3146 timedelta(microseconds=1)):
3147
Tim Peters521fc152002-12-31 17:36:56 +00003148 self.checkinside(dston, tz, utc, dston, dstoff)
3149 for during in dston + delta, dstoff - delta:
3150 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003151
Tim Peters521fc152002-12-31 17:36:56 +00003152 self.checkoutside(dstoff, tz, utc)
3153 for outside in dston - delta, dstoff + delta:
3154 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003155
Tim Peters621818b2002-12-29 23:44:49 +00003156 def test_easy(self):
3157 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003158 self.convert_between_tz_and_utc(Eastern, utc_real)
3159 self.convert_between_tz_and_utc(Pacific, utc_real)
3160 self.convert_between_tz_and_utc(Eastern, utc_fake)
3161 self.convert_between_tz_and_utc(Pacific, utc_fake)
3162 # The next is really dancing near the edge. It works because
3163 # Pacific and Eastern are far enough apart that their "problem
3164 # hours" don't overlap.
3165 self.convert_between_tz_and_utc(Eastern, Pacific)
3166 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003167 # OTOH, these fail! Don't enable them. The difficulty is that
3168 # the edge case tests assume that every hour is representable in
3169 # the "utc" class. This is always true for a fixed-offset tzinfo
3170 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3171 # For these adjacent DST-aware time zones, the range of time offsets
3172 # tested ends up creating hours in the one that aren't representable
3173 # in the other. For the same reason, we would see failures in the
3174 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3175 # offset deltas in convert_between_tz_and_utc().
3176 #
3177 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3178 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003179
Tim Petersf3615152003-01-01 21:51:37 +00003180 def test_tricky(self):
3181 # 22:00 on day before daylight starts.
3182 fourback = self.dston - timedelta(hours=4)
3183 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003184 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003185 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3186 # 2", we should get the 3 spelling.
3187 # If we plug 22:00 the day before into Eastern, it "looks like std
3188 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3189 # to 22:00 lands on 2:00, which makes no sense in local time (the
3190 # local clock jumps from 1 to 3). The point here is to make sure we
3191 # get the 3 spelling.
3192 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003193 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003194 self.assertEqual(expected, got)
3195
3196 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3197 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003198 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003199 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3200 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3201 # spelling.
3202 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003203 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003204 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003205
Tim Petersadf64202003-01-04 06:03:15 +00003206 # Now on the day DST ends, we want "repeat an hour" behavior.
3207 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3208 # EST 23:MM 0:MM 1:MM 2:MM
3209 # EDT 0:MM 1:MM 2:MM 3:MM
3210 # wall 0:MM 1:MM 1:MM 2:MM against these
3211 for utc in utc_real, utc_fake:
3212 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003213 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003214 # Convert that to UTC.
3215 first_std_hour -= tz.utcoffset(None)
3216 # Adjust for possibly fake UTC.
3217 asutc = first_std_hour + utc.utcoffset(None)
3218 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3219 # tz=Eastern.
3220 asutcbase = asutc.replace(tzinfo=utc)
3221 for tzhour in (0, 1, 1, 2):
3222 expectedbase = self.dstoff.replace(hour=tzhour)
3223 for minute in 0, 30, 59:
3224 expected = expectedbase.replace(minute=minute)
3225 asutc = asutcbase.replace(minute=minute)
3226 astz = asutc.astimezone(tz)
3227 self.assertEqual(astz.replace(tzinfo=None), expected)
3228 asutcbase += HOUR
3229
3230
Tim Peters710fb152003-01-02 19:35:54 +00003231 def test_bogus_dst(self):
3232 class ok(tzinfo):
3233 def utcoffset(self, dt): return HOUR
3234 def dst(self, dt): return HOUR
3235
3236 now = self.theclass.now().replace(tzinfo=utc_real)
3237 # Doesn't blow up.
3238 now.astimezone(ok())
3239
3240 # Does blow up.
3241 class notok(ok):
3242 def dst(self, dt): return None
3243 self.assertRaises(ValueError, now.astimezone, notok())
3244
Tim Peters52dcce22003-01-23 16:36:11 +00003245 def test_fromutc(self):
3246 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3247 now = datetime.utcnow().replace(tzinfo=utc_real)
3248 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3249 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3250 enow = Eastern.fromutc(now) # doesn't blow up
3251 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3252 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3253 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3254
3255 # Always converts UTC to standard time.
3256 class FauxUSTimeZone(USTimeZone):
3257 def fromutc(self, dt):
3258 return dt + self.stdoffset
3259 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3260
3261 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3262 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3263 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3264
3265 # Check around DST start.
3266 start = self.dston.replace(hour=4, tzinfo=Eastern)
3267 fstart = start.replace(tzinfo=FEastern)
3268 for wall in 23, 0, 1, 3, 4, 5:
3269 expected = start.replace(hour=wall)
3270 if wall == 23:
3271 expected -= timedelta(days=1)
3272 got = Eastern.fromutc(start)
3273 self.assertEqual(expected, got)
3274
3275 expected = fstart + FEastern.stdoffset
3276 got = FEastern.fromutc(fstart)
3277 self.assertEqual(expected, got)
3278
3279 # Ensure astimezone() calls fromutc() too.
3280 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3281 self.assertEqual(expected, got)
3282
3283 start += HOUR
3284 fstart += HOUR
3285
3286 # Check around DST end.
3287 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3288 fstart = start.replace(tzinfo=FEastern)
3289 for wall in 0, 1, 1, 2, 3, 4:
3290 expected = start.replace(hour=wall)
3291 got = Eastern.fromutc(start)
3292 self.assertEqual(expected, got)
3293
3294 expected = fstart + FEastern.stdoffset
3295 got = FEastern.fromutc(fstart)
3296 self.assertEqual(expected, got)
3297
3298 # Ensure astimezone() calls fromutc() too.
3299 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3300 self.assertEqual(expected, got)
3301
3302 start += HOUR
3303 fstart += HOUR
3304
Tim Peters710fb152003-01-02 19:35:54 +00003305
Tim Peters528ca532004-09-16 01:30:50 +00003306#############################################################################
3307# oddballs
3308
3309class Oddballs(unittest.TestCase):
3310
3311 def test_bug_1028306(self):
3312 # Trying to compare a date to a datetime should act like a mixed-
3313 # type comparison, despite that datetime is a subclass of date.
3314 as_date = date.today()
3315 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003316 self.assertTrue(as_date != as_datetime)
3317 self.assertTrue(as_datetime != as_date)
3318 self.assertTrue(not as_date == as_datetime)
3319 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003320 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3321 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3322 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3323 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3324 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3325 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3326 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3327 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3328
3329 # Neverthelss, comparison should work with the base-class (date)
3330 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003331 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003332 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003333 as_different = as_datetime.replace(day= different_day)
3334 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003335
3336 # And date should compare with other subclasses of date. If a
3337 # subclass wants to stop this, it's up to the subclass to do so.
3338 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3339 self.assertEqual(as_date, date_sc)
3340 self.assertEqual(date_sc, as_date)
3341
3342 # Ditto for datetimes.
3343 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3344 as_date.day, 0, 0, 0)
3345 self.assertEqual(as_datetime, datetime_sc)
3346 self.assertEqual(datetime_sc, as_datetime)
3347
Tim Peters2a799bf2002-12-16 20:18:38 +00003348def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003349 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003350
3351if __name__ == "__main__":
3352 test_main()