blob: 74736bc317d4bbee108491e94d1c539c58f23df9 [file] [log] [blame]
Tim Peters0bf60bd2003-01-08 20:40:01 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
Tim Peters2a799bf2002-12-16 20:18:38 +00005
Guido van Rossumd8faa362007-04-27 19:54:29 +00006import os
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00008import unittest
9
Mark Dickinsona56c4672009-01-27 18:17:45 +000010from operator import lt, le, gt, ge, eq, ne
11
Benjamin Petersonee8712c2008-05-20 21:35:26 +000012from test import support
Tim Peters2a799bf2002-12-16 20:18:38 +000013
14from datetime import MINYEAR, MAXYEAR
15from datetime import timedelta
16from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000017from datetime import time
18from datetime import date, datetime
19
Guido van Rossumbe6fe542007-07-19 23:55:34 +000020pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
21assert len(pickle_choices) == 3
Guido van Rossum177e41a2003-01-30 22:06:23 +000022
Tim Peters68124bb2003-02-08 03:46:31 +000023# An arbitrary collection of objects of non-datetime types, for testing
24# mixed-type comparisons.
Mark Dickinson5c2db372009-12-05 20:28:34 +000025OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000026
Tim Peters2a799bf2002-12-16 20:18:38 +000027
28#############################################################################
29# module tests
30
31class TestModule(unittest.TestCase):
32
33 def test_constants(self):
34 import datetime
35 self.assertEqual(datetime.MINYEAR, 1)
36 self.assertEqual(datetime.MAXYEAR, 9999)
37
38#############################################################################
39# tzinfo tests
40
41class FixedOffset(tzinfo):
42 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000043 if isinstance(offset, int):
44 offset = timedelta(minutes=offset)
45 if isinstance(dstoffset, int):
46 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000047 self.__offset = offset
48 self.__name = name
49 self.__dstoffset = dstoffset
50 def __repr__(self):
51 return self.__name.lower()
52 def utcoffset(self, dt):
53 return self.__offset
54 def tzname(self, dt):
55 return self.__name
56 def dst(self, dt):
57 return self.__dstoffset
58
Tim Petersfb8472c2002-12-21 05:04:42 +000059class PicklableFixedOffset(FixedOffset):
60 def __init__(self, offset=None, name=None, dstoffset=None):
61 FixedOffset.__init__(self, offset, name, dstoffset)
62
Tim Peters2a799bf2002-12-16 20:18:38 +000063class TestTZInfo(unittest.TestCase):
64
65 def test_non_abstractness(self):
66 # In order to allow subclasses to get pickled, the C implementation
67 # wasn't able to get away with having __init__ raise
68 # NotImplementedError.
69 useless = tzinfo()
70 dt = datetime.max
71 self.assertRaises(NotImplementedError, useless.tzname, dt)
72 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
73 self.assertRaises(NotImplementedError, useless.dst, dt)
74
75 def test_subclass_must_override(self):
76 class NotEnough(tzinfo):
77 def __init__(self, offset, name):
78 self.__offset = offset
79 self.__name = name
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000080 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000081 ne = NotEnough(3, "NotByALongShot")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000082 self.assertTrue(isinstance(ne, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000083
84 dt = datetime.now()
85 self.assertRaises(NotImplementedError, ne.tzname, dt)
86 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
87 self.assertRaises(NotImplementedError, ne.dst, dt)
88
89 def test_normal(self):
90 fo = FixedOffset(3, "Three")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000091 self.assertTrue(isinstance(fo, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000092 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +000093 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +000094 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +000095 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +000096
97 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +000098 # There's no point to pickling tzinfo objects on their own (they
99 # carry no data), but they need to be picklable anyway else
100 # concrete subclasses can't be pickled.
101 orig = tzinfo.__new__(tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000102 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000103 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000104 green = pickler.dumps(orig, proto)
105 derived = unpickler.loads(green)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000106 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000107
108 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000109 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000110 offset = timedelta(minutes=-300)
111 orig = PicklableFixedOffset(offset, 'cookie')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000112 self.assertTrue(isinstance(orig, tzinfo))
113 self.assertTrue(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000114 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000115 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000116 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000117 green = pickler.dumps(orig, proto)
118 derived = unpickler.loads(green)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000119 self.assertTrue(isinstance(derived, tzinfo))
120 self.assertTrue(type(derived) is PicklableFixedOffset)
Tim Petersf2715e02003-02-19 02:35:07 +0000121 self.assertEqual(derived.utcoffset(None), offset)
122 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000123
124#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000125# Base clase for testing a particular aspect of timedelta, time, date and
126# datetime comparisons.
127
Guido van Rossumd8faa362007-04-27 19:54:29 +0000128class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000129 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
130
131 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
132 # legit constructor.
133
134 def test_harmless_mixed_comparison(self):
135 me = self.theclass(1, 1, 1)
136
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000137 self.assertFalse(me == ())
138 self.assertTrue(me != ())
139 self.assertFalse(() == me)
140 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000141
Benjamin 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
Mark Dickinson5c2db372009-12-05 20:28:34 +0000235 # Add/sub ints or floats should be illegal
236 for i in 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.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000253 zero = 0
254 self.assertRaises(TypeError, lambda: zero // a)
255 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Tim Peters2a799bf2002-12-16 20:18:38 +0000256
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
Mark Dickinson5c2db372009-12-05 20:28:34 +0000688 # Add/sub ints or floats should be illegal
689 for i in 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")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001181 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001182 # str is ISO format with the separator forced to a blank.
1183 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1184
1185 t = self.theclass(2, 3, 2)
1186 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1187 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1188 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1189 # str is ISO format with the separator forced to a blank.
1190 self.assertEqual(str(t), "0002-03-02 00:00:00")
1191
Eric Smith1ba31142007-09-11 18:06:02 +00001192 def test_format(self):
1193 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001194 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001195
1196 # check that a derived class's __str__() gets called
1197 class A(self.theclass):
1198 def __str__(self):
1199 return 'A'
1200 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001201 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001202
1203 # check that a derived class's strftime gets called
1204 class B(self.theclass):
1205 def strftime(self, format_spec):
1206 return 'B'
1207 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001208 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001209
1210 for fmt in ["m:%m d:%d y:%y",
1211 "m:%m d:%d y:%y H:%H M:%M S:%S",
1212 "%z %Z",
1213 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001214 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1215 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1216 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001217
Tim Peters2a799bf2002-12-16 20:18:38 +00001218 def test_more_ctime(self):
1219 # Test fields that TestDate doesn't touch.
1220 import time
1221
1222 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1223 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1224 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1225 # out. The difference is that t.ctime() produces " 2" for the day,
1226 # but platform ctime() produces "02" for the day. According to
1227 # C99, t.ctime() is correct here.
1228 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1229
1230 # So test a case where that difference doesn't matter.
1231 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1232 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1233
1234 def test_tz_independent_comparing(self):
1235 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1236 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1237 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1238 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001239 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001240
1241 # Make sure comparison doesn't forget microseconds, and isn't done
1242 # via comparing a float timestamp (an IEEE double doesn't have enough
1243 # precision to span microsecond resolution across years 1 thru 9999,
1244 # so comparing via timestamp necessarily calls some distinct values
1245 # equal).
1246 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1247 us = timedelta(microseconds=1)
1248 dt2 = dt1 + us
1249 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001250 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001251
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001252 def test_strftime_with_bad_tzname_replace(self):
1253 # verify ok if tzinfo.tzname().replace() returns a non-string
1254 class MyTzInfo(FixedOffset):
1255 def tzname(self, dt):
1256 class MyStr(str):
1257 def replace(self, *args):
1258 return None
1259 return MyStr('name')
1260 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1261 self.assertRaises(TypeError, t.strftime, '%Z')
1262
Tim Peters2a799bf2002-12-16 20:18:38 +00001263 def test_bad_constructor_arguments(self):
1264 # bad years
1265 self.theclass(MINYEAR, 1, 1) # no exception
1266 self.theclass(MAXYEAR, 1, 1) # no exception
1267 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1268 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1269 # bad months
1270 self.theclass(2000, 1, 1) # no exception
1271 self.theclass(2000, 12, 1) # no exception
1272 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1273 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1274 # bad days
1275 self.theclass(2000, 2, 29) # no exception
1276 self.theclass(2004, 2, 29) # no exception
1277 self.theclass(2400, 2, 29) # no exception
1278 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1279 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1280 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1281 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1282 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1283 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1284 # bad hours
1285 self.theclass(2000, 1, 31, 0) # no exception
1286 self.theclass(2000, 1, 31, 23) # no exception
1287 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1288 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1289 # bad minutes
1290 self.theclass(2000, 1, 31, 23, 0) # no exception
1291 self.theclass(2000, 1, 31, 23, 59) # no exception
1292 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1293 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1294 # bad seconds
1295 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1296 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1297 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1298 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1299 # bad microseconds
1300 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1301 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1302 self.assertRaises(ValueError, self.theclass,
1303 2000, 1, 31, 23, 59, 59, -1)
1304 self.assertRaises(ValueError, self.theclass,
1305 2000, 1, 31, 23, 59, 59,
1306 1000000)
1307
1308 def test_hash_equality(self):
1309 d = self.theclass(2000, 12, 31, 23, 30, 17)
1310 e = self.theclass(2000, 12, 31, 23, 30, 17)
1311 self.assertEqual(d, e)
1312 self.assertEqual(hash(d), hash(e))
1313
1314 dic = {d: 1}
1315 dic[e] = 2
1316 self.assertEqual(len(dic), 1)
1317 self.assertEqual(dic[d], 2)
1318 self.assertEqual(dic[e], 2)
1319
1320 d = self.theclass(2001, 1, 1, 0, 5, 17)
1321 e = self.theclass(2001, 1, 1, 0, 5, 17)
1322 self.assertEqual(d, e)
1323 self.assertEqual(hash(d), hash(e))
1324
1325 dic = {d: 1}
1326 dic[e] = 2
1327 self.assertEqual(len(dic), 1)
1328 self.assertEqual(dic[d], 2)
1329 self.assertEqual(dic[e], 2)
1330
1331 def test_computations(self):
1332 a = self.theclass(2002, 1, 31)
1333 b = self.theclass(1956, 1, 31)
1334 diff = a-b
1335 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1336 self.assertEqual(diff.seconds, 0)
1337 self.assertEqual(diff.microseconds, 0)
1338 a = self.theclass(2002, 3, 2, 17, 6)
1339 millisec = timedelta(0, 0, 1000)
1340 hour = timedelta(0, 3600)
1341 day = timedelta(1)
1342 week = timedelta(7)
1343 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1344 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1345 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1346 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1347 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1348 self.assertEqual(a - hour, a + -hour)
1349 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1350 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1351 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1352 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1353 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1354 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1355 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1356 self.assertEqual((a + week) - a, week)
1357 self.assertEqual((a + day) - a, day)
1358 self.assertEqual((a + hour) - a, hour)
1359 self.assertEqual((a + millisec) - a, millisec)
1360 self.assertEqual((a - week) - a, -week)
1361 self.assertEqual((a - day) - a, -day)
1362 self.assertEqual((a - hour) - a, -hour)
1363 self.assertEqual((a - millisec) - a, -millisec)
1364 self.assertEqual(a - (a + week), -week)
1365 self.assertEqual(a - (a + day), -day)
1366 self.assertEqual(a - (a + hour), -hour)
1367 self.assertEqual(a - (a + millisec), -millisec)
1368 self.assertEqual(a - (a - week), week)
1369 self.assertEqual(a - (a - day), day)
1370 self.assertEqual(a - (a - hour), hour)
1371 self.assertEqual(a - (a - millisec), millisec)
1372 self.assertEqual(a + (week + day + hour + millisec),
1373 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1374 self.assertEqual(a + (week + day + hour + millisec),
1375 (((a + week) + day) + hour) + millisec)
1376 self.assertEqual(a - (week + day + hour + millisec),
1377 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1378 self.assertEqual(a - (week + day + hour + millisec),
1379 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001380 # Add/sub ints or floats should be illegal
1381 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001382 self.assertRaises(TypeError, lambda: a+i)
1383 self.assertRaises(TypeError, lambda: a-i)
1384 self.assertRaises(TypeError, lambda: i+a)
1385 self.assertRaises(TypeError, lambda: i-a)
1386
1387 # delta - datetime is senseless.
1388 self.assertRaises(TypeError, lambda: day - a)
1389 # mixing datetime and (delta or datetime) via * or // is senseless
1390 self.assertRaises(TypeError, lambda: day * a)
1391 self.assertRaises(TypeError, lambda: a * day)
1392 self.assertRaises(TypeError, lambda: day // a)
1393 self.assertRaises(TypeError, lambda: a // day)
1394 self.assertRaises(TypeError, lambda: a * a)
1395 self.assertRaises(TypeError, lambda: a // a)
1396 # datetime + datetime is senseless
1397 self.assertRaises(TypeError, lambda: a + a)
1398
1399 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001400 args = 6, 7, 23, 20, 59, 1, 64**2
1401 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001402 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001403 green = pickler.dumps(orig, proto)
1404 derived = unpickler.loads(green)
1405 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001406
Guido van Rossum275666f2003-02-07 21:49:01 +00001407 def test_more_pickling(self):
1408 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1409 s = pickle.dumps(a)
1410 b = pickle.loads(s)
1411 self.assertEqual(b.year, 2003)
1412 self.assertEqual(b.month, 2)
1413 self.assertEqual(b.day, 7)
1414
Tim Peters604c0132004-06-07 23:04:33 +00001415 def test_pickling_subclass_datetime(self):
1416 args = 6, 7, 23, 20, 59, 1, 64**2
1417 orig = SubclassDatetime(*args)
1418 for pickler, unpickler, proto in pickle_choices:
1419 green = pickler.dumps(orig, proto)
1420 derived = unpickler.loads(green)
1421 self.assertEqual(orig, derived)
1422
Tim Peters2a799bf2002-12-16 20:18:38 +00001423 def test_more_compare(self):
1424 # The test_compare() inherited from TestDate covers the error cases.
1425 # We just want to test lexicographic ordering on the members datetime
1426 # has that date lacks.
1427 args = [2000, 11, 29, 20, 58, 16, 999998]
1428 t1 = self.theclass(*args)
1429 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001430 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001431 self.assertTrue(t1 <= t2)
1432 self.assertTrue(t1 >= t2)
1433 self.assertTrue(not t1 != t2)
1434 self.assertTrue(not t1 < t2)
1435 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001436
1437 for i in range(len(args)):
1438 newargs = args[:]
1439 newargs[i] = args[i] + 1
1440 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001441 self.assertTrue(t1 < t2)
1442 self.assertTrue(t2 > t1)
1443 self.assertTrue(t1 <= t2)
1444 self.assertTrue(t2 >= t1)
1445 self.assertTrue(t1 != t2)
1446 self.assertTrue(t2 != t1)
1447 self.assertTrue(not t1 == t2)
1448 self.assertTrue(not t2 == t1)
1449 self.assertTrue(not t1 > t2)
1450 self.assertTrue(not t2 < t1)
1451 self.assertTrue(not t1 >= t2)
1452 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001453
1454
1455 # A helper for timestamp constructor tests.
1456 def verify_field_equality(self, expected, got):
1457 self.assertEqual(expected.tm_year, got.year)
1458 self.assertEqual(expected.tm_mon, got.month)
1459 self.assertEqual(expected.tm_mday, got.day)
1460 self.assertEqual(expected.tm_hour, got.hour)
1461 self.assertEqual(expected.tm_min, got.minute)
1462 self.assertEqual(expected.tm_sec, got.second)
1463
1464 def test_fromtimestamp(self):
1465 import time
1466
1467 ts = time.time()
1468 expected = time.localtime(ts)
1469 got = self.theclass.fromtimestamp(ts)
1470 self.verify_field_equality(expected, got)
1471
1472 def test_utcfromtimestamp(self):
1473 import time
1474
1475 ts = time.time()
1476 expected = time.gmtime(ts)
1477 got = self.theclass.utcfromtimestamp(ts)
1478 self.verify_field_equality(expected, got)
1479
Thomas Wouters477c8d52006-05-27 19:21:47 +00001480 def test_microsecond_rounding(self):
1481 # Test whether fromtimestamp "rounds up" floats that are less
1482 # than one microsecond smaller than an integer.
1483 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1484 self.theclass.fromtimestamp(1))
1485
Tim Peters1b6f7a92004-06-20 02:50:16 +00001486 def test_insane_fromtimestamp(self):
1487 # It's possible that some platform maps time_t to double,
1488 # and that this test will fail there. This test should
1489 # exempt such platforms (provided they return reasonable
1490 # results!).
1491 for insane in -1e200, 1e200:
1492 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1493 insane)
1494
1495 def test_insane_utcfromtimestamp(self):
1496 # It's possible that some platform maps time_t to double,
1497 # and that this test will fail there. This test should
1498 # exempt such platforms (provided they return reasonable
1499 # results!).
1500 for insane in -1e200, 1e200:
1501 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1502 insane)
1503
Guido van Rossumd8faa362007-04-27 19:54:29 +00001504 def test_negative_float_fromtimestamp(self):
1505 # Windows doesn't accept negative timestamps
1506 if os.name == "nt":
1507 return
1508 # The result is tz-dependent; at least test that this doesn't
1509 # fail (like it did before bug 1646728 was fixed).
1510 self.theclass.fromtimestamp(-1.05)
1511
1512 def test_negative_float_utcfromtimestamp(self):
1513 # Windows doesn't accept negative timestamps
1514 if os.name == "nt":
1515 return
1516 d = self.theclass.utcfromtimestamp(-1.05)
1517 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1518
Tim Peters2a799bf2002-12-16 20:18:38 +00001519 def test_utcnow(self):
1520 import time
1521
1522 # Call it a success if utcnow() and utcfromtimestamp() are within
1523 # a second of each other.
1524 tolerance = timedelta(seconds=1)
1525 for dummy in range(3):
1526 from_now = self.theclass.utcnow()
1527 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1528 if abs(from_timestamp - from_now) <= tolerance:
1529 break
1530 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001531 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001532
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001533 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001534 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001535
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001536 string = '2004-12-01 13:02:47.197'
1537 format = '%Y-%m-%d %H:%M:%S.%f'
1538 result, frac = _strptime._strptime(string, format)
1539 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001540 got = self.theclass.strptime(string, format)
1541 self.assertEqual(expected, got)
1542
Tim Peters2a799bf2002-12-16 20:18:38 +00001543 def test_more_timetuple(self):
1544 # This tests fields beyond those tested by the TestDate.test_timetuple.
1545 t = self.theclass(2004, 12, 31, 6, 22, 33)
1546 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1547 self.assertEqual(t.timetuple(),
1548 (t.year, t.month, t.day,
1549 t.hour, t.minute, t.second,
1550 t.weekday(),
1551 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1552 -1))
1553 tt = t.timetuple()
1554 self.assertEqual(tt.tm_year, t.year)
1555 self.assertEqual(tt.tm_mon, t.month)
1556 self.assertEqual(tt.tm_mday, t.day)
1557 self.assertEqual(tt.tm_hour, t.hour)
1558 self.assertEqual(tt.tm_min, t.minute)
1559 self.assertEqual(tt.tm_sec, t.second)
1560 self.assertEqual(tt.tm_wday, t.weekday())
1561 self.assertEqual(tt.tm_yday, t.toordinal() -
1562 date(t.year, 1, 1).toordinal() + 1)
1563 self.assertEqual(tt.tm_isdst, -1)
1564
1565 def test_more_strftime(self):
1566 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001567 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1568 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1569 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001570
1571 def test_extract(self):
1572 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1573 self.assertEqual(dt.date(), date(2002, 3, 4))
1574 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1575
1576 def test_combine(self):
1577 d = date(2002, 3, 4)
1578 t = time(18, 45, 3, 1234)
1579 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1580 combine = self.theclass.combine
1581 dt = combine(d, t)
1582 self.assertEqual(dt, expected)
1583
1584 dt = combine(time=t, date=d)
1585 self.assertEqual(dt, expected)
1586
1587 self.assertEqual(d, dt.date())
1588 self.assertEqual(t, dt.time())
1589 self.assertEqual(dt, combine(dt.date(), dt.time()))
1590
1591 self.assertRaises(TypeError, combine) # need an arg
1592 self.assertRaises(TypeError, combine, d) # need two args
1593 self.assertRaises(TypeError, combine, t, d) # args reversed
1594 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1595 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1596
Tim Peters12bf3392002-12-24 05:41:27 +00001597 def test_replace(self):
1598 cls = self.theclass
1599 args = [1, 2, 3, 4, 5, 6, 7]
1600 base = cls(*args)
1601 self.assertEqual(base, base.replace())
1602
1603 i = 0
1604 for name, newval in (("year", 2),
1605 ("month", 3),
1606 ("day", 4),
1607 ("hour", 5),
1608 ("minute", 6),
1609 ("second", 7),
1610 ("microsecond", 8)):
1611 newargs = args[:]
1612 newargs[i] = newval
1613 expected = cls(*newargs)
1614 got = base.replace(**{name: newval})
1615 self.assertEqual(expected, got)
1616 i += 1
1617
1618 # Out of bounds.
1619 base = cls(2000, 2, 29)
1620 self.assertRaises(ValueError, base.replace, year=2001)
1621
Tim Peters80475bb2002-12-25 07:40:55 +00001622 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001623 # Pretty boring! The TZ test is more interesting here. astimezone()
1624 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001625 dt = self.theclass.now()
1626 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001627 self.assertRaises(TypeError, dt.astimezone) # not enough args
1628 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1629 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001630 self.assertRaises(ValueError, dt.astimezone, f) # naive
1631 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001632
Tim Peters52dcce22003-01-23 16:36:11 +00001633 class Bogus(tzinfo):
1634 def utcoffset(self, dt): return None
1635 def dst(self, dt): return timedelta(0)
1636 bog = Bogus()
1637 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1638
1639 class AlsoBogus(tzinfo):
1640 def utcoffset(self, dt): return timedelta(0)
1641 def dst(self, dt): return None
1642 alsobog = AlsoBogus()
1643 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001644
Tim Petersa98924a2003-05-17 05:55:19 +00001645 def test_subclass_datetime(self):
1646
1647 class C(self.theclass):
1648 theAnswer = 42
1649
1650 def __new__(cls, *args, **kws):
1651 temp = kws.copy()
1652 extra = temp.pop('extra')
1653 result = self.theclass.__new__(cls, *args, **temp)
1654 result.extra = extra
1655 return result
1656
1657 def newmeth(self, start):
1658 return start + self.year + self.month + self.second
1659
1660 args = 2003, 4, 14, 12, 13, 41
1661
1662 dt1 = self.theclass(*args)
1663 dt2 = C(*args, **{'extra': 7})
1664
1665 self.assertEqual(dt2.__class__, C)
1666 self.assertEqual(dt2.theAnswer, 42)
1667 self.assertEqual(dt2.extra, 7)
1668 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1669 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1670 dt1.second - 7)
1671
Tim Peters604c0132004-06-07 23:04:33 +00001672class SubclassTime(time):
1673 sub_var = 1
1674
Guido van Rossumd8faa362007-04-27 19:54:29 +00001675class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001676
1677 theclass = time
1678
1679 def test_basic_attributes(self):
1680 t = self.theclass(12, 0)
1681 self.assertEqual(t.hour, 12)
1682 self.assertEqual(t.minute, 0)
1683 self.assertEqual(t.second, 0)
1684 self.assertEqual(t.microsecond, 0)
1685
1686 def test_basic_attributes_nonzero(self):
1687 # Make sure all attributes are non-zero so bugs in
1688 # bit-shifting access show up.
1689 t = self.theclass(12, 59, 59, 8000)
1690 self.assertEqual(t.hour, 12)
1691 self.assertEqual(t.minute, 59)
1692 self.assertEqual(t.second, 59)
1693 self.assertEqual(t.microsecond, 8000)
1694
1695 def test_roundtrip(self):
1696 t = self.theclass(1, 2, 3, 4)
1697
1698 # Verify t -> string -> time identity.
1699 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001700 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001701 s = s[9:]
1702 t2 = eval(s)
1703 self.assertEqual(t, t2)
1704
1705 # Verify identity via reconstructing from pieces.
1706 t2 = self.theclass(t.hour, t.minute, t.second,
1707 t.microsecond)
1708 self.assertEqual(t, t2)
1709
1710 def test_comparing(self):
1711 args = [1, 2, 3, 4]
1712 t1 = self.theclass(*args)
1713 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001714 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001715 self.assertTrue(t1 <= t2)
1716 self.assertTrue(t1 >= t2)
1717 self.assertTrue(not t1 != t2)
1718 self.assertTrue(not t1 < t2)
1719 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001720
1721 for i in range(len(args)):
1722 newargs = args[:]
1723 newargs[i] = args[i] + 1
1724 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001725 self.assertTrue(t1 < t2)
1726 self.assertTrue(t2 > t1)
1727 self.assertTrue(t1 <= t2)
1728 self.assertTrue(t2 >= t1)
1729 self.assertTrue(t1 != t2)
1730 self.assertTrue(t2 != t1)
1731 self.assertTrue(not t1 == t2)
1732 self.assertTrue(not t2 == t1)
1733 self.assertTrue(not t1 > t2)
1734 self.assertTrue(not t2 < t1)
1735 self.assertTrue(not t1 >= t2)
1736 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001737
Tim Peters68124bb2003-02-08 03:46:31 +00001738 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001739 self.assertEqual(t1 == badarg, False)
1740 self.assertEqual(t1 != badarg, True)
1741 self.assertEqual(badarg == t1, False)
1742 self.assertEqual(badarg != t1, True)
1743
Tim Peters2a799bf2002-12-16 20:18:38 +00001744 self.assertRaises(TypeError, lambda: t1 <= badarg)
1745 self.assertRaises(TypeError, lambda: t1 < badarg)
1746 self.assertRaises(TypeError, lambda: t1 > badarg)
1747 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001748 self.assertRaises(TypeError, lambda: badarg <= t1)
1749 self.assertRaises(TypeError, lambda: badarg < t1)
1750 self.assertRaises(TypeError, lambda: badarg > t1)
1751 self.assertRaises(TypeError, lambda: badarg >= t1)
1752
1753 def test_bad_constructor_arguments(self):
1754 # bad hours
1755 self.theclass(0, 0) # no exception
1756 self.theclass(23, 0) # no exception
1757 self.assertRaises(ValueError, self.theclass, -1, 0)
1758 self.assertRaises(ValueError, self.theclass, 24, 0)
1759 # bad minutes
1760 self.theclass(23, 0) # no exception
1761 self.theclass(23, 59) # no exception
1762 self.assertRaises(ValueError, self.theclass, 23, -1)
1763 self.assertRaises(ValueError, self.theclass, 23, 60)
1764 # bad seconds
1765 self.theclass(23, 59, 0) # no exception
1766 self.theclass(23, 59, 59) # no exception
1767 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1768 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1769 # bad microseconds
1770 self.theclass(23, 59, 59, 0) # no exception
1771 self.theclass(23, 59, 59, 999999) # no exception
1772 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1773 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1774
1775 def test_hash_equality(self):
1776 d = self.theclass(23, 30, 17)
1777 e = self.theclass(23, 30, 17)
1778 self.assertEqual(d, e)
1779 self.assertEqual(hash(d), hash(e))
1780
1781 dic = {d: 1}
1782 dic[e] = 2
1783 self.assertEqual(len(dic), 1)
1784 self.assertEqual(dic[d], 2)
1785 self.assertEqual(dic[e], 2)
1786
1787 d = self.theclass(0, 5, 17)
1788 e = self.theclass(0, 5, 17)
1789 self.assertEqual(d, e)
1790 self.assertEqual(hash(d), hash(e))
1791
1792 dic = {d: 1}
1793 dic[e] = 2
1794 self.assertEqual(len(dic), 1)
1795 self.assertEqual(dic[d], 2)
1796 self.assertEqual(dic[e], 2)
1797
1798 def test_isoformat(self):
1799 t = self.theclass(4, 5, 1, 123)
1800 self.assertEqual(t.isoformat(), "04:05:01.000123")
1801 self.assertEqual(t.isoformat(), str(t))
1802
1803 t = self.theclass()
1804 self.assertEqual(t.isoformat(), "00:00:00")
1805 self.assertEqual(t.isoformat(), str(t))
1806
1807 t = self.theclass(microsecond=1)
1808 self.assertEqual(t.isoformat(), "00:00:00.000001")
1809 self.assertEqual(t.isoformat(), str(t))
1810
1811 t = self.theclass(microsecond=10)
1812 self.assertEqual(t.isoformat(), "00:00:00.000010")
1813 self.assertEqual(t.isoformat(), str(t))
1814
1815 t = self.theclass(microsecond=100)
1816 self.assertEqual(t.isoformat(), "00:00:00.000100")
1817 self.assertEqual(t.isoformat(), str(t))
1818
1819 t = self.theclass(microsecond=1000)
1820 self.assertEqual(t.isoformat(), "00:00:00.001000")
1821 self.assertEqual(t.isoformat(), str(t))
1822
1823 t = self.theclass(microsecond=10000)
1824 self.assertEqual(t.isoformat(), "00:00:00.010000")
1825 self.assertEqual(t.isoformat(), str(t))
1826
1827 t = self.theclass(microsecond=100000)
1828 self.assertEqual(t.isoformat(), "00:00:00.100000")
1829 self.assertEqual(t.isoformat(), str(t))
1830
Thomas Wouterscf297e42007-02-23 15:07:44 +00001831 def test_1653736(self):
1832 # verify it doesn't accept extra keyword arguments
1833 t = self.theclass(second=1)
1834 self.assertRaises(TypeError, t.isoformat, foo=3)
1835
Tim Peters2a799bf2002-12-16 20:18:38 +00001836 def test_strftime(self):
1837 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001838 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001839 # A naive object replaces %z and %Z with empty strings.
1840 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1841
Eric Smith1ba31142007-09-11 18:06:02 +00001842 def test_format(self):
1843 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001844 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001845
1846 # check that a derived class's __str__() gets called
1847 class A(self.theclass):
1848 def __str__(self):
1849 return 'A'
1850 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001851 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001852
1853 # check that a derived class's strftime gets called
1854 class B(self.theclass):
1855 def strftime(self, format_spec):
1856 return 'B'
1857 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001858 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001859
1860 for fmt in ['%H %M %S',
1861 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001862 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1863 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1864 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001865
Tim Peters2a799bf2002-12-16 20:18:38 +00001866 def test_str(self):
1867 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1868 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1869 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1870 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1871 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1872
1873 def test_repr(self):
1874 name = 'datetime.' + self.theclass.__name__
1875 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1876 "%s(1, 2, 3, 4)" % name)
1877 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1878 "%s(10, 2, 3, 4000)" % name)
1879 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1880 "%s(0, 2, 3, 400000)" % name)
1881 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1882 "%s(12, 2, 3)" % name)
1883 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1884 "%s(23, 15)" % name)
1885
1886 def test_resolution_info(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001887 self.assertTrue(isinstance(self.theclass.min, self.theclass))
1888 self.assertTrue(isinstance(self.theclass.max, self.theclass))
1889 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
1890 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001891
1892 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001893 args = 20, 59, 16, 64**2
1894 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001895 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001896 green = pickler.dumps(orig, proto)
1897 derived = unpickler.loads(green)
1898 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001899
Tim Peters604c0132004-06-07 23:04:33 +00001900 def test_pickling_subclass_time(self):
1901 args = 20, 59, 16, 64**2
1902 orig = SubclassTime(*args)
1903 for pickler, unpickler, proto in pickle_choices:
1904 green = pickler.dumps(orig, proto)
1905 derived = unpickler.loads(green)
1906 self.assertEqual(orig, derived)
1907
Tim Peters2a799bf2002-12-16 20:18:38 +00001908 def test_bool(self):
1909 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001910 self.assertTrue(cls(1))
1911 self.assertTrue(cls(0, 1))
1912 self.assertTrue(cls(0, 0, 1))
1913 self.assertTrue(cls(0, 0, 0, 1))
1914 self.assertTrue(not cls(0))
1915 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001916
Tim Peters12bf3392002-12-24 05:41:27 +00001917 def test_replace(self):
1918 cls = self.theclass
1919 args = [1, 2, 3, 4]
1920 base = cls(*args)
1921 self.assertEqual(base, base.replace())
1922
1923 i = 0
1924 for name, newval in (("hour", 5),
1925 ("minute", 6),
1926 ("second", 7),
1927 ("microsecond", 8)):
1928 newargs = args[:]
1929 newargs[i] = newval
1930 expected = cls(*newargs)
1931 got = base.replace(**{name: newval})
1932 self.assertEqual(expected, got)
1933 i += 1
1934
1935 # Out of bounds.
1936 base = cls(1)
1937 self.assertRaises(ValueError, base.replace, hour=24)
1938 self.assertRaises(ValueError, base.replace, minute=-1)
1939 self.assertRaises(ValueError, base.replace, second=100)
1940 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1941
Tim Petersa98924a2003-05-17 05:55:19 +00001942 def test_subclass_time(self):
1943
1944 class C(self.theclass):
1945 theAnswer = 42
1946
1947 def __new__(cls, *args, **kws):
1948 temp = kws.copy()
1949 extra = temp.pop('extra')
1950 result = self.theclass.__new__(cls, *args, **temp)
1951 result.extra = extra
1952 return result
1953
1954 def newmeth(self, start):
1955 return start + self.hour + self.second
1956
1957 args = 4, 5, 6
1958
1959 dt1 = self.theclass(*args)
1960 dt2 = C(*args, **{'extra': 7})
1961
1962 self.assertEqual(dt2.__class__, C)
1963 self.assertEqual(dt2.theAnswer, 42)
1964 self.assertEqual(dt2.extra, 7)
1965 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1966 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1967
Armin Rigof4afb212005-11-07 07:15:48 +00001968 def test_backdoor_resistance(self):
1969 # see TestDate.test_backdoor_resistance().
1970 base = '2:59.0'
1971 for hour_byte in ' ', '9', chr(24), '\xff':
1972 self.assertRaises(TypeError, self.theclass,
1973 hour_byte + base[1:])
1974
Tim Peters855fe882002-12-22 03:43:39 +00001975# A mixin for classes with a tzinfo= argument. Subclasses must define
1976# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001977# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001978class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001979
Tim Petersbad8ff02002-12-30 20:52:32 +00001980 def test_argument_passing(self):
1981 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001982 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001983 class introspective(tzinfo):
1984 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001985 def utcoffset(self, dt):
1986 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001987 dst = utcoffset
1988
1989 obj = cls(1, 2, 3, tzinfo=introspective())
1990
Tim Peters0bf60bd2003-01-08 20:40:01 +00001991 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001992 self.assertEqual(obj.tzname(), expected)
1993
Tim Peters0bf60bd2003-01-08 20:40:01 +00001994 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001995 self.assertEqual(obj.utcoffset(), expected)
1996 self.assertEqual(obj.dst(), expected)
1997
Tim Peters855fe882002-12-22 03:43:39 +00001998 def test_bad_tzinfo_classes(self):
1999 cls = self.theclass
2000 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002001
Tim Peters855fe882002-12-22 03:43:39 +00002002 class NiceTry(object):
2003 def __init__(self): pass
2004 def utcoffset(self, dt): pass
2005 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2006
2007 class BetterTry(tzinfo):
2008 def __init__(self): pass
2009 def utcoffset(self, dt): pass
2010 b = BetterTry()
2011 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002012 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002013
2014 def test_utc_offset_out_of_bounds(self):
2015 class Edgy(tzinfo):
2016 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002017 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002018 def utcoffset(self, dt):
2019 return self.offset
2020
2021 cls = self.theclass
2022 for offset, legit in ((-1440, False),
2023 (-1439, True),
2024 (1439, True),
2025 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002026 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002027 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002028 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002029 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002030 else:
2031 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002032 if legit:
2033 aofs = abs(offset)
2034 h, m = divmod(aofs, 60)
2035 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002036 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002037 t = t.timetz()
2038 self.assertEqual(str(t), "01:02:03" + tag)
2039 else:
2040 self.assertRaises(ValueError, str, t)
2041
2042 def test_tzinfo_classes(self):
2043 cls = self.theclass
2044 class C1(tzinfo):
2045 def utcoffset(self, dt): return None
2046 def dst(self, dt): return None
2047 def tzname(self, dt): return None
2048 for t in (cls(1, 1, 1),
2049 cls(1, 1, 1, tzinfo=None),
2050 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002051 self.assertTrue(t.utcoffset() is None)
2052 self.assertTrue(t.dst() is None)
2053 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002054
Tim Peters855fe882002-12-22 03:43:39 +00002055 class C3(tzinfo):
2056 def utcoffset(self, dt): return timedelta(minutes=-1439)
2057 def dst(self, dt): return timedelta(minutes=1439)
2058 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002059 t = cls(1, 1, 1, tzinfo=C3())
2060 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2061 self.assertEqual(t.dst(), timedelta(minutes=1439))
2062 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002063
2064 # Wrong types.
2065 class C4(tzinfo):
2066 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002067 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002068 def tzname(self, dt): return 0
2069 t = cls(1, 1, 1, tzinfo=C4())
2070 self.assertRaises(TypeError, t.utcoffset)
2071 self.assertRaises(TypeError, t.dst)
2072 self.assertRaises(TypeError, t.tzname)
2073
2074 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002075 class C6(tzinfo):
2076 def utcoffset(self, dt): return timedelta(hours=-24)
2077 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002078 t = cls(1, 1, 1, tzinfo=C6())
2079 self.assertRaises(ValueError, t.utcoffset)
2080 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002081
2082 # Not a whole number of minutes.
2083 class C7(tzinfo):
2084 def utcoffset(self, dt): return timedelta(seconds=61)
2085 def dst(self, dt): return timedelta(microseconds=-81)
2086 t = cls(1, 1, 1, tzinfo=C7())
2087 self.assertRaises(ValueError, t.utcoffset)
2088 self.assertRaises(ValueError, t.dst)
2089
Tim Peters4c0db782002-12-26 05:01:19 +00002090 def test_aware_compare(self):
2091 cls = self.theclass
2092
Tim Peters60c76e42002-12-27 00:41:11 +00002093 # Ensure that utcoffset() gets ignored if the comparands have
2094 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002095 class OperandDependentOffset(tzinfo):
2096 def utcoffset(self, t):
2097 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002098 # d0 and d1 equal after adjustment
2099 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002100 else:
Tim Peters397301e2003-01-02 21:28:08 +00002101 # d2 off in the weeds
2102 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002103
2104 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2105 d0 = base.replace(minute=3)
2106 d1 = base.replace(minute=9)
2107 d2 = base.replace(minute=11)
2108 for x in d0, d1, d2:
2109 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002110 for op in lt, le, gt, ge, eq, ne:
2111 got = op(x, y)
2112 expected = op(x.minute, y.minute)
2113 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002114
2115 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002116 # Note that a time can't actually have an operand-depedent offset,
2117 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2118 # so skip this test for time.
2119 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002120 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2121 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2122 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2123 for x in d0, d1, d2:
2124 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002125 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002126 if (x is d0 or x is d1) and (y is d0 or y is d1):
2127 expected = 0
2128 elif x is y is d2:
2129 expected = 0
2130 elif x is d2:
2131 expected = -1
2132 else:
2133 assert y is d2
2134 expected = 1
2135 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002136
Tim Peters855fe882002-12-22 03:43:39 +00002137
Tim Peters0bf60bd2003-01-08 20:40:01 +00002138# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002139class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002140 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002141
2142 def test_empty(self):
2143 t = self.theclass()
2144 self.assertEqual(t.hour, 0)
2145 self.assertEqual(t.minute, 0)
2146 self.assertEqual(t.second, 0)
2147 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002148 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002149
Tim Peters2a799bf2002-12-16 20:18:38 +00002150 def test_zones(self):
2151 est = FixedOffset(-300, "EST", 1)
2152 utc = FixedOffset(0, "UTC", -2)
2153 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002154 t1 = time( 7, 47, tzinfo=est)
2155 t2 = time(12, 47, tzinfo=utc)
2156 t3 = time(13, 47, tzinfo=met)
2157 t4 = time(microsecond=40)
2158 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002159
2160 self.assertEqual(t1.tzinfo, est)
2161 self.assertEqual(t2.tzinfo, utc)
2162 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002163 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002164 self.assertEqual(t5.tzinfo, utc)
2165
Tim Peters855fe882002-12-22 03:43:39 +00002166 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2167 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2168 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002169 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002170 self.assertRaises(TypeError, t1.utcoffset, "no args")
2171
2172 self.assertEqual(t1.tzname(), "EST")
2173 self.assertEqual(t2.tzname(), "UTC")
2174 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002175 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002176 self.assertRaises(TypeError, t1.tzname, "no args")
2177
Tim Peters855fe882002-12-22 03:43:39 +00002178 self.assertEqual(t1.dst(), timedelta(minutes=1))
2179 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2180 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002181 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002182 self.assertRaises(TypeError, t1.dst, "no args")
2183
2184 self.assertEqual(hash(t1), hash(t2))
2185 self.assertEqual(hash(t1), hash(t3))
2186 self.assertEqual(hash(t2), hash(t3))
2187
2188 self.assertEqual(t1, t2)
2189 self.assertEqual(t1, t3)
2190 self.assertEqual(t2, t3)
2191 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2192 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2193 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2194
2195 self.assertEqual(str(t1), "07:47:00-05:00")
2196 self.assertEqual(str(t2), "12:47:00+00:00")
2197 self.assertEqual(str(t3), "13:47:00+01:00")
2198 self.assertEqual(str(t4), "00:00:00.000040")
2199 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2200
2201 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2202 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2203 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2204 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2205 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2206
Tim Peters0bf60bd2003-01-08 20:40:01 +00002207 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002208 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2209 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2210 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2211 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2212 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2213
2214 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2215 "07:47:00 %Z=EST %z=-0500")
2216 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2217 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2218
2219 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002220 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002221 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2222 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2223
Tim Petersb92bb712002-12-21 17:44:07 +00002224 # Check that an invalid tzname result raises an exception.
2225 class Badtzname(tzinfo):
2226 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002227 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002228 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2229 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002230
2231 def test_hash_edge_cases(self):
2232 # Offsets that overflow a basic time.
2233 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2234 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2235 self.assertEqual(hash(t1), hash(t2))
2236
2237 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2238 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2239 self.assertEqual(hash(t1), hash(t2))
2240
Tim Peters2a799bf2002-12-16 20:18:38 +00002241 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002242 # Try one without a tzinfo.
2243 args = 20, 59, 16, 64**2
2244 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002245 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002246 green = pickler.dumps(orig, proto)
2247 derived = unpickler.loads(green)
2248 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002249
2250 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002251 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002252 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002253 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002254 green = pickler.dumps(orig, proto)
2255 derived = unpickler.loads(green)
2256 self.assertEqual(orig, derived)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002257 self.assertTrue(isinstance(derived.tzinfo, PicklableFixedOffset))
Tim Peters96940c92003-01-31 21:55:33 +00002258 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2259 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002260
2261 def test_more_bool(self):
2262 # Test cases with non-None tzinfo.
2263 cls = self.theclass
2264
2265 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002266 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002267
2268 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002269 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002270
2271 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002272 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002273
2274 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002275 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002276
2277 # Mostly ensuring this doesn't overflow internally.
2278 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002279 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002280
2281 # But this should yield a value error -- the utcoffset is bogus.
2282 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2283 self.assertRaises(ValueError, lambda: bool(t))
2284
2285 # Likewise.
2286 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2287 self.assertRaises(ValueError, lambda: bool(t))
2288
Tim Peters12bf3392002-12-24 05:41:27 +00002289 def test_replace(self):
2290 cls = self.theclass
2291 z100 = FixedOffset(100, "+100")
2292 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2293 args = [1, 2, 3, 4, z100]
2294 base = cls(*args)
2295 self.assertEqual(base, base.replace())
2296
2297 i = 0
2298 for name, newval in (("hour", 5),
2299 ("minute", 6),
2300 ("second", 7),
2301 ("microsecond", 8),
2302 ("tzinfo", zm200)):
2303 newargs = args[:]
2304 newargs[i] = newval
2305 expected = cls(*newargs)
2306 got = base.replace(**{name: newval})
2307 self.assertEqual(expected, got)
2308 i += 1
2309
2310 # Ensure we can get rid of a tzinfo.
2311 self.assertEqual(base.tzname(), "+100")
2312 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002313 self.assertTrue(base2.tzinfo is None)
2314 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002315
2316 # Ensure we can add one.
2317 base3 = base2.replace(tzinfo=z100)
2318 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002319 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002320
2321 # Out of bounds.
2322 base = cls(1)
2323 self.assertRaises(ValueError, base.replace, hour=24)
2324 self.assertRaises(ValueError, base.replace, minute=-1)
2325 self.assertRaises(ValueError, base.replace, second=100)
2326 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2327
Tim Peters60c76e42002-12-27 00:41:11 +00002328 def test_mixed_compare(self):
2329 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002330 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002331 self.assertEqual(t1, t2)
2332 t2 = t2.replace(tzinfo=None)
2333 self.assertEqual(t1, t2)
2334 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2335 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002336 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2337 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002338
Tim Peters0bf60bd2003-01-08 20:40:01 +00002339 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002340 class Varies(tzinfo):
2341 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002342 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002343 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002344 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002345 return self.offset
2346
2347 v = Varies()
2348 t1 = t2.replace(tzinfo=v)
2349 t2 = t2.replace(tzinfo=v)
2350 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2351 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2352 self.assertEqual(t1, t2)
2353
2354 # But if they're not identical, it isn't ignored.
2355 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002356 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002357
Tim Petersa98924a2003-05-17 05:55:19 +00002358 def test_subclass_timetz(self):
2359
2360 class C(self.theclass):
2361 theAnswer = 42
2362
2363 def __new__(cls, *args, **kws):
2364 temp = kws.copy()
2365 extra = temp.pop('extra')
2366 result = self.theclass.__new__(cls, *args, **temp)
2367 result.extra = extra
2368 return result
2369
2370 def newmeth(self, start):
2371 return start + self.hour + self.second
2372
2373 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2374
2375 dt1 = self.theclass(*args)
2376 dt2 = C(*args, **{'extra': 7})
2377
2378 self.assertEqual(dt2.__class__, C)
2379 self.assertEqual(dt2.theAnswer, 42)
2380 self.assertEqual(dt2.extra, 7)
2381 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2382 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2383
Tim Peters4c0db782002-12-26 05:01:19 +00002384
Tim Peters0bf60bd2003-01-08 20:40:01 +00002385# Testing datetime objects with a non-None tzinfo.
2386
Guido van Rossumd8faa362007-04-27 19:54:29 +00002387class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002388 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002389
2390 def test_trivial(self):
2391 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2392 self.assertEqual(dt.year, 1)
2393 self.assertEqual(dt.month, 2)
2394 self.assertEqual(dt.day, 3)
2395 self.assertEqual(dt.hour, 4)
2396 self.assertEqual(dt.minute, 5)
2397 self.assertEqual(dt.second, 6)
2398 self.assertEqual(dt.microsecond, 7)
2399 self.assertEqual(dt.tzinfo, None)
2400
2401 def test_even_more_compare(self):
2402 # The test_compare() and test_more_compare() inherited from TestDate
2403 # and TestDateTime covered non-tzinfo cases.
2404
2405 # Smallest possible after UTC adjustment.
2406 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2407 # Largest possible after UTC adjustment.
2408 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2409 tzinfo=FixedOffset(-1439, ""))
2410
2411 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002412 self.assertTrue(t1 < t2)
2413 self.assertTrue(t1 != t2)
2414 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002415
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002416 self.assertEqual(t1, t1)
2417 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002418
2419 # Equal afer adjustment.
2420 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2421 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2422 self.assertEqual(t1, t2)
2423
2424 # Change t1 not to subtract a minute, and t1 should be larger.
2425 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002426 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002427
2428 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2429 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002430 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002431
2432 # Back to the original t1, but make seconds resolve it.
2433 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2434 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002435 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002436
2437 # Likewise, but make microseconds resolve it.
2438 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2439 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002440 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002441
2442 # Make t2 naive and it should fail.
2443 t2 = self.theclass.min
2444 self.assertRaises(TypeError, lambda: t1 == t2)
2445 self.assertEqual(t2, t2)
2446
2447 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2448 class Naive(tzinfo):
2449 def utcoffset(self, dt): return None
2450 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2451 self.assertRaises(TypeError, lambda: t1 == t2)
2452 self.assertEqual(t2, t2)
2453
2454 # OTOH, it's OK to compare two of these mixing the two ways of being
2455 # naive.
2456 t1 = self.theclass(5, 6, 7)
2457 self.assertEqual(t1, t2)
2458
2459 # Try a bogus uctoffset.
2460 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002461 def utcoffset(self, dt):
2462 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002463 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2464 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002465 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002466
Tim Peters2a799bf2002-12-16 20:18:38 +00002467 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002468 # Try one without a tzinfo.
2469 args = 6, 7, 23, 20, 59, 1, 64**2
2470 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002471 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002472 green = pickler.dumps(orig, proto)
2473 derived = unpickler.loads(green)
2474 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002475
2476 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002477 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002478 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002479 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002480 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002481 green = pickler.dumps(orig, proto)
2482 derived = unpickler.loads(green)
2483 self.assertEqual(orig, derived)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002484 self.assertTrue(isinstance(derived.tzinfo,
Tim Peters96940c92003-01-31 21:55:33 +00002485 PicklableFixedOffset))
2486 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2487 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002488
2489 def test_extreme_hashes(self):
2490 # If an attempt is made to hash these via subtracting the offset
2491 # then hashing a datetime object, OverflowError results. The
2492 # Python implementation used to blow up here.
2493 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2494 hash(t)
2495 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2496 tzinfo=FixedOffset(-1439, ""))
2497 hash(t)
2498
2499 # OTOH, an OOB offset should blow up.
2500 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2501 self.assertRaises(ValueError, hash, t)
2502
2503 def test_zones(self):
2504 est = FixedOffset(-300, "EST")
2505 utc = FixedOffset(0, "UTC")
2506 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002507 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2508 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2509 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002510 self.assertEqual(t1.tzinfo, est)
2511 self.assertEqual(t2.tzinfo, utc)
2512 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002513 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2514 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2515 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002516 self.assertEqual(t1.tzname(), "EST")
2517 self.assertEqual(t2.tzname(), "UTC")
2518 self.assertEqual(t3.tzname(), "MET")
2519 self.assertEqual(hash(t1), hash(t2))
2520 self.assertEqual(hash(t1), hash(t3))
2521 self.assertEqual(hash(t2), hash(t3))
2522 self.assertEqual(t1, t2)
2523 self.assertEqual(t1, t3)
2524 self.assertEqual(t2, t3)
2525 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2526 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2527 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002528 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002529 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2530 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2531 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2532
2533 def test_combine(self):
2534 met = FixedOffset(60, "MET")
2535 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002536 tz = time(18, 45, 3, 1234, tzinfo=met)
2537 dt = datetime.combine(d, tz)
2538 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002539 tzinfo=met))
2540
2541 def test_extract(self):
2542 met = FixedOffset(60, "MET")
2543 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2544 self.assertEqual(dt.date(), date(2002, 3, 4))
2545 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002546 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002547
2548 def test_tz_aware_arithmetic(self):
2549 import random
2550
2551 now = self.theclass.now()
2552 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002553 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002554 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002555 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002556 self.assertEqual(nowaware.timetz(), timeaware)
2557
2558 # Can't mix aware and non-aware.
2559 self.assertRaises(TypeError, lambda: now - nowaware)
2560 self.assertRaises(TypeError, lambda: nowaware - now)
2561
Tim Peters0bf60bd2003-01-08 20:40:01 +00002562 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002563 self.assertRaises(TypeError, lambda: now + nowaware)
2564 self.assertRaises(TypeError, lambda: nowaware + now)
2565 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2566
2567 # Subtracting should yield 0.
2568 self.assertEqual(now - now, timedelta(0))
2569 self.assertEqual(nowaware - nowaware, timedelta(0))
2570
2571 # Adding a delta should preserve tzinfo.
2572 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2573 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002574 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002575 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002576 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002577 self.assertEqual(nowawareplus, nowawareplus2)
2578
2579 # that - delta should be what we started with, and that - what we
2580 # started with should be delta.
2581 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002582 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002583 self.assertEqual(nowaware, diff)
2584 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2585 self.assertEqual(nowawareplus - nowaware, delta)
2586
2587 # Make up a random timezone.
2588 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002589 # Attach it to nowawareplus.
2590 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002591 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002592 # Make sure the difference takes the timezone adjustments into account.
2593 got = nowaware - nowawareplus
2594 # Expected: (nowaware base - nowaware offset) -
2595 # (nowawareplus base - nowawareplus offset) =
2596 # (nowaware base - nowawareplus base) +
2597 # (nowawareplus offset - nowaware offset) =
2598 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002599 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002600 self.assertEqual(got, expected)
2601
2602 # Try max possible difference.
2603 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2604 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2605 tzinfo=FixedOffset(-1439, "max"))
2606 maxdiff = max - min
2607 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2608 timedelta(minutes=2*1439))
2609
2610 def test_tzinfo_now(self):
2611 meth = self.theclass.now
2612 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2613 base = meth()
2614 # Try with and without naming the keyword.
2615 off42 = FixedOffset(42, "42")
2616 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002617 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002618 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002619 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002620 # Bad argument with and w/o naming the keyword.
2621 self.assertRaises(TypeError, meth, 16)
2622 self.assertRaises(TypeError, meth, tzinfo=16)
2623 # Bad keyword name.
2624 self.assertRaises(TypeError, meth, tinfo=off42)
2625 # Too many args.
2626 self.assertRaises(TypeError, meth, off42, off42)
2627
Tim Peters10cadce2003-01-23 19:58:02 +00002628 # We don't know which time zone we're in, and don't have a tzinfo
2629 # class to represent it, so seeing whether a tz argument actually
2630 # does a conversion is tricky.
2631 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2632 utc = FixedOffset(0, "utc", 0)
2633 for dummy in range(3):
2634 now = datetime.now(weirdtz)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002635 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002636 utcnow = datetime.utcnow().replace(tzinfo=utc)
2637 now2 = utcnow.astimezone(weirdtz)
2638 if abs(now - now2) < timedelta(seconds=30):
2639 break
2640 # Else the code is broken, or more than 30 seconds passed between
2641 # calls; assuming the latter, just try again.
2642 else:
2643 # Three strikes and we're out.
2644 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2645
Tim Peters2a799bf2002-12-16 20:18:38 +00002646 def test_tzinfo_fromtimestamp(self):
2647 import time
2648 meth = self.theclass.fromtimestamp
2649 ts = time.time()
2650 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2651 base = meth(ts)
2652 # Try with and without naming the keyword.
2653 off42 = FixedOffset(42, "42")
2654 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002655 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002656 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002657 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002658 # Bad argument with and w/o naming the keyword.
2659 self.assertRaises(TypeError, meth, ts, 16)
2660 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2661 # Bad keyword name.
2662 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2663 # Too many args.
2664 self.assertRaises(TypeError, meth, ts, off42, off42)
2665 # Too few args.
2666 self.assertRaises(TypeError, meth)
2667
Tim Peters2a44a8d2003-01-23 20:53:10 +00002668 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002669 timestamp = 1000000000
2670 utcdatetime = datetime.utcfromtimestamp(timestamp)
2671 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2672 # But on some flavor of Mac, it's nowhere near that. So we can't have
2673 # any idea here what time that actually is, we can only test that
2674 # relative changes match.
2675 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2676 tz = FixedOffset(utcoffset, "tz", 0)
2677 expected = utcdatetime + utcoffset
2678 got = datetime.fromtimestamp(timestamp, tz)
2679 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002680
Tim Peters2a799bf2002-12-16 20:18:38 +00002681 def test_tzinfo_utcnow(self):
2682 meth = self.theclass.utcnow
2683 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2684 base = meth()
2685 # Try with and without naming the keyword; for whatever reason,
2686 # utcnow() doesn't accept a tzinfo argument.
2687 off42 = FixedOffset(42, "42")
2688 self.assertRaises(TypeError, meth, off42)
2689 self.assertRaises(TypeError, meth, tzinfo=off42)
2690
2691 def test_tzinfo_utcfromtimestamp(self):
2692 import time
2693 meth = self.theclass.utcfromtimestamp
2694 ts = time.time()
2695 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2696 base = meth(ts)
2697 # Try with and without naming the keyword; for whatever reason,
2698 # utcfromtimestamp() doesn't accept a tzinfo argument.
2699 off42 = FixedOffset(42, "42")
2700 self.assertRaises(TypeError, meth, ts, off42)
2701 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2702
2703 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002704 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002705 # DST flag.
2706 class DST(tzinfo):
2707 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002708 if isinstance(dstvalue, int):
2709 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002710 self.dstvalue = dstvalue
2711 def dst(self, dt):
2712 return self.dstvalue
2713
2714 cls = self.theclass
2715 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2716 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2717 t = d.timetuple()
2718 self.assertEqual(1, t.tm_year)
2719 self.assertEqual(1, t.tm_mon)
2720 self.assertEqual(1, t.tm_mday)
2721 self.assertEqual(10, t.tm_hour)
2722 self.assertEqual(20, t.tm_min)
2723 self.assertEqual(30, t.tm_sec)
2724 self.assertEqual(0, t.tm_wday)
2725 self.assertEqual(1, t.tm_yday)
2726 self.assertEqual(flag, t.tm_isdst)
2727
2728 # dst() returns wrong type.
2729 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2730
2731 # dst() at the edge.
2732 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2733 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2734
2735 # dst() out of range.
2736 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2737 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2738
2739 def test_utctimetuple(self):
2740 class DST(tzinfo):
2741 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002742 if isinstance(dstvalue, int):
2743 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002744 self.dstvalue = dstvalue
2745 def dst(self, dt):
2746 return self.dstvalue
2747
2748 cls = self.theclass
2749 # This can't work: DST didn't implement utcoffset.
2750 self.assertRaises(NotImplementedError,
2751 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2752
2753 class UOFS(DST):
2754 def __init__(self, uofs, dofs=None):
2755 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002756 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002757 def utcoffset(self, dt):
2758 return self.uofs
2759
2760 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2761 # in effect for a UTC time.
2762 for dstvalue in -33, 33, 0, None:
2763 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2764 t = d.utctimetuple()
2765 self.assertEqual(d.year, t.tm_year)
2766 self.assertEqual(d.month, t.tm_mon)
2767 self.assertEqual(d.day, t.tm_mday)
2768 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2769 self.assertEqual(13, t.tm_min)
2770 self.assertEqual(d.second, t.tm_sec)
2771 self.assertEqual(d.weekday(), t.tm_wday)
2772 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2773 t.tm_yday)
2774 self.assertEqual(0, t.tm_isdst)
2775
2776 # At the edges, UTC adjustment can normalize into years out-of-range
2777 # for a datetime object. Ensure that a correct timetuple is
2778 # created anyway.
2779 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2780 # That goes back 1 minute less than a full day.
2781 t = tiny.utctimetuple()
2782 self.assertEqual(t.tm_year, MINYEAR-1)
2783 self.assertEqual(t.tm_mon, 12)
2784 self.assertEqual(t.tm_mday, 31)
2785 self.assertEqual(t.tm_hour, 0)
2786 self.assertEqual(t.tm_min, 1)
2787 self.assertEqual(t.tm_sec, 37)
2788 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2789 self.assertEqual(t.tm_isdst, 0)
2790
2791 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2792 # That goes forward 1 minute less than a full day.
2793 t = huge.utctimetuple()
2794 self.assertEqual(t.tm_year, MAXYEAR+1)
2795 self.assertEqual(t.tm_mon, 1)
2796 self.assertEqual(t.tm_mday, 1)
2797 self.assertEqual(t.tm_hour, 23)
2798 self.assertEqual(t.tm_min, 58)
2799 self.assertEqual(t.tm_sec, 37)
2800 self.assertEqual(t.tm_yday, 1)
2801 self.assertEqual(t.tm_isdst, 0)
2802
2803 def test_tzinfo_isoformat(self):
2804 zero = FixedOffset(0, "+00:00")
2805 plus = FixedOffset(220, "+03:40")
2806 minus = FixedOffset(-231, "-03:51")
2807 unknown = FixedOffset(None, "")
2808
2809 cls = self.theclass
2810 datestr = '0001-02-03'
2811 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002812 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002813 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2814 timestr = '04:05:59' + (us and '.987001' or '')
2815 ofsstr = ofs is not None and d.tzname() or ''
2816 tailstr = timestr + ofsstr
2817 iso = d.isoformat()
2818 self.assertEqual(iso, datestr + 'T' + tailstr)
2819 self.assertEqual(iso, d.isoformat('T'))
2820 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002821 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002822 self.assertEqual(str(d), datestr + ' ' + tailstr)
2823
Tim Peters12bf3392002-12-24 05:41:27 +00002824 def test_replace(self):
2825 cls = self.theclass
2826 z100 = FixedOffset(100, "+100")
2827 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2828 args = [1, 2, 3, 4, 5, 6, 7, z100]
2829 base = cls(*args)
2830 self.assertEqual(base, base.replace())
2831
2832 i = 0
2833 for name, newval in (("year", 2),
2834 ("month", 3),
2835 ("day", 4),
2836 ("hour", 5),
2837 ("minute", 6),
2838 ("second", 7),
2839 ("microsecond", 8),
2840 ("tzinfo", zm200)):
2841 newargs = args[:]
2842 newargs[i] = newval
2843 expected = cls(*newargs)
2844 got = base.replace(**{name: newval})
2845 self.assertEqual(expected, got)
2846 i += 1
2847
2848 # Ensure we can get rid of a tzinfo.
2849 self.assertEqual(base.tzname(), "+100")
2850 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002851 self.assertTrue(base2.tzinfo is None)
2852 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002853
2854 # Ensure we can add one.
2855 base3 = base2.replace(tzinfo=z100)
2856 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002857 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002858
2859 # Out of bounds.
2860 base = cls(2000, 2, 29)
2861 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002862
Tim Peters80475bb2002-12-25 07:40:55 +00002863 def test_more_astimezone(self):
2864 # The inherited test_astimezone covered some trivial and error cases.
2865 fnone = FixedOffset(None, "None")
2866 f44m = FixedOffset(44, "44")
2867 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2868
Tim Peters10cadce2003-01-23 19:58:02 +00002869 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002870 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002871 # Replacing with degenerate tzinfo raises an exception.
2872 self.assertRaises(ValueError, dt.astimezone, fnone)
2873 # Ditto with None tz.
2874 self.assertRaises(TypeError, dt.astimezone, None)
2875 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002876 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002877 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002878 self.assertEqual(x.date(), dt.date())
2879 self.assertEqual(x.time(), dt.time())
2880
2881 # Replacing with different tzinfo does adjust.
2882 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002883 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002884 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2885 expected = dt - dt.utcoffset() # in effect, convert to UTC
2886 expected += fm5h.utcoffset(dt) # and from there to local time
2887 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2888 self.assertEqual(got.date(), expected.date())
2889 self.assertEqual(got.time(), expected.time())
2890 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002891 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002892 self.assertEqual(got, expected)
2893
Tim Peters4c0db782002-12-26 05:01:19 +00002894 def test_aware_subtract(self):
2895 cls = self.theclass
2896
Tim Peters60c76e42002-12-27 00:41:11 +00002897 # Ensure that utcoffset() is ignored when the operands have the
2898 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002899 class OperandDependentOffset(tzinfo):
2900 def utcoffset(self, t):
2901 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002902 # d0 and d1 equal after adjustment
2903 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002904 else:
Tim Peters397301e2003-01-02 21:28:08 +00002905 # d2 off in the weeds
2906 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002907
2908 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2909 d0 = base.replace(minute=3)
2910 d1 = base.replace(minute=9)
2911 d2 = base.replace(minute=11)
2912 for x in d0, d1, d2:
2913 for y in d0, d1, d2:
2914 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002915 expected = timedelta(minutes=x.minute - y.minute)
2916 self.assertEqual(got, expected)
2917
2918 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2919 # ignored.
2920 base = cls(8, 9, 10, 11, 12, 13, 14)
2921 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2922 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2923 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2924 for x in d0, d1, d2:
2925 for y in d0, d1, d2:
2926 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002927 if (x is d0 or x is d1) and (y is d0 or y is d1):
2928 expected = timedelta(0)
2929 elif x is y is d2:
2930 expected = timedelta(0)
2931 elif x is d2:
2932 expected = timedelta(minutes=(11-59)-0)
2933 else:
2934 assert y is d2
2935 expected = timedelta(minutes=0-(11-59))
2936 self.assertEqual(got, expected)
2937
Tim Peters60c76e42002-12-27 00:41:11 +00002938 def test_mixed_compare(self):
2939 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002940 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002941 self.assertEqual(t1, t2)
2942 t2 = t2.replace(tzinfo=None)
2943 self.assertEqual(t1, t2)
2944 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2945 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002946 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2947 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002948
Tim Peters0bf60bd2003-01-08 20:40:01 +00002949 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002950 class Varies(tzinfo):
2951 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002952 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002953 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002954 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002955 return self.offset
2956
2957 v = Varies()
2958 t1 = t2.replace(tzinfo=v)
2959 t2 = t2.replace(tzinfo=v)
2960 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2961 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2962 self.assertEqual(t1, t2)
2963
2964 # But if they're not identical, it isn't ignored.
2965 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002966 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002967
Tim Petersa98924a2003-05-17 05:55:19 +00002968 def test_subclass_datetimetz(self):
2969
2970 class C(self.theclass):
2971 theAnswer = 42
2972
2973 def __new__(cls, *args, **kws):
2974 temp = kws.copy()
2975 extra = temp.pop('extra')
2976 result = self.theclass.__new__(cls, *args, **temp)
2977 result.extra = extra
2978 return result
2979
2980 def newmeth(self, start):
2981 return start + self.hour + self.year
2982
2983 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2984
2985 dt1 = self.theclass(*args)
2986 dt2 = C(*args, **{'extra': 7})
2987
2988 self.assertEqual(dt2.__class__, C)
2989 self.assertEqual(dt2.theAnswer, 42)
2990 self.assertEqual(dt2.extra, 7)
2991 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2992 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2993
Tim Peters621818b2002-12-29 23:44:49 +00002994# Pain to set up DST-aware tzinfo classes.
2995
2996def first_sunday_on_or_after(dt):
2997 days_to_go = 6 - dt.weekday()
2998 if days_to_go:
2999 dt += timedelta(days_to_go)
3000 return dt
3001
3002ZERO = timedelta(0)
3003HOUR = timedelta(hours=1)
3004DAY = timedelta(days=1)
3005# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3006DSTSTART = datetime(1, 4, 1, 2)
3007# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003008# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3009# being standard time on that day, there is no spelling in local time of
3010# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3011DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003012
3013class USTimeZone(tzinfo):
3014
3015 def __init__(self, hours, reprname, stdname, dstname):
3016 self.stdoffset = timedelta(hours=hours)
3017 self.reprname = reprname
3018 self.stdname = stdname
3019 self.dstname = dstname
3020
3021 def __repr__(self):
3022 return self.reprname
3023
3024 def tzname(self, dt):
3025 if self.dst(dt):
3026 return self.dstname
3027 else:
3028 return self.stdname
3029
3030 def utcoffset(self, dt):
3031 return self.stdoffset + self.dst(dt)
3032
3033 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003034 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003035 # An exception instead may be sensible here, in one or more of
3036 # the cases.
3037 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003038 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003039
3040 # Find first Sunday in April.
3041 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3042 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3043
3044 # Find last Sunday in October.
3045 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3046 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3047
Tim Peters621818b2002-12-29 23:44:49 +00003048 # Can't compare naive to aware objects, so strip the timezone from
3049 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003050 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003051 return HOUR
3052 else:
3053 return ZERO
3054
Tim Peters521fc152002-12-31 17:36:56 +00003055Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3056Central = USTimeZone(-6, "Central", "CST", "CDT")
3057Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3058Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003059utc_real = FixedOffset(0, "UTC", 0)
3060# For better test coverage, we want another flavor of UTC that's west of
3061# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003062utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003063
3064class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003065 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003066 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003067 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003068
Tim Peters0bf60bd2003-01-08 20:40:01 +00003069 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003070
Tim Peters521fc152002-12-31 17:36:56 +00003071 # Check a time that's inside DST.
3072 def checkinside(self, dt, tz, utc, dston, dstoff):
3073 self.assertEqual(dt.dst(), HOUR)
3074
3075 # Conversion to our own timezone is always an identity.
3076 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003077
3078 asutc = dt.astimezone(utc)
3079 there_and_back = asutc.astimezone(tz)
3080
3081 # Conversion to UTC and back isn't always an identity here,
3082 # because there are redundant spellings (in local time) of
3083 # UTC time when DST begins: the clock jumps from 1:59:59
3084 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3085 # make sense then. The classes above treat 2:MM:SS as
3086 # daylight time then (it's "after 2am"), really an alias
3087 # for 1:MM:SS standard time. The latter form is what
3088 # conversion back from UTC produces.
3089 if dt.date() == dston.date() and dt.hour == 2:
3090 # We're in the redundant hour, and coming back from
3091 # UTC gives the 1:MM:SS standard-time spelling.
3092 self.assertEqual(there_and_back + HOUR, dt)
3093 # Although during was considered to be in daylight
3094 # time, there_and_back is not.
3095 self.assertEqual(there_and_back.dst(), ZERO)
3096 # They're the same times in UTC.
3097 self.assertEqual(there_and_back.astimezone(utc),
3098 dt.astimezone(utc))
3099 else:
3100 # We're not in the redundant hour.
3101 self.assertEqual(dt, there_and_back)
3102
Tim Peters327098a2003-01-20 22:54:38 +00003103 # Because we have a redundant spelling when DST begins, there is
3104 # (unforunately) an hour when DST ends that can't be spelled at all in
3105 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3106 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3107 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3108 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3109 # expressed in local time. Nevertheless, we want conversion back
3110 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003111 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003112 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003113 if dt.date() == dstoff.date() and dt.hour == 0:
3114 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003115 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003116 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3117 nexthour_utc += HOUR
3118 nexthour_tz = nexthour_utc.astimezone(tz)
3119 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003120 else:
Tim Peters327098a2003-01-20 22:54:38 +00003121 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003122
3123 # Check a time that's outside DST.
3124 def checkoutside(self, dt, tz, utc):
3125 self.assertEqual(dt.dst(), ZERO)
3126
3127 # Conversion to our own timezone is always an identity.
3128 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003129
3130 # Converting to UTC and back is an identity too.
3131 asutc = dt.astimezone(utc)
3132 there_and_back = asutc.astimezone(tz)
3133 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003134
Tim Peters1024bf82002-12-30 17:09:40 +00003135 def convert_between_tz_and_utc(self, tz, utc):
3136 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003137 # Because 1:MM on the day DST ends is taken as being standard time,
3138 # there is no spelling in tz for the last hour of daylight time.
3139 # For purposes of the test, the last hour of DST is 0:MM, which is
3140 # taken as being daylight time (and 1:MM is taken as being standard
3141 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003142 dstoff = self.dstoff.replace(tzinfo=tz)
3143 for delta in (timedelta(weeks=13),
3144 DAY,
3145 HOUR,
3146 timedelta(minutes=1),
3147 timedelta(microseconds=1)):
3148
Tim Peters521fc152002-12-31 17:36:56 +00003149 self.checkinside(dston, tz, utc, dston, dstoff)
3150 for during in dston + delta, dstoff - delta:
3151 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003152
Tim Peters521fc152002-12-31 17:36:56 +00003153 self.checkoutside(dstoff, tz, utc)
3154 for outside in dston - delta, dstoff + delta:
3155 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003156
Tim Peters621818b2002-12-29 23:44:49 +00003157 def test_easy(self):
3158 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003159 self.convert_between_tz_and_utc(Eastern, utc_real)
3160 self.convert_between_tz_and_utc(Pacific, utc_real)
3161 self.convert_between_tz_and_utc(Eastern, utc_fake)
3162 self.convert_between_tz_and_utc(Pacific, utc_fake)
3163 # The next is really dancing near the edge. It works because
3164 # Pacific and Eastern are far enough apart that their "problem
3165 # hours" don't overlap.
3166 self.convert_between_tz_and_utc(Eastern, Pacific)
3167 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003168 # OTOH, these fail! Don't enable them. The difficulty is that
3169 # the edge case tests assume that every hour is representable in
3170 # the "utc" class. This is always true for a fixed-offset tzinfo
3171 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3172 # For these adjacent DST-aware time zones, the range of time offsets
3173 # tested ends up creating hours in the one that aren't representable
3174 # in the other. For the same reason, we would see failures in the
3175 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3176 # offset deltas in convert_between_tz_and_utc().
3177 #
3178 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3179 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003180
Tim Petersf3615152003-01-01 21:51:37 +00003181 def test_tricky(self):
3182 # 22:00 on day before daylight starts.
3183 fourback = self.dston - timedelta(hours=4)
3184 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003185 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003186 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3187 # 2", we should get the 3 spelling.
3188 # If we plug 22:00 the day before into Eastern, it "looks like std
3189 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3190 # to 22:00 lands on 2:00, which makes no sense in local time (the
3191 # local clock jumps from 1 to 3). The point here is to make sure we
3192 # get the 3 spelling.
3193 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003194 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003195 self.assertEqual(expected, got)
3196
3197 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3198 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003199 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003200 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3201 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3202 # spelling.
3203 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003204 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003205 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003206
Tim Petersadf64202003-01-04 06:03:15 +00003207 # Now on the day DST ends, we want "repeat an hour" behavior.
3208 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3209 # EST 23:MM 0:MM 1:MM 2:MM
3210 # EDT 0:MM 1:MM 2:MM 3:MM
3211 # wall 0:MM 1:MM 1:MM 2:MM against these
3212 for utc in utc_real, utc_fake:
3213 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003214 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003215 # Convert that to UTC.
3216 first_std_hour -= tz.utcoffset(None)
3217 # Adjust for possibly fake UTC.
3218 asutc = first_std_hour + utc.utcoffset(None)
3219 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3220 # tz=Eastern.
3221 asutcbase = asutc.replace(tzinfo=utc)
3222 for tzhour in (0, 1, 1, 2):
3223 expectedbase = self.dstoff.replace(hour=tzhour)
3224 for minute in 0, 30, 59:
3225 expected = expectedbase.replace(minute=minute)
3226 asutc = asutcbase.replace(minute=minute)
3227 astz = asutc.astimezone(tz)
3228 self.assertEqual(astz.replace(tzinfo=None), expected)
3229 asutcbase += HOUR
3230
3231
Tim Peters710fb152003-01-02 19:35:54 +00003232 def test_bogus_dst(self):
3233 class ok(tzinfo):
3234 def utcoffset(self, dt): return HOUR
3235 def dst(self, dt): return HOUR
3236
3237 now = self.theclass.now().replace(tzinfo=utc_real)
3238 # Doesn't blow up.
3239 now.astimezone(ok())
3240
3241 # Does blow up.
3242 class notok(ok):
3243 def dst(self, dt): return None
3244 self.assertRaises(ValueError, now.astimezone, notok())
3245
Tim Peters52dcce22003-01-23 16:36:11 +00003246 def test_fromutc(self):
3247 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3248 now = datetime.utcnow().replace(tzinfo=utc_real)
3249 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3250 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3251 enow = Eastern.fromutc(now) # doesn't blow up
3252 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3253 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3254 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3255
3256 # Always converts UTC to standard time.
3257 class FauxUSTimeZone(USTimeZone):
3258 def fromutc(self, dt):
3259 return dt + self.stdoffset
3260 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3261
3262 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3263 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3264 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3265
3266 # Check around DST start.
3267 start = self.dston.replace(hour=4, tzinfo=Eastern)
3268 fstart = start.replace(tzinfo=FEastern)
3269 for wall in 23, 0, 1, 3, 4, 5:
3270 expected = start.replace(hour=wall)
3271 if wall == 23:
3272 expected -= timedelta(days=1)
3273 got = Eastern.fromutc(start)
3274 self.assertEqual(expected, got)
3275
3276 expected = fstart + FEastern.stdoffset
3277 got = FEastern.fromutc(fstart)
3278 self.assertEqual(expected, got)
3279
3280 # Ensure astimezone() calls fromutc() too.
3281 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3282 self.assertEqual(expected, got)
3283
3284 start += HOUR
3285 fstart += HOUR
3286
3287 # Check around DST end.
3288 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3289 fstart = start.replace(tzinfo=FEastern)
3290 for wall in 0, 1, 1, 2, 3, 4:
3291 expected = start.replace(hour=wall)
3292 got = Eastern.fromutc(start)
3293 self.assertEqual(expected, got)
3294
3295 expected = fstart + FEastern.stdoffset
3296 got = FEastern.fromutc(fstart)
3297 self.assertEqual(expected, got)
3298
3299 # Ensure astimezone() calls fromutc() too.
3300 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3301 self.assertEqual(expected, got)
3302
3303 start += HOUR
3304 fstart += HOUR
3305
Tim Peters710fb152003-01-02 19:35:54 +00003306
Tim Peters528ca532004-09-16 01:30:50 +00003307#############################################################################
3308# oddballs
3309
3310class Oddballs(unittest.TestCase):
3311
3312 def test_bug_1028306(self):
3313 # Trying to compare a date to a datetime should act like a mixed-
3314 # type comparison, despite that datetime is a subclass of date.
3315 as_date = date.today()
3316 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003317 self.assertTrue(as_date != as_datetime)
3318 self.assertTrue(as_datetime != as_date)
3319 self.assertTrue(not as_date == as_datetime)
3320 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003321 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3322 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3323 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3324 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3325 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3326 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3327 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3328 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3329
3330 # Neverthelss, comparison should work with the base-class (date)
3331 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003332 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003333 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003334 as_different = as_datetime.replace(day= different_day)
3335 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003336
3337 # And date should compare with other subclasses of date. If a
3338 # subclass wants to stop this, it's up to the subclass to do so.
3339 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3340 self.assertEqual(as_date, date_sc)
3341 self.assertEqual(date_sc, as_date)
3342
3343 # Ditto for datetimes.
3344 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3345 as_date.day, 0, 0, 0)
3346 self.assertEqual(as_datetime, datetime_sc)
3347 self.assertEqual(datetime_sc, as_datetime)
3348
Tim Peters2a799bf2002-12-16 20:18:38 +00003349def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003350 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003351
3352if __name__ == "__main__":
3353 test_main()