blob: d5238e70b72c2cc127e1c86b07c233a14145e2b5 [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
Alexander Belopolsky2f4087d2010-05-31 16:11:21 +00006import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00008import unittest
9
Mark Dickinsona56c4672009-01-27 18:17:45 +000010from operator import lt, le, gt, ge, eq, ne
11
Benjamin Petersonee8712c2008-05-20 21:35:26 +000012from test import support
Tim Peters2a799bf2002-12-16 20:18:38 +000013
14from datetime import MINYEAR, MAXYEAR
15from datetime import timedelta
16from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000017from datetime import time
18from datetime import date, datetime
19
Guido van Rossumbe6fe542007-07-19 23:55:34 +000020pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
21assert len(pickle_choices) == 3
Guido van Rossum177e41a2003-01-30 22:06:23 +000022
Tim Peters68124bb2003-02-08 03:46:31 +000023# An arbitrary collection of objects of non-datetime types, for testing
24# mixed-type comparisons.
Guido van Rossume2a383d2007-01-15 16:59:06 +000025OTHERSTUFF = (10, 10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000026
Tim Peters2a799bf2002-12-16 20:18:38 +000027
28#############################################################################
29# module tests
30
31class TestModule(unittest.TestCase):
32
33 def test_constants(self):
34 import datetime
35 self.assertEqual(datetime.MINYEAR, 1)
36 self.assertEqual(datetime.MAXYEAR, 9999)
37
38#############################################################################
39# tzinfo tests
40
41class FixedOffset(tzinfo):
42 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000043 if isinstance(offset, int):
44 offset = timedelta(minutes=offset)
45 if isinstance(dstoffset, int):
46 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000047 self.__offset = offset
48 self.__name = name
49 self.__dstoffset = dstoffset
50 def __repr__(self):
51 return self.__name.lower()
52 def utcoffset(self, dt):
53 return self.__offset
54 def tzname(self, dt):
55 return self.__name
56 def dst(self, dt):
57 return self.__dstoffset
58
Tim Petersfb8472c2002-12-21 05:04:42 +000059class PicklableFixedOffset(FixedOffset):
60 def __init__(self, offset=None, name=None, dstoffset=None):
61 FixedOffset.__init__(self, offset, name, dstoffset)
62
Tim Peters2a799bf2002-12-16 20:18:38 +000063class TestTZInfo(unittest.TestCase):
64
65 def test_non_abstractness(self):
66 # In order to allow subclasses to get pickled, the C implementation
67 # wasn't able to get away with having __init__ raise
68 # NotImplementedError.
69 useless = tzinfo()
70 dt = datetime.max
71 self.assertRaises(NotImplementedError, useless.tzname, dt)
72 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
73 self.assertRaises(NotImplementedError, useless.dst, dt)
74
75 def test_subclass_must_override(self):
76 class NotEnough(tzinfo):
77 def __init__(self, offset, name):
78 self.__offset = offset
79 self.__name = name
Georg Brandlab91fde2009-08-13 08:51:18 +000080 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000081 ne = NotEnough(3, "NotByALongShot")
Georg Brandlab91fde2009-08-13 08:51:18 +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")
Georg Brandlab91fde2009-08-13 08:51:18 +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)
Georg Brandlab91fde2009-08-13 08:51:18 +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)
Georg Brandlab91fde2009-08-13 08:51:18 +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')
Georg Brandlab91fde2009-08-13 08:51:18 +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)
Georg Brandlab91fde2009-08-13 08:51:18 +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
Georg Brandlab91fde2009-08-13 08:51:18 +0000137 self.assertFalse(me == ())
138 self.assertTrue(me != ())
139 self.assertFalse(() == me)
140 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000141
Georg Brandlab91fde2009-08-13 08:51:18 +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
Georg Brandlab91fde2009-08-13 08:51:18 +0000145 self.assertTrue([] in [me, 1, 20, []])
146 self.assertFalse([] not in [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000147
148 def test_harmful_mixed_comparison(self):
149 me = self.theclass(1, 1, 1)
150
151 self.assertRaises(TypeError, lambda: me < ())
152 self.assertRaises(TypeError, lambda: me <= ())
153 self.assertRaises(TypeError, lambda: me > ())
154 self.assertRaises(TypeError, lambda: me >= ())
155
156 self.assertRaises(TypeError, lambda: () < me)
157 self.assertRaises(TypeError, lambda: () <= me)
158 self.assertRaises(TypeError, lambda: () > me)
159 self.assertRaises(TypeError, lambda: () >= me)
160
Tim Peters07534a62003-02-07 22:50:28 +0000161#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000162# timedelta tests
163
Guido van Rossumd8faa362007-04-27 19:54:29 +0000164class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000165
166 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000167
168 def test_constructor(self):
169 eq = self.assertEqual
170 td = timedelta
171
172 # Check keyword args to constructor
173 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
174 milliseconds=0, microseconds=0))
175 eq(td(1), td(days=1))
176 eq(td(0, 1), td(seconds=1))
177 eq(td(0, 0, 1), td(microseconds=1))
178 eq(td(weeks=1), td(days=7))
179 eq(td(days=1), td(hours=24))
180 eq(td(hours=1), td(minutes=60))
181 eq(td(minutes=1), td(seconds=60))
182 eq(td(seconds=1), td(milliseconds=1000))
183 eq(td(milliseconds=1), td(microseconds=1000))
184
185 # Check float args to constructor
186 eq(td(weeks=1.0/7), td(days=1))
187 eq(td(days=1.0/24), td(hours=1))
188 eq(td(hours=1.0/60), td(minutes=1))
189 eq(td(minutes=1.0/60), td(seconds=1))
190 eq(td(seconds=0.001), td(milliseconds=1))
191 eq(td(milliseconds=0.001), td(microseconds=1))
192
193 def test_computations(self):
194 eq = self.assertEqual
195 td = timedelta
196
197 a = td(7) # One week
198 b = td(0, 60) # One minute
199 c = td(0, 0, 1000) # One millisecond
200 eq(a+b+c, td(7, 60, 1000))
201 eq(a-b, td(6, 24*3600 - 60))
202 eq(-a, td(-7))
203 eq(+a, td(7))
204 eq(-b, td(-1, 24*3600 - 60))
205 eq(-c, td(-1, 24*3600 - 1, 999000))
206 eq(abs(a), a)
207 eq(abs(-a), a)
208 eq(td(6, 24*3600), a)
209 eq(td(0, 0, 60*1000000), b)
210 eq(a*10, td(70))
211 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000212 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000213 eq(b*10, td(0, 600))
214 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000215 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000216 eq(c*10, td(0, 0, 10000))
217 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000218 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000219 eq(a*-1, -a)
220 eq(b*-2, -b-b)
221 eq(c*-2, -c+-c)
222 eq(b*(60*24), (b*60)*24)
223 eq(b*(60*24), (60*b)*24)
224 eq(c*1000, td(0, 1))
225 eq(1000*c, td(0, 1))
226 eq(a//7, td(1))
227 eq(b//10, td(0, 6))
228 eq(c//1000, td(0, 0, 1))
229 eq(a//10, td(0, 7*24*360))
230 eq(a//3600000, td(0, 0, 7*24*1000))
231
232 def test_disallowed_computations(self):
233 a = timedelta(42)
234
235 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000236 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000237 self.assertRaises(TypeError, lambda: a+i)
238 self.assertRaises(TypeError, lambda: a-i)
239 self.assertRaises(TypeError, lambda: i+a)
240 self.assertRaises(TypeError, lambda: i-a)
241
242 # Mul/div by float isn't supported.
243 x = 2.3
244 self.assertRaises(TypeError, lambda: a*x)
245 self.assertRaises(TypeError, lambda: x*a)
246 self.assertRaises(TypeError, lambda: a/x)
247 self.assertRaises(TypeError, lambda: x/a)
248 self.assertRaises(TypeError, lambda: a // x)
249 self.assertRaises(TypeError, lambda: x // a)
250
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000251 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000252 # Division by zero doesn't make sense.
Guido van Rossume2a383d2007-01-15 16:59:06 +0000253 for zero in 0, 0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000254 self.assertRaises(TypeError, lambda: zero // a)
255 self.assertRaises(ZeroDivisionError, lambda: a // zero)
256
257 def test_basic_attributes(self):
258 days, seconds, us = 1, 7, 31
259 td = timedelta(days, seconds, us)
260 self.assertEqual(td.days, days)
261 self.assertEqual(td.seconds, seconds)
262 self.assertEqual(td.microseconds, us)
263
264 def test_carries(self):
265 t1 = timedelta(days=100,
266 weeks=-7,
267 hours=-24*(100-49),
268 minutes=-3,
269 seconds=12,
270 microseconds=(3*60 - 12) * 1e6 + 1)
271 t2 = timedelta(microseconds=1)
272 self.assertEqual(t1, t2)
273
274 def test_hash_equality(self):
275 t1 = timedelta(days=100,
276 weeks=-7,
277 hours=-24*(100-49),
278 minutes=-3,
279 seconds=12,
280 microseconds=(3*60 - 12) * 1000000)
281 t2 = timedelta()
282 self.assertEqual(hash(t1), hash(t2))
283
284 t1 += timedelta(weeks=7)
285 t2 += timedelta(days=7*7)
286 self.assertEqual(t1, t2)
287 self.assertEqual(hash(t1), hash(t2))
288
289 d = {t1: 1}
290 d[t2] = 2
291 self.assertEqual(len(d), 1)
292 self.assertEqual(d[t1], 2)
293
294 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000295 args = 12, 34, 56
296 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000297 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000298 green = pickler.dumps(orig, proto)
299 derived = unpickler.loads(green)
300 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000301
302 def test_compare(self):
303 t1 = timedelta(2, 3, 4)
304 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000305 self.assertEqual(t1, t2)
Georg Brandlab91fde2009-08-13 08:51:18 +0000306 self.assertTrue(t1 <= t2)
307 self.assertTrue(t1 >= t2)
308 self.assertTrue(not t1 != t2)
309 self.assertTrue(not t1 < t2)
310 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000311
312 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
313 t2 = timedelta(*args) # this is larger than t1
Georg Brandlab91fde2009-08-13 08:51:18 +0000314 self.assertTrue(t1 < t2)
315 self.assertTrue(t2 > t1)
316 self.assertTrue(t1 <= t2)
317 self.assertTrue(t2 >= t1)
318 self.assertTrue(t1 != t2)
319 self.assertTrue(t2 != t1)
320 self.assertTrue(not t1 == t2)
321 self.assertTrue(not t2 == t1)
322 self.assertTrue(not t1 > t2)
323 self.assertTrue(not t2 < t1)
324 self.assertTrue(not t1 >= t2)
325 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000326
Tim Peters68124bb2003-02-08 03:46:31 +0000327 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000328 self.assertEqual(t1 == badarg, False)
329 self.assertEqual(t1 != badarg, True)
330 self.assertEqual(badarg == t1, False)
331 self.assertEqual(badarg != t1, True)
332
Tim Peters2a799bf2002-12-16 20:18:38 +0000333 self.assertRaises(TypeError, lambda: t1 <= badarg)
334 self.assertRaises(TypeError, lambda: t1 < badarg)
335 self.assertRaises(TypeError, lambda: t1 > badarg)
336 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000337 self.assertRaises(TypeError, lambda: badarg <= t1)
338 self.assertRaises(TypeError, lambda: badarg < t1)
339 self.assertRaises(TypeError, lambda: badarg > t1)
340 self.assertRaises(TypeError, lambda: badarg >= t1)
341
342 def test_str(self):
343 td = timedelta
344 eq = self.assertEqual
345
346 eq(str(td(1)), "1 day, 0:00:00")
347 eq(str(td(-1)), "-1 day, 0:00:00")
348 eq(str(td(2)), "2 days, 0:00:00")
349 eq(str(td(-2)), "-2 days, 0:00:00")
350
351 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
352 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
353 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
354 "-210 days, 23:12:34")
355
356 eq(str(td(milliseconds=1)), "0:00:00.001000")
357 eq(str(td(microseconds=3)), "0:00:00.000003")
358
359 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
360 microseconds=999999)),
361 "999999999 days, 23:59:59.999999")
362
363 def test_roundtrip(self):
364 for td in (timedelta(days=999999999, hours=23, minutes=59,
365 seconds=59, microseconds=999999),
366 timedelta(days=-999999999),
367 timedelta(days=1, seconds=2, microseconds=3)):
368
369 # Verify td -> string -> td identity.
370 s = repr(td)
Georg Brandlab91fde2009-08-13 08:51:18 +0000371 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000372 s = s[9:]
373 td2 = eval(s)
374 self.assertEqual(td, td2)
375
376 # Verify identity via reconstructing from pieces.
377 td2 = timedelta(td.days, td.seconds, td.microseconds)
378 self.assertEqual(td, td2)
379
380 def test_resolution_info(self):
Georg Brandlab91fde2009-08-13 08:51:18 +0000381 self.assertTrue(isinstance(timedelta.min, timedelta))
382 self.assertTrue(isinstance(timedelta.max, timedelta))
383 self.assertTrue(isinstance(timedelta.resolution, timedelta))
384 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000385 self.assertEqual(timedelta.min, timedelta(-999999999))
386 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
387 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
388
389 def test_overflow(self):
390 tiny = timedelta.resolution
391
392 td = timedelta.min + tiny
393 td -= tiny # no problem
394 self.assertRaises(OverflowError, td.__sub__, tiny)
395 self.assertRaises(OverflowError, td.__add__, -tiny)
396
397 td = timedelta.max - tiny
398 td += tiny # no problem
399 self.assertRaises(OverflowError, td.__add__, tiny)
400 self.assertRaises(OverflowError, td.__sub__, -tiny)
401
402 self.assertRaises(OverflowError, lambda: -timedelta.max)
403
404 def test_microsecond_rounding(self):
405 td = timedelta
406 eq = self.assertEqual
407
408 # Single-field rounding.
409 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
410 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
411 eq(td(milliseconds=0.6/1000), td(microseconds=1))
412 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
413
414 # Rounding due to contributions from more than one field.
415 us_per_hour = 3600e6
416 us_per_day = us_per_hour * 24
417 eq(td(days=.4/us_per_day), td(0))
418 eq(td(hours=.2/us_per_hour), td(0))
419 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
420
421 eq(td(days=-.4/us_per_day), td(0))
422 eq(td(hours=-.2/us_per_hour), td(0))
423 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
424
425 def test_massive_normalization(self):
426 td = timedelta(microseconds=-1)
427 self.assertEqual((td.days, td.seconds, td.microseconds),
428 (-1, 24*3600-1, 999999))
429
430 def test_bool(self):
Georg Brandlab91fde2009-08-13 08:51:18 +0000431 self.assertTrue(timedelta(1))
432 self.assertTrue(timedelta(0, 1))
433 self.assertTrue(timedelta(0, 0, 1))
434 self.assertTrue(timedelta(microseconds=1))
435 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000436
Tim Petersb0c854d2003-05-17 15:57:00 +0000437 def test_subclass_timedelta(self):
438
439 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000440 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000441 def from_td(td):
442 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000443
444 def as_hours(self):
445 sum = (self.days * 24 +
446 self.seconds / 3600.0 +
447 self.microseconds / 3600e6)
448 return round(sum)
449
450 t1 = T(days=1)
Georg Brandlab91fde2009-08-13 08:51:18 +0000451 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000452 self.assertEqual(t1.as_hours(), 24)
453
454 t2 = T(days=-1, seconds=-3600)
Georg Brandlab91fde2009-08-13 08:51:18 +0000455 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000456 self.assertEqual(t2.as_hours(), -25)
457
458 t3 = t1 + t2
Georg Brandlab91fde2009-08-13 08:51:18 +0000459 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000460 t4 = T.from_td(t3)
Georg Brandlab91fde2009-08-13 08:51:18 +0000461 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000462 self.assertEqual(t3.days, t4.days)
463 self.assertEqual(t3.seconds, t4.seconds)
464 self.assertEqual(t3.microseconds, t4.microseconds)
465 self.assertEqual(str(t3), str(t4))
466 self.assertEqual(t4.as_hours(), -1)
467
Tim Peters2a799bf2002-12-16 20:18:38 +0000468#############################################################################
469# date tests
470
471class TestDateOnly(unittest.TestCase):
472 # Tests here won't pass if also run on datetime objects, so don't
473 # subclass this to test datetimes too.
474
475 def test_delta_non_days_ignored(self):
476 dt = date(2000, 1, 2)
477 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
478 microseconds=5)
479 days = timedelta(delta.days)
480 self.assertEqual(days, timedelta(1))
481
482 dt2 = dt + delta
483 self.assertEqual(dt2, dt + days)
484
485 dt2 = delta + dt
486 self.assertEqual(dt2, dt + days)
487
488 dt2 = dt - delta
489 self.assertEqual(dt2, dt - days)
490
491 delta = -delta
492 days = timedelta(delta.days)
493 self.assertEqual(days, timedelta(-2))
494
495 dt2 = dt + delta
496 self.assertEqual(dt2, dt + days)
497
498 dt2 = delta + dt
499 self.assertEqual(dt2, dt + days)
500
501 dt2 = dt - delta
502 self.assertEqual(dt2, dt - days)
503
Tim Peters604c0132004-06-07 23:04:33 +0000504class SubclassDate(date):
505 sub_var = 1
506
Guido van Rossumd8faa362007-04-27 19:54:29 +0000507class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000508 # Tests here should pass for both dates and datetimes, except for a
509 # few tests that TestDateTime overrides.
510
511 theclass = date
512
513 def test_basic_attributes(self):
514 dt = self.theclass(2002, 3, 1)
515 self.assertEqual(dt.year, 2002)
516 self.assertEqual(dt.month, 3)
517 self.assertEqual(dt.day, 1)
518
519 def test_roundtrip(self):
520 for dt in (self.theclass(1, 2, 3),
521 self.theclass.today()):
522 # Verify dt -> string -> date identity.
523 s = repr(dt)
Georg Brandlab91fde2009-08-13 08:51:18 +0000524 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000525 s = s[9:]
526 dt2 = eval(s)
527 self.assertEqual(dt, dt2)
528
529 # Verify identity via reconstructing from pieces.
530 dt2 = self.theclass(dt.year, dt.month, dt.day)
531 self.assertEqual(dt, dt2)
532
533 def test_ordinal_conversions(self):
534 # Check some fixed values.
535 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
536 (1, 12, 31, 365),
537 (2, 1, 1, 366),
538 # first example from "Calendrical Calculations"
539 (1945, 11, 12, 710347)]:
540 d = self.theclass(y, m, d)
541 self.assertEqual(n, d.toordinal())
542 fromord = self.theclass.fromordinal(n)
543 self.assertEqual(d, fromord)
544 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000545 # if we're checking something fancier than a date, verify
546 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000547 self.assertEqual(fromord.hour, 0)
548 self.assertEqual(fromord.minute, 0)
549 self.assertEqual(fromord.second, 0)
550 self.assertEqual(fromord.microsecond, 0)
551
Tim Peters0bf60bd2003-01-08 20:40:01 +0000552 # Check first and last days of year spottily across the whole
553 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000554 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000555 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
556 d = self.theclass(year, 1, 1)
557 n = d.toordinal()
558 d2 = self.theclass.fromordinal(n)
559 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000560 # Verify that moving back a day gets to the end of year-1.
561 if year > 1:
562 d = self.theclass.fromordinal(n-1)
563 d2 = self.theclass(year-1, 12, 31)
564 self.assertEqual(d, d2)
565 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000566
567 # Test every day in a leap-year and a non-leap year.
568 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
569 for year, isleap in (2000, True), (2002, False):
570 n = self.theclass(year, 1, 1).toordinal()
571 for month, maxday in zip(range(1, 13), dim):
572 if month == 2 and isleap:
573 maxday += 1
574 for day in range(1, maxday+1):
575 d = self.theclass(year, month, day)
576 self.assertEqual(d.toordinal(), n)
577 self.assertEqual(d, self.theclass.fromordinal(n))
578 n += 1
579
580 def test_extreme_ordinals(self):
581 a = self.theclass.min
582 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
583 aord = a.toordinal()
584 b = a.fromordinal(aord)
585 self.assertEqual(a, b)
586
587 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
588
589 b = a + timedelta(days=1)
590 self.assertEqual(b.toordinal(), aord + 1)
591 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
592
593 a = self.theclass.max
594 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
595 aord = a.toordinal()
596 b = a.fromordinal(aord)
597 self.assertEqual(a, b)
598
599 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
600
601 b = a - timedelta(days=1)
602 self.assertEqual(b.toordinal(), aord - 1)
603 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
604
605 def test_bad_constructor_arguments(self):
606 # bad years
607 self.theclass(MINYEAR, 1, 1) # no exception
608 self.theclass(MAXYEAR, 1, 1) # no exception
609 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
610 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
611 # bad months
612 self.theclass(2000, 1, 1) # no exception
613 self.theclass(2000, 12, 1) # no exception
614 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
615 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
616 # bad days
617 self.theclass(2000, 2, 29) # no exception
618 self.theclass(2004, 2, 29) # no exception
619 self.theclass(2400, 2, 29) # no exception
620 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
621 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
622 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
623 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
624 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
625 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
626
627 def test_hash_equality(self):
628 d = self.theclass(2000, 12, 31)
629 # same thing
630 e = self.theclass(2000, 12, 31)
631 self.assertEqual(d, e)
632 self.assertEqual(hash(d), hash(e))
633
634 dic = {d: 1}
635 dic[e] = 2
636 self.assertEqual(len(dic), 1)
637 self.assertEqual(dic[d], 2)
638 self.assertEqual(dic[e], 2)
639
640 d = self.theclass(2001, 1, 1)
641 # same thing
642 e = self.theclass(2001, 1, 1)
643 self.assertEqual(d, e)
644 self.assertEqual(hash(d), hash(e))
645
646 dic = {d: 1}
647 dic[e] = 2
648 self.assertEqual(len(dic), 1)
649 self.assertEqual(dic[d], 2)
650 self.assertEqual(dic[e], 2)
651
652 def test_computations(self):
653 a = self.theclass(2002, 1, 31)
654 b = self.theclass(1956, 1, 31)
655
656 diff = a-b
657 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
658 self.assertEqual(diff.seconds, 0)
659 self.assertEqual(diff.microseconds, 0)
660
661 day = timedelta(1)
662 week = timedelta(7)
663 a = self.theclass(2002, 3, 2)
664 self.assertEqual(a + day, self.theclass(2002, 3, 3))
665 self.assertEqual(day + a, self.theclass(2002, 3, 3))
666 self.assertEqual(a - day, self.theclass(2002, 3, 1))
667 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
668 self.assertEqual(a + week, self.theclass(2002, 3, 9))
669 self.assertEqual(a - week, self.theclass(2002, 2, 23))
670 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
671 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
672 self.assertEqual((a + week) - a, week)
673 self.assertEqual((a + day) - a, day)
674 self.assertEqual((a - week) - a, -week)
675 self.assertEqual((a - day) - a, -day)
676 self.assertEqual(a - (a + week), -week)
677 self.assertEqual(a - (a + day), -day)
678 self.assertEqual(a - (a - week), week)
679 self.assertEqual(a - (a - day), day)
680
681 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000682 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000683 self.assertRaises(TypeError, lambda: a+i)
684 self.assertRaises(TypeError, lambda: a-i)
685 self.assertRaises(TypeError, lambda: i+a)
686 self.assertRaises(TypeError, lambda: i-a)
687
688 # delta - date is senseless.
689 self.assertRaises(TypeError, lambda: day - a)
690 # mixing date and (delta or date) via * or // is senseless
691 self.assertRaises(TypeError, lambda: day * a)
692 self.assertRaises(TypeError, lambda: a * day)
693 self.assertRaises(TypeError, lambda: day // a)
694 self.assertRaises(TypeError, lambda: a // day)
695 self.assertRaises(TypeError, lambda: a * a)
696 self.assertRaises(TypeError, lambda: a // a)
697 # date + date is senseless
698 self.assertRaises(TypeError, lambda: a + a)
699
700 def test_overflow(self):
701 tiny = self.theclass.resolution
702
Alexander Belopolsky3efc2fd2010-05-27 22:03:53 +0000703 for delta in [tiny, timedelta(1), timedelta(2)]:
704 dt = self.theclass.min + delta
705 dt -= delta # no problem
706 self.assertRaises(OverflowError, dt.__sub__, delta)
707 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000708
Alexander Belopolsky3efc2fd2010-05-27 22:03:53 +0000709 dt = self.theclass.max - delta
710 dt += delta # no problem
711 self.assertRaises(OverflowError, dt.__add__, delta)
712 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000713
714 def test_fromtimestamp(self):
715 import time
716
717 # Try an arbitrary fixed value.
718 year, month, day = 1999, 9, 19
719 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
720 d = self.theclass.fromtimestamp(ts)
721 self.assertEqual(d.year, year)
722 self.assertEqual(d.month, month)
723 self.assertEqual(d.day, day)
724
Tim Peters1b6f7a92004-06-20 02:50:16 +0000725 def test_insane_fromtimestamp(self):
726 # It's possible that some platform maps time_t to double,
727 # and that this test will fail there. This test should
728 # exempt such platforms (provided they return reasonable
729 # results!).
730 for insane in -1e200, 1e200:
731 self.assertRaises(ValueError, self.theclass.fromtimestamp,
732 insane)
733
Tim Peters2a799bf2002-12-16 20:18:38 +0000734 def test_today(self):
735 import time
736
737 # We claim that today() is like fromtimestamp(time.time()), so
738 # prove it.
739 for dummy in range(3):
740 today = self.theclass.today()
741 ts = time.time()
742 todayagain = self.theclass.fromtimestamp(ts)
743 if today == todayagain:
744 break
745 # There are several legit reasons that could fail:
746 # 1. It recently became midnight, between the today() and the
747 # time() calls.
748 # 2. The platform time() has such fine resolution that we'll
749 # never get the same value twice.
750 # 3. The platform time() has poor resolution, and we just
751 # happened to call today() right before a resolution quantum
752 # boundary.
753 # 4. The system clock got fiddled between calls.
754 # In any case, wait a little while and try again.
755 time.sleep(0.1)
756
757 # It worked or it didn't. If it didn't, assume it's reason #2, and
758 # let the test pass if they're within half a second of each other.
Georg Brandlab91fde2009-08-13 08:51:18 +0000759 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000760 abs(todayagain - today) < timedelta(seconds=0.5))
761
762 def test_weekday(self):
763 for i in range(7):
764 # March 4, 2002 is a Monday
765 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
766 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
767 # January 2, 1956 is a Monday
768 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
769 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
770
771 def test_isocalendar(self):
772 # Check examples from
773 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
774 for i in range(7):
775 d = self.theclass(2003, 12, 22+i)
776 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
777 d = self.theclass(2003, 12, 29) + timedelta(i)
778 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
779 d = self.theclass(2004, 1, 5+i)
780 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
781 d = self.theclass(2009, 12, 21+i)
782 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
783 d = self.theclass(2009, 12, 28) + timedelta(i)
784 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
785 d = self.theclass(2010, 1, 4+i)
786 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
787
788 def test_iso_long_years(self):
789 # Calculate long ISO years and compare to table from
790 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
791 ISO_LONG_YEARS_TABLE = """
792 4 32 60 88
793 9 37 65 93
794 15 43 71 99
795 20 48 76
796 26 54 82
797
798 105 133 161 189
799 111 139 167 195
800 116 144 172
801 122 150 178
802 128 156 184
803
804 201 229 257 285
805 207 235 263 291
806 212 240 268 296
807 218 246 274
808 224 252 280
809
810 303 331 359 387
811 308 336 364 392
812 314 342 370 398
813 320 348 376
814 325 353 381
815 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000816 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000817 L = []
818 for i in range(400):
819 d = self.theclass(2000+i, 12, 31)
820 d1 = self.theclass(1600+i, 12, 31)
821 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
822 if d.isocalendar()[1] == 53:
823 L.append(i)
824 self.assertEqual(L, iso_long_years)
825
826 def test_isoformat(self):
827 t = self.theclass(2, 3, 2)
828 self.assertEqual(t.isoformat(), "0002-03-02")
829
830 def test_ctime(self):
831 t = self.theclass(2002, 3, 2)
832 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
833
834 def test_strftime(self):
835 t = self.theclass(2005, 3, 2)
836 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000837 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000838 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000839
840 self.assertRaises(TypeError, t.strftime) # needs an arg
841 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
842 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
843
Georg Brandlf78e02b2008-06-10 17:40:04 +0000844 # test that unicode input is allowed (issue 2782)
845 self.assertEqual(t.strftime("%m"), "03")
846
Tim Peters2a799bf2002-12-16 20:18:38 +0000847 # A naive object replaces %z and %Z w/ empty strings.
848 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
849
Benjamin Petersone1cdfd72009-01-18 21:02:37 +0000850 #make sure that invalid format specifiers are handled correctly
851 #self.assertRaises(ValueError, t.strftime, "%e")
852 #self.assertRaises(ValueError, t.strftime, "%")
853 #self.assertRaises(ValueError, t.strftime, "%#")
854
855 #oh well, some systems just ignore those invalid ones.
856 #at least, excercise them to make sure that no crashes
857 #are generated
858 for f in ["%e", "%", "%#"]:
859 try:
860 t.strftime(f)
861 except ValueError:
862 pass
863
864 #check that this standard extension works
865 t.strftime("%f")
866
Georg Brandlf78e02b2008-06-10 17:40:04 +0000867
Eric Smith1ba31142007-09-11 18:06:02 +0000868 def test_format(self):
869 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000870 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000871
872 # check that a derived class's __str__() gets called
873 class A(self.theclass):
874 def __str__(self):
875 return 'A'
876 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000877 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000878
879 # check that a derived class's strftime gets called
880 class B(self.theclass):
881 def strftime(self, format_spec):
882 return 'B'
883 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000884 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000885
886 for fmt in ["m:%m d:%d y:%y",
887 "m:%m d:%d y:%y H:%H M:%M S:%S",
888 "%z %Z",
889 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +0000890 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
891 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
892 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +0000893
Tim Peters2a799bf2002-12-16 20:18:38 +0000894 def test_resolution_info(self):
Georg Brandlab91fde2009-08-13 08:51:18 +0000895 self.assertTrue(isinstance(self.theclass.min, self.theclass))
896 self.assertTrue(isinstance(self.theclass.max, self.theclass))
897 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
898 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000899
900 def test_extreme_timedelta(self):
901 big = self.theclass.max - self.theclass.min
902 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
903 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
904 # n == 315537897599999999 ~= 2**58.13
905 justasbig = timedelta(0, 0, n)
906 self.assertEqual(big, justasbig)
907 self.assertEqual(self.theclass.min + big, self.theclass.max)
908 self.assertEqual(self.theclass.max - big, self.theclass.min)
909
910 def test_timetuple(self):
911 for i in range(7):
912 # January 2, 1956 is a Monday (0)
913 d = self.theclass(1956, 1, 2+i)
914 t = d.timetuple()
915 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
916 # February 1, 1956 is a Wednesday (2)
917 d = self.theclass(1956, 2, 1+i)
918 t = d.timetuple()
919 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
920 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
921 # of the year.
922 d = self.theclass(1956, 3, 1+i)
923 t = d.timetuple()
924 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
925 self.assertEqual(t.tm_year, 1956)
926 self.assertEqual(t.tm_mon, 3)
927 self.assertEqual(t.tm_mday, 1+i)
928 self.assertEqual(t.tm_hour, 0)
929 self.assertEqual(t.tm_min, 0)
930 self.assertEqual(t.tm_sec, 0)
931 self.assertEqual(t.tm_wday, (3+i)%7)
932 self.assertEqual(t.tm_yday, 61+i)
933 self.assertEqual(t.tm_isdst, -1)
934
935 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000936 args = 6, 7, 23
937 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000938 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000939 green = pickler.dumps(orig, proto)
940 derived = unpickler.loads(green)
941 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000942
943 def test_compare(self):
944 t1 = self.theclass(2, 3, 4)
945 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000946 self.assertEqual(t1, t2)
Georg Brandlab91fde2009-08-13 08:51:18 +0000947 self.assertTrue(t1 <= t2)
948 self.assertTrue(t1 >= t2)
949 self.assertTrue(not t1 != t2)
950 self.assertTrue(not t1 < t2)
951 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000952
953 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
954 t2 = self.theclass(*args) # this is larger than t1
Georg Brandlab91fde2009-08-13 08:51:18 +0000955 self.assertTrue(t1 < t2)
956 self.assertTrue(t2 > t1)
957 self.assertTrue(t1 <= t2)
958 self.assertTrue(t2 >= t1)
959 self.assertTrue(t1 != t2)
960 self.assertTrue(t2 != t1)
961 self.assertTrue(not t1 == t2)
962 self.assertTrue(not t2 == t1)
963 self.assertTrue(not t1 > t2)
964 self.assertTrue(not t2 < t1)
965 self.assertTrue(not t1 >= t2)
966 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000967
Tim Peters68124bb2003-02-08 03:46:31 +0000968 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000969 self.assertEqual(t1 == badarg, False)
970 self.assertEqual(t1 != badarg, True)
971 self.assertEqual(badarg == t1, False)
972 self.assertEqual(badarg != t1, True)
973
Tim Peters2a799bf2002-12-16 20:18:38 +0000974 self.assertRaises(TypeError, lambda: t1 < badarg)
975 self.assertRaises(TypeError, lambda: t1 > badarg)
976 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000977 self.assertRaises(TypeError, lambda: badarg <= t1)
978 self.assertRaises(TypeError, lambda: badarg < t1)
979 self.assertRaises(TypeError, lambda: badarg > t1)
980 self.assertRaises(TypeError, lambda: badarg >= t1)
981
Tim Peters8d81a012003-01-24 22:36:34 +0000982 def test_mixed_compare(self):
983 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000984
985 # Our class can be compared for equality to other classes
986 self.assertEqual(our == 1, False)
987 self.assertEqual(1 == our, False)
988 self.assertEqual(our != 1, True)
989 self.assertEqual(1 != our, True)
990
991 # But the ordering is undefined
992 self.assertRaises(TypeError, lambda: our < 1)
993 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +0000994
Guido van Rossum19960592006-08-24 17:29:38 +0000995 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +0000996
Guido van Rossum19960592006-08-24 17:29:38 +0000997 class SomeClass:
998 pass
999
1000 their = SomeClass()
1001 self.assertEqual(our == their, False)
1002 self.assertEqual(their == our, False)
1003 self.assertEqual(our != their, True)
1004 self.assertEqual(their != our, True)
1005 self.assertRaises(TypeError, lambda: our < their)
1006 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001007
Guido van Rossum19960592006-08-24 17:29:38 +00001008 # However, if the other class explicitly defines ordering
1009 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001010
Guido van Rossum19960592006-08-24 17:29:38 +00001011 class LargerThanAnything:
1012 def __lt__(self, other):
1013 return False
1014 def __le__(self, other):
1015 return isinstance(other, LargerThanAnything)
1016 def __eq__(self, other):
1017 return isinstance(other, LargerThanAnything)
1018 def __ne__(self, other):
1019 return not isinstance(other, LargerThanAnything)
1020 def __gt__(self, other):
1021 return not isinstance(other, LargerThanAnything)
1022 def __ge__(self, other):
1023 return True
1024
1025 their = LargerThanAnything()
1026 self.assertEqual(our == their, False)
1027 self.assertEqual(their == our, False)
1028 self.assertEqual(our != their, True)
1029 self.assertEqual(their != our, True)
1030 self.assertEqual(our < their, True)
1031 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001032
Tim Peters2a799bf2002-12-16 20:18:38 +00001033 def test_bool(self):
1034 # All dates are considered true.
Georg Brandlab91fde2009-08-13 08:51:18 +00001035 self.assertTrue(self.theclass.min)
1036 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001037
Guido van Rossum04110fb2007-08-24 16:32:05 +00001038 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001039 # For nasty technical reasons, we can't handle years before 1900.
1040 cls = self.theclass
1041 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1042 for y in 1, 49, 51, 99, 100, 1000, 1899:
1043 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001044
1045 def test_replace(self):
1046 cls = self.theclass
1047 args = [1, 2, 3]
1048 base = cls(*args)
1049 self.assertEqual(base, base.replace())
1050
1051 i = 0
1052 for name, newval in (("year", 2),
1053 ("month", 3),
1054 ("day", 4)):
1055 newargs = args[:]
1056 newargs[i] = newval
1057 expected = cls(*newargs)
1058 got = base.replace(**{name: newval})
1059 self.assertEqual(expected, got)
1060 i += 1
1061
1062 # Out of bounds.
1063 base = cls(2000, 2, 29)
1064 self.assertRaises(ValueError, base.replace, year=2001)
1065
Tim Petersa98924a2003-05-17 05:55:19 +00001066 def test_subclass_date(self):
1067
1068 class C(self.theclass):
1069 theAnswer = 42
1070
1071 def __new__(cls, *args, **kws):
1072 temp = kws.copy()
1073 extra = temp.pop('extra')
1074 result = self.theclass.__new__(cls, *args, **temp)
1075 result.extra = extra
1076 return result
1077
1078 def newmeth(self, start):
1079 return start + self.year + self.month
1080
1081 args = 2003, 4, 14
1082
1083 dt1 = self.theclass(*args)
1084 dt2 = C(*args, **{'extra': 7})
1085
1086 self.assertEqual(dt2.__class__, C)
1087 self.assertEqual(dt2.theAnswer, 42)
1088 self.assertEqual(dt2.extra, 7)
1089 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1090 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1091
Tim Peters604c0132004-06-07 23:04:33 +00001092 def test_pickling_subclass_date(self):
1093
1094 args = 6, 7, 23
1095 orig = SubclassDate(*args)
1096 for pickler, unpickler, proto in pickle_choices:
1097 green = pickler.dumps(orig, proto)
1098 derived = unpickler.loads(green)
1099 self.assertEqual(orig, derived)
1100
Tim Peters3f606292004-03-21 23:38:41 +00001101 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001102 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001103 # This is a low-overhead backdoor. A user can (by intent or
1104 # mistake) pass a string directly, which (if it's the right length)
1105 # will get treated like a pickle, and bypass the normal sanity
1106 # checks in the constructor. This can create insane objects.
1107 # The constructor doesn't want to burn the time to validate all
1108 # fields, but does check the month field. This stops, e.g.,
1109 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001110 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001111 if not issubclass(self.theclass, datetime):
1112 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001113 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001114 self.assertRaises(TypeError, self.theclass,
1115 base[:2] + month_byte + base[3:])
1116 for ord_byte in range(1, 13):
1117 # This shouldn't blow up because of the month byte alone. If
1118 # the implementation changes to do more-careful checking, it may
1119 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001120 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001121
Tim Peters2a799bf2002-12-16 20:18:38 +00001122#############################################################################
1123# datetime tests
1124
Tim Peters604c0132004-06-07 23:04:33 +00001125class SubclassDatetime(datetime):
1126 sub_var = 1
1127
Tim Peters2a799bf2002-12-16 20:18:38 +00001128class TestDateTime(TestDate):
1129
1130 theclass = datetime
1131
1132 def test_basic_attributes(self):
1133 dt = self.theclass(2002, 3, 1, 12, 0)
1134 self.assertEqual(dt.year, 2002)
1135 self.assertEqual(dt.month, 3)
1136 self.assertEqual(dt.day, 1)
1137 self.assertEqual(dt.hour, 12)
1138 self.assertEqual(dt.minute, 0)
1139 self.assertEqual(dt.second, 0)
1140 self.assertEqual(dt.microsecond, 0)
1141
1142 def test_basic_attributes_nonzero(self):
1143 # Make sure all attributes are non-zero so bugs in
1144 # bit-shifting access show up.
1145 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1146 self.assertEqual(dt.year, 2002)
1147 self.assertEqual(dt.month, 3)
1148 self.assertEqual(dt.day, 1)
1149 self.assertEqual(dt.hour, 12)
1150 self.assertEqual(dt.minute, 59)
1151 self.assertEqual(dt.second, 59)
1152 self.assertEqual(dt.microsecond, 8000)
1153
1154 def test_roundtrip(self):
1155 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1156 self.theclass.now()):
1157 # Verify dt -> string -> datetime identity.
1158 s = repr(dt)
Georg Brandlab91fde2009-08-13 08:51:18 +00001159 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001160 s = s[9:]
1161 dt2 = eval(s)
1162 self.assertEqual(dt, dt2)
1163
1164 # Verify identity via reconstructing from pieces.
1165 dt2 = self.theclass(dt.year, dt.month, dt.day,
1166 dt.hour, dt.minute, dt.second,
1167 dt.microsecond)
1168 self.assertEqual(dt, dt2)
1169
1170 def test_isoformat(self):
1171 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1172 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1173 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1174 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1175 # str is ISO format with the separator forced to a blank.
1176 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1177
1178 t = self.theclass(2, 3, 2)
1179 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1180 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1181 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1182 # str is ISO format with the separator forced to a blank.
1183 self.assertEqual(str(t), "0002-03-02 00:00:00")
1184
Eric Smith1ba31142007-09-11 18:06:02 +00001185 def test_format(self):
1186 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001187 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001188
1189 # check that a derived class's __str__() gets called
1190 class A(self.theclass):
1191 def __str__(self):
1192 return 'A'
1193 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001194 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001195
1196 # check that a derived class's strftime gets called
1197 class B(self.theclass):
1198 def strftime(self, format_spec):
1199 return 'B'
1200 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001201 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001202
1203 for fmt in ["m:%m d:%d y:%y",
1204 "m:%m d:%d y:%y H:%H M:%M S:%S",
1205 "%z %Z",
1206 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001207 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1208 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1209 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001210
Tim Peters2a799bf2002-12-16 20:18:38 +00001211 def test_more_ctime(self):
1212 # Test fields that TestDate doesn't touch.
1213 import time
1214
1215 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1216 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1217 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1218 # out. The difference is that t.ctime() produces " 2" for the day,
1219 # but platform ctime() produces "02" for the day. According to
1220 # C99, t.ctime() is correct here.
1221 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1222
1223 # So test a case where that difference doesn't matter.
1224 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1225 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1226
1227 def test_tz_independent_comparing(self):
1228 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1229 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1230 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1231 self.assertEqual(dt1, dt3)
Georg Brandlab91fde2009-08-13 08:51:18 +00001232 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001233
1234 # Make sure comparison doesn't forget microseconds, and isn't done
1235 # via comparing a float timestamp (an IEEE double doesn't have enough
1236 # precision to span microsecond resolution across years 1 thru 9999,
1237 # so comparing via timestamp necessarily calls some distinct values
1238 # equal).
1239 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1240 us = timedelta(microseconds=1)
1241 dt2 = dt1 + us
1242 self.assertEqual(dt2 - dt1, us)
Georg Brandlab91fde2009-08-13 08:51:18 +00001243 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001244
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001245 def test_strftime_with_bad_tzname_replace(self):
1246 # verify ok if tzinfo.tzname().replace() returns a non-string
1247 class MyTzInfo(FixedOffset):
1248 def tzname(self, dt):
1249 class MyStr(str):
1250 def replace(self, *args):
1251 return None
1252 return MyStr('name')
1253 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1254 self.assertRaises(TypeError, t.strftime, '%Z')
1255
Tim Peters2a799bf2002-12-16 20:18:38 +00001256 def test_bad_constructor_arguments(self):
1257 # bad years
1258 self.theclass(MINYEAR, 1, 1) # no exception
1259 self.theclass(MAXYEAR, 1, 1) # no exception
1260 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1261 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1262 # bad months
1263 self.theclass(2000, 1, 1) # no exception
1264 self.theclass(2000, 12, 1) # no exception
1265 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1266 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1267 # bad days
1268 self.theclass(2000, 2, 29) # no exception
1269 self.theclass(2004, 2, 29) # no exception
1270 self.theclass(2400, 2, 29) # no exception
1271 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1272 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1273 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1274 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1275 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1276 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1277 # bad hours
1278 self.theclass(2000, 1, 31, 0) # no exception
1279 self.theclass(2000, 1, 31, 23) # no exception
1280 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1281 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1282 # bad minutes
1283 self.theclass(2000, 1, 31, 23, 0) # no exception
1284 self.theclass(2000, 1, 31, 23, 59) # no exception
1285 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1286 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1287 # bad seconds
1288 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1289 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1290 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1291 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1292 # bad microseconds
1293 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1294 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1295 self.assertRaises(ValueError, self.theclass,
1296 2000, 1, 31, 23, 59, 59, -1)
1297 self.assertRaises(ValueError, self.theclass,
1298 2000, 1, 31, 23, 59, 59,
1299 1000000)
1300
1301 def test_hash_equality(self):
1302 d = self.theclass(2000, 12, 31, 23, 30, 17)
1303 e = self.theclass(2000, 12, 31, 23, 30, 17)
1304 self.assertEqual(d, e)
1305 self.assertEqual(hash(d), hash(e))
1306
1307 dic = {d: 1}
1308 dic[e] = 2
1309 self.assertEqual(len(dic), 1)
1310 self.assertEqual(dic[d], 2)
1311 self.assertEqual(dic[e], 2)
1312
1313 d = self.theclass(2001, 1, 1, 0, 5, 17)
1314 e = self.theclass(2001, 1, 1, 0, 5, 17)
1315 self.assertEqual(d, e)
1316 self.assertEqual(hash(d), hash(e))
1317
1318 dic = {d: 1}
1319 dic[e] = 2
1320 self.assertEqual(len(dic), 1)
1321 self.assertEqual(dic[d], 2)
1322 self.assertEqual(dic[e], 2)
1323
1324 def test_computations(self):
1325 a = self.theclass(2002, 1, 31)
1326 b = self.theclass(1956, 1, 31)
1327 diff = a-b
1328 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1329 self.assertEqual(diff.seconds, 0)
1330 self.assertEqual(diff.microseconds, 0)
1331 a = self.theclass(2002, 3, 2, 17, 6)
1332 millisec = timedelta(0, 0, 1000)
1333 hour = timedelta(0, 3600)
1334 day = timedelta(1)
1335 week = timedelta(7)
1336 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1337 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1338 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1339 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1340 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1341 self.assertEqual(a - hour, a + -hour)
1342 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1343 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1344 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1345 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1346 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1347 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1348 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1349 self.assertEqual((a + week) - a, week)
1350 self.assertEqual((a + day) - a, day)
1351 self.assertEqual((a + hour) - a, hour)
1352 self.assertEqual((a + millisec) - a, millisec)
1353 self.assertEqual((a - week) - a, -week)
1354 self.assertEqual((a - day) - a, -day)
1355 self.assertEqual((a - hour) - a, -hour)
1356 self.assertEqual((a - millisec) - a, -millisec)
1357 self.assertEqual(a - (a + week), -week)
1358 self.assertEqual(a - (a + day), -day)
1359 self.assertEqual(a - (a + hour), -hour)
1360 self.assertEqual(a - (a + millisec), -millisec)
1361 self.assertEqual(a - (a - week), week)
1362 self.assertEqual(a - (a - day), day)
1363 self.assertEqual(a - (a - hour), hour)
1364 self.assertEqual(a - (a - millisec), millisec)
1365 self.assertEqual(a + (week + day + hour + millisec),
1366 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1367 self.assertEqual(a + (week + day + hour + millisec),
1368 (((a + week) + day) + hour) + millisec)
1369 self.assertEqual(a - (week + day + hour + millisec),
1370 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1371 self.assertEqual(a - (week + day + hour + millisec),
1372 (((a - week) - day) - hour) - millisec)
1373 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001374 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001375 self.assertRaises(TypeError, lambda: a+i)
1376 self.assertRaises(TypeError, lambda: a-i)
1377 self.assertRaises(TypeError, lambda: i+a)
1378 self.assertRaises(TypeError, lambda: i-a)
1379
1380 # delta - datetime is senseless.
1381 self.assertRaises(TypeError, lambda: day - a)
1382 # mixing datetime and (delta or datetime) via * or // is senseless
1383 self.assertRaises(TypeError, lambda: day * a)
1384 self.assertRaises(TypeError, lambda: a * day)
1385 self.assertRaises(TypeError, lambda: day // a)
1386 self.assertRaises(TypeError, lambda: a // day)
1387 self.assertRaises(TypeError, lambda: a * a)
1388 self.assertRaises(TypeError, lambda: a // a)
1389 # datetime + datetime is senseless
1390 self.assertRaises(TypeError, lambda: a + a)
1391
1392 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001393 args = 6, 7, 23, 20, 59, 1, 64**2
1394 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001395 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001396 green = pickler.dumps(orig, proto)
1397 derived = unpickler.loads(green)
1398 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001399
Guido van Rossum275666f2003-02-07 21:49:01 +00001400 def test_more_pickling(self):
1401 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1402 s = pickle.dumps(a)
1403 b = pickle.loads(s)
1404 self.assertEqual(b.year, 2003)
1405 self.assertEqual(b.month, 2)
1406 self.assertEqual(b.day, 7)
1407
Tim Peters604c0132004-06-07 23:04:33 +00001408 def test_pickling_subclass_datetime(self):
1409 args = 6, 7, 23, 20, 59, 1, 64**2
1410 orig = SubclassDatetime(*args)
1411 for pickler, unpickler, proto in pickle_choices:
1412 green = pickler.dumps(orig, proto)
1413 derived = unpickler.loads(green)
1414 self.assertEqual(orig, derived)
1415
Tim Peters2a799bf2002-12-16 20:18:38 +00001416 def test_more_compare(self):
1417 # The test_compare() inherited from TestDate covers the error cases.
1418 # We just want to test lexicographic ordering on the members datetime
1419 # has that date lacks.
1420 args = [2000, 11, 29, 20, 58, 16, 999998]
1421 t1 = self.theclass(*args)
1422 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001423 self.assertEqual(t1, t2)
Georg Brandlab91fde2009-08-13 08:51:18 +00001424 self.assertTrue(t1 <= t2)
1425 self.assertTrue(t1 >= t2)
1426 self.assertTrue(not t1 != t2)
1427 self.assertTrue(not t1 < t2)
1428 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001429
1430 for i in range(len(args)):
1431 newargs = args[:]
1432 newargs[i] = args[i] + 1
1433 t2 = self.theclass(*newargs) # this is larger than t1
Georg Brandlab91fde2009-08-13 08:51:18 +00001434 self.assertTrue(t1 < t2)
1435 self.assertTrue(t2 > t1)
1436 self.assertTrue(t1 <= t2)
1437 self.assertTrue(t2 >= t1)
1438 self.assertTrue(t1 != t2)
1439 self.assertTrue(t2 != t1)
1440 self.assertTrue(not t1 == t2)
1441 self.assertTrue(not t2 == t1)
1442 self.assertTrue(not t1 > t2)
1443 self.assertTrue(not t2 < t1)
1444 self.assertTrue(not t1 >= t2)
1445 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001446
1447
1448 # A helper for timestamp constructor tests.
1449 def verify_field_equality(self, expected, got):
1450 self.assertEqual(expected.tm_year, got.year)
1451 self.assertEqual(expected.tm_mon, got.month)
1452 self.assertEqual(expected.tm_mday, got.day)
1453 self.assertEqual(expected.tm_hour, got.hour)
1454 self.assertEqual(expected.tm_min, got.minute)
1455 self.assertEqual(expected.tm_sec, got.second)
1456
1457 def test_fromtimestamp(self):
1458 import time
1459
1460 ts = time.time()
1461 expected = time.localtime(ts)
1462 got = self.theclass.fromtimestamp(ts)
1463 self.verify_field_equality(expected, got)
1464
1465 def test_utcfromtimestamp(self):
1466 import time
1467
1468 ts = time.time()
1469 expected = time.gmtime(ts)
1470 got = self.theclass.utcfromtimestamp(ts)
1471 self.verify_field_equality(expected, got)
1472
Thomas Wouters477c8d52006-05-27 19:21:47 +00001473 def test_microsecond_rounding(self):
1474 # Test whether fromtimestamp "rounds up" floats that are less
1475 # than one microsecond smaller than an integer.
1476 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1477 self.theclass.fromtimestamp(1))
1478
Tim Peters1b6f7a92004-06-20 02:50:16 +00001479 def test_insane_fromtimestamp(self):
1480 # It's possible that some platform maps time_t to double,
1481 # and that this test will fail there. This test should
1482 # exempt such platforms (provided they return reasonable
1483 # results!).
1484 for insane in -1e200, 1e200:
1485 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1486 insane)
1487
1488 def test_insane_utcfromtimestamp(self):
1489 # It's possible that some platform maps time_t to double,
1490 # and that this test will fail there. This test should
1491 # exempt such platforms (provided they return reasonable
1492 # results!).
1493 for insane in -1e200, 1e200:
1494 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1495 insane)
Alexander Belopolsky5ca31ed2010-05-31 16:21:02 +00001496 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001497 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001498 # The result is tz-dependent; at least test that this doesn't
1499 # fail (like it did before bug 1646728 was fixed).
1500 self.theclass.fromtimestamp(-1.05)
1501
Alexander Belopolsky5ca31ed2010-05-31 16:21:02 +00001502 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001503 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001504 d = self.theclass.utcfromtimestamp(-1.05)
1505 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1506
Tim Peters2a799bf2002-12-16 20:18:38 +00001507 def test_utcnow(self):
1508 import time
1509
1510 # Call it a success if utcnow() and utcfromtimestamp() are within
1511 # a second of each other.
1512 tolerance = timedelta(seconds=1)
1513 for dummy in range(3):
1514 from_now = self.theclass.utcnow()
1515 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1516 if abs(from_timestamp - from_now) <= tolerance:
1517 break
1518 # Else try again a few times.
Georg Brandlab91fde2009-08-13 08:51:18 +00001519 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001520
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001521 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001522 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001523
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001524 string = '2004-12-01 13:02:47.197'
1525 format = '%Y-%m-%d %H:%M:%S.%f'
1526 result, frac = _strptime._strptime(string, format)
1527 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001528 got = self.theclass.strptime(string, format)
1529 self.assertEqual(expected, got)
1530
Tim Peters2a799bf2002-12-16 20:18:38 +00001531 def test_more_timetuple(self):
1532 # This tests fields beyond those tested by the TestDate.test_timetuple.
1533 t = self.theclass(2004, 12, 31, 6, 22, 33)
1534 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1535 self.assertEqual(t.timetuple(),
1536 (t.year, t.month, t.day,
1537 t.hour, t.minute, t.second,
1538 t.weekday(),
1539 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1540 -1))
1541 tt = t.timetuple()
1542 self.assertEqual(tt.tm_year, t.year)
1543 self.assertEqual(tt.tm_mon, t.month)
1544 self.assertEqual(tt.tm_mday, t.day)
1545 self.assertEqual(tt.tm_hour, t.hour)
1546 self.assertEqual(tt.tm_min, t.minute)
1547 self.assertEqual(tt.tm_sec, t.second)
1548 self.assertEqual(tt.tm_wday, t.weekday())
1549 self.assertEqual(tt.tm_yday, t.toordinal() -
1550 date(t.year, 1, 1).toordinal() + 1)
1551 self.assertEqual(tt.tm_isdst, -1)
1552
1553 def test_more_strftime(self):
1554 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001555 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1556 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1557 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001558
1559 def test_extract(self):
1560 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1561 self.assertEqual(dt.date(), date(2002, 3, 4))
1562 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1563
1564 def test_combine(self):
1565 d = date(2002, 3, 4)
1566 t = time(18, 45, 3, 1234)
1567 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1568 combine = self.theclass.combine
1569 dt = combine(d, t)
1570 self.assertEqual(dt, expected)
1571
1572 dt = combine(time=t, date=d)
1573 self.assertEqual(dt, expected)
1574
1575 self.assertEqual(d, dt.date())
1576 self.assertEqual(t, dt.time())
1577 self.assertEqual(dt, combine(dt.date(), dt.time()))
1578
1579 self.assertRaises(TypeError, combine) # need an arg
1580 self.assertRaises(TypeError, combine, d) # need two args
1581 self.assertRaises(TypeError, combine, t, d) # args reversed
1582 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1583 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1584
Tim Peters12bf3392002-12-24 05:41:27 +00001585 def test_replace(self):
1586 cls = self.theclass
1587 args = [1, 2, 3, 4, 5, 6, 7]
1588 base = cls(*args)
1589 self.assertEqual(base, base.replace())
1590
1591 i = 0
1592 for name, newval in (("year", 2),
1593 ("month", 3),
1594 ("day", 4),
1595 ("hour", 5),
1596 ("minute", 6),
1597 ("second", 7),
1598 ("microsecond", 8)):
1599 newargs = args[:]
1600 newargs[i] = newval
1601 expected = cls(*newargs)
1602 got = base.replace(**{name: newval})
1603 self.assertEqual(expected, got)
1604 i += 1
1605
1606 # Out of bounds.
1607 base = cls(2000, 2, 29)
1608 self.assertRaises(ValueError, base.replace, year=2001)
1609
Tim Peters80475bb2002-12-25 07:40:55 +00001610 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001611 # Pretty boring! The TZ test is more interesting here. astimezone()
1612 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001613 dt = self.theclass.now()
1614 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001615 self.assertRaises(TypeError, dt.astimezone) # not enough args
1616 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1617 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001618 self.assertRaises(ValueError, dt.astimezone, f) # naive
1619 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001620
Tim Peters52dcce22003-01-23 16:36:11 +00001621 class Bogus(tzinfo):
1622 def utcoffset(self, dt): return None
1623 def dst(self, dt): return timedelta(0)
1624 bog = Bogus()
1625 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1626
1627 class AlsoBogus(tzinfo):
1628 def utcoffset(self, dt): return timedelta(0)
1629 def dst(self, dt): return None
1630 alsobog = AlsoBogus()
1631 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001632
Tim Petersa98924a2003-05-17 05:55:19 +00001633 def test_subclass_datetime(self):
1634
1635 class C(self.theclass):
1636 theAnswer = 42
1637
1638 def __new__(cls, *args, **kws):
1639 temp = kws.copy()
1640 extra = temp.pop('extra')
1641 result = self.theclass.__new__(cls, *args, **temp)
1642 result.extra = extra
1643 return result
1644
1645 def newmeth(self, start):
1646 return start + self.year + self.month + self.second
1647
1648 args = 2003, 4, 14, 12, 13, 41
1649
1650 dt1 = self.theclass(*args)
1651 dt2 = C(*args, **{'extra': 7})
1652
1653 self.assertEqual(dt2.__class__, C)
1654 self.assertEqual(dt2.theAnswer, 42)
1655 self.assertEqual(dt2.extra, 7)
1656 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1657 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1658 dt1.second - 7)
1659
Tim Peters604c0132004-06-07 23:04:33 +00001660class SubclassTime(time):
1661 sub_var = 1
1662
Guido van Rossumd8faa362007-04-27 19:54:29 +00001663class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001664
1665 theclass = time
1666
1667 def test_basic_attributes(self):
1668 t = self.theclass(12, 0)
1669 self.assertEqual(t.hour, 12)
1670 self.assertEqual(t.minute, 0)
1671 self.assertEqual(t.second, 0)
1672 self.assertEqual(t.microsecond, 0)
1673
1674 def test_basic_attributes_nonzero(self):
1675 # Make sure all attributes are non-zero so bugs in
1676 # bit-shifting access show up.
1677 t = self.theclass(12, 59, 59, 8000)
1678 self.assertEqual(t.hour, 12)
1679 self.assertEqual(t.minute, 59)
1680 self.assertEqual(t.second, 59)
1681 self.assertEqual(t.microsecond, 8000)
1682
1683 def test_roundtrip(self):
1684 t = self.theclass(1, 2, 3, 4)
1685
1686 # Verify t -> string -> time identity.
1687 s = repr(t)
Georg Brandlab91fde2009-08-13 08:51:18 +00001688 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001689 s = s[9:]
1690 t2 = eval(s)
1691 self.assertEqual(t, t2)
1692
1693 # Verify identity via reconstructing from pieces.
1694 t2 = self.theclass(t.hour, t.minute, t.second,
1695 t.microsecond)
1696 self.assertEqual(t, t2)
1697
1698 def test_comparing(self):
1699 args = [1, 2, 3, 4]
1700 t1 = self.theclass(*args)
1701 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001702 self.assertEqual(t1, t2)
Georg Brandlab91fde2009-08-13 08:51:18 +00001703 self.assertTrue(t1 <= t2)
1704 self.assertTrue(t1 >= t2)
1705 self.assertTrue(not t1 != t2)
1706 self.assertTrue(not t1 < t2)
1707 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001708
1709 for i in range(len(args)):
1710 newargs = args[:]
1711 newargs[i] = args[i] + 1
1712 t2 = self.theclass(*newargs) # this is larger than t1
Georg Brandlab91fde2009-08-13 08:51:18 +00001713 self.assertTrue(t1 < t2)
1714 self.assertTrue(t2 > t1)
1715 self.assertTrue(t1 <= t2)
1716 self.assertTrue(t2 >= t1)
1717 self.assertTrue(t1 != t2)
1718 self.assertTrue(t2 != t1)
1719 self.assertTrue(not t1 == t2)
1720 self.assertTrue(not t2 == t1)
1721 self.assertTrue(not t1 > t2)
1722 self.assertTrue(not t2 < t1)
1723 self.assertTrue(not t1 >= t2)
1724 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001725
Tim Peters68124bb2003-02-08 03:46:31 +00001726 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001727 self.assertEqual(t1 == badarg, False)
1728 self.assertEqual(t1 != badarg, True)
1729 self.assertEqual(badarg == t1, False)
1730 self.assertEqual(badarg != t1, True)
1731
Tim Peters2a799bf2002-12-16 20:18:38 +00001732 self.assertRaises(TypeError, lambda: t1 <= badarg)
1733 self.assertRaises(TypeError, lambda: t1 < badarg)
1734 self.assertRaises(TypeError, lambda: t1 > badarg)
1735 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001736 self.assertRaises(TypeError, lambda: badarg <= t1)
1737 self.assertRaises(TypeError, lambda: badarg < t1)
1738 self.assertRaises(TypeError, lambda: badarg > t1)
1739 self.assertRaises(TypeError, lambda: badarg >= t1)
1740
1741 def test_bad_constructor_arguments(self):
1742 # bad hours
1743 self.theclass(0, 0) # no exception
1744 self.theclass(23, 0) # no exception
1745 self.assertRaises(ValueError, self.theclass, -1, 0)
1746 self.assertRaises(ValueError, self.theclass, 24, 0)
1747 # bad minutes
1748 self.theclass(23, 0) # no exception
1749 self.theclass(23, 59) # no exception
1750 self.assertRaises(ValueError, self.theclass, 23, -1)
1751 self.assertRaises(ValueError, self.theclass, 23, 60)
1752 # bad seconds
1753 self.theclass(23, 59, 0) # no exception
1754 self.theclass(23, 59, 59) # no exception
1755 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1756 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1757 # bad microseconds
1758 self.theclass(23, 59, 59, 0) # no exception
1759 self.theclass(23, 59, 59, 999999) # no exception
1760 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1761 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1762
1763 def test_hash_equality(self):
1764 d = self.theclass(23, 30, 17)
1765 e = self.theclass(23, 30, 17)
1766 self.assertEqual(d, e)
1767 self.assertEqual(hash(d), hash(e))
1768
1769 dic = {d: 1}
1770 dic[e] = 2
1771 self.assertEqual(len(dic), 1)
1772 self.assertEqual(dic[d], 2)
1773 self.assertEqual(dic[e], 2)
1774
1775 d = self.theclass(0, 5, 17)
1776 e = self.theclass(0, 5, 17)
1777 self.assertEqual(d, e)
1778 self.assertEqual(hash(d), hash(e))
1779
1780 dic = {d: 1}
1781 dic[e] = 2
1782 self.assertEqual(len(dic), 1)
1783 self.assertEqual(dic[d], 2)
1784 self.assertEqual(dic[e], 2)
1785
1786 def test_isoformat(self):
1787 t = self.theclass(4, 5, 1, 123)
1788 self.assertEqual(t.isoformat(), "04:05:01.000123")
1789 self.assertEqual(t.isoformat(), str(t))
1790
1791 t = self.theclass()
1792 self.assertEqual(t.isoformat(), "00:00:00")
1793 self.assertEqual(t.isoformat(), str(t))
1794
1795 t = self.theclass(microsecond=1)
1796 self.assertEqual(t.isoformat(), "00:00:00.000001")
1797 self.assertEqual(t.isoformat(), str(t))
1798
1799 t = self.theclass(microsecond=10)
1800 self.assertEqual(t.isoformat(), "00:00:00.000010")
1801 self.assertEqual(t.isoformat(), str(t))
1802
1803 t = self.theclass(microsecond=100)
1804 self.assertEqual(t.isoformat(), "00:00:00.000100")
1805 self.assertEqual(t.isoformat(), str(t))
1806
1807 t = self.theclass(microsecond=1000)
1808 self.assertEqual(t.isoformat(), "00:00:00.001000")
1809 self.assertEqual(t.isoformat(), str(t))
1810
1811 t = self.theclass(microsecond=10000)
1812 self.assertEqual(t.isoformat(), "00:00:00.010000")
1813 self.assertEqual(t.isoformat(), str(t))
1814
1815 t = self.theclass(microsecond=100000)
1816 self.assertEqual(t.isoformat(), "00:00:00.100000")
1817 self.assertEqual(t.isoformat(), str(t))
1818
Thomas Wouterscf297e42007-02-23 15:07:44 +00001819 def test_1653736(self):
1820 # verify it doesn't accept extra keyword arguments
1821 t = self.theclass(second=1)
1822 self.assertRaises(TypeError, t.isoformat, foo=3)
1823
Tim Peters2a799bf2002-12-16 20:18:38 +00001824 def test_strftime(self):
1825 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001826 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001827 # A naive object replaces %z and %Z with empty strings.
1828 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1829
Eric Smith1ba31142007-09-11 18:06:02 +00001830 def test_format(self):
1831 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001832 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001833
1834 # check that a derived class's __str__() gets called
1835 class A(self.theclass):
1836 def __str__(self):
1837 return 'A'
1838 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001839 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001840
1841 # check that a derived class's strftime gets called
1842 class B(self.theclass):
1843 def strftime(self, format_spec):
1844 return 'B'
1845 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001846 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001847
1848 for fmt in ['%H %M %S',
1849 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001850 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1851 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1852 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001853
Tim Peters2a799bf2002-12-16 20:18:38 +00001854 def test_str(self):
1855 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1856 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1857 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1858 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1859 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1860
1861 def test_repr(self):
1862 name = 'datetime.' + self.theclass.__name__
1863 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1864 "%s(1, 2, 3, 4)" % name)
1865 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1866 "%s(10, 2, 3, 4000)" % name)
1867 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1868 "%s(0, 2, 3, 400000)" % name)
1869 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1870 "%s(12, 2, 3)" % name)
1871 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1872 "%s(23, 15)" % name)
1873
1874 def test_resolution_info(self):
Georg Brandlab91fde2009-08-13 08:51:18 +00001875 self.assertTrue(isinstance(self.theclass.min, self.theclass))
1876 self.assertTrue(isinstance(self.theclass.max, self.theclass))
1877 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
1878 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001879
1880 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001881 args = 20, 59, 16, 64**2
1882 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001883 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001884 green = pickler.dumps(orig, proto)
1885 derived = unpickler.loads(green)
1886 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001887
Tim Peters604c0132004-06-07 23:04:33 +00001888 def test_pickling_subclass_time(self):
1889 args = 20, 59, 16, 64**2
1890 orig = SubclassTime(*args)
1891 for pickler, unpickler, proto in pickle_choices:
1892 green = pickler.dumps(orig, proto)
1893 derived = unpickler.loads(green)
1894 self.assertEqual(orig, derived)
1895
Tim Peters2a799bf2002-12-16 20:18:38 +00001896 def test_bool(self):
1897 cls = self.theclass
Georg Brandlab91fde2009-08-13 08:51:18 +00001898 self.assertTrue(cls(1))
1899 self.assertTrue(cls(0, 1))
1900 self.assertTrue(cls(0, 0, 1))
1901 self.assertTrue(cls(0, 0, 0, 1))
1902 self.assertTrue(not cls(0))
1903 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001904
Tim Peters12bf3392002-12-24 05:41:27 +00001905 def test_replace(self):
1906 cls = self.theclass
1907 args = [1, 2, 3, 4]
1908 base = cls(*args)
1909 self.assertEqual(base, base.replace())
1910
1911 i = 0
1912 for name, newval in (("hour", 5),
1913 ("minute", 6),
1914 ("second", 7),
1915 ("microsecond", 8)):
1916 newargs = args[:]
1917 newargs[i] = newval
1918 expected = cls(*newargs)
1919 got = base.replace(**{name: newval})
1920 self.assertEqual(expected, got)
1921 i += 1
1922
1923 # Out of bounds.
1924 base = cls(1)
1925 self.assertRaises(ValueError, base.replace, hour=24)
1926 self.assertRaises(ValueError, base.replace, minute=-1)
1927 self.assertRaises(ValueError, base.replace, second=100)
1928 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1929
Tim Petersa98924a2003-05-17 05:55:19 +00001930 def test_subclass_time(self):
1931
1932 class C(self.theclass):
1933 theAnswer = 42
1934
1935 def __new__(cls, *args, **kws):
1936 temp = kws.copy()
1937 extra = temp.pop('extra')
1938 result = self.theclass.__new__(cls, *args, **temp)
1939 result.extra = extra
1940 return result
1941
1942 def newmeth(self, start):
1943 return start + self.hour + self.second
1944
1945 args = 4, 5, 6
1946
1947 dt1 = self.theclass(*args)
1948 dt2 = C(*args, **{'extra': 7})
1949
1950 self.assertEqual(dt2.__class__, C)
1951 self.assertEqual(dt2.theAnswer, 42)
1952 self.assertEqual(dt2.extra, 7)
1953 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1954 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1955
Armin Rigof4afb212005-11-07 07:15:48 +00001956 def test_backdoor_resistance(self):
1957 # see TestDate.test_backdoor_resistance().
1958 base = '2:59.0'
1959 for hour_byte in ' ', '9', chr(24), '\xff':
1960 self.assertRaises(TypeError, self.theclass,
1961 hour_byte + base[1:])
1962
Tim Peters855fe882002-12-22 03:43:39 +00001963# A mixin for classes with a tzinfo= argument. Subclasses must define
1964# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001965# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001966class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001967
Tim Petersbad8ff02002-12-30 20:52:32 +00001968 def test_argument_passing(self):
1969 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001970 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001971 class introspective(tzinfo):
1972 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001973 def utcoffset(self, dt):
1974 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001975 dst = utcoffset
1976
1977 obj = cls(1, 2, 3, tzinfo=introspective())
1978
Tim Peters0bf60bd2003-01-08 20:40:01 +00001979 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001980 self.assertEqual(obj.tzname(), expected)
1981
Tim Peters0bf60bd2003-01-08 20:40:01 +00001982 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001983 self.assertEqual(obj.utcoffset(), expected)
1984 self.assertEqual(obj.dst(), expected)
1985
Tim Peters855fe882002-12-22 03:43:39 +00001986 def test_bad_tzinfo_classes(self):
1987 cls = self.theclass
1988 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00001989
Tim Peters855fe882002-12-22 03:43:39 +00001990 class NiceTry(object):
1991 def __init__(self): pass
1992 def utcoffset(self, dt): pass
1993 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
1994
1995 class BetterTry(tzinfo):
1996 def __init__(self): pass
1997 def utcoffset(self, dt): pass
1998 b = BetterTry()
1999 t = cls(1, 1, 1, tzinfo=b)
Georg Brandlab91fde2009-08-13 08:51:18 +00002000 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002001
2002 def test_utc_offset_out_of_bounds(self):
2003 class Edgy(tzinfo):
2004 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002005 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002006 def utcoffset(self, dt):
2007 return self.offset
2008
2009 cls = self.theclass
2010 for offset, legit in ((-1440, False),
2011 (-1439, True),
2012 (1439, True),
2013 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002014 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002015 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002016 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002017 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002018 else:
2019 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002020 if legit:
2021 aofs = abs(offset)
2022 h, m = divmod(aofs, 60)
2023 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002024 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002025 t = t.timetz()
2026 self.assertEqual(str(t), "01:02:03" + tag)
2027 else:
2028 self.assertRaises(ValueError, str, t)
2029
2030 def test_tzinfo_classes(self):
2031 cls = self.theclass
2032 class C1(tzinfo):
2033 def utcoffset(self, dt): return None
2034 def dst(self, dt): return None
2035 def tzname(self, dt): return None
2036 for t in (cls(1, 1, 1),
2037 cls(1, 1, 1, tzinfo=None),
2038 cls(1, 1, 1, tzinfo=C1())):
Georg Brandlab91fde2009-08-13 08:51:18 +00002039 self.assertTrue(t.utcoffset() is None)
2040 self.assertTrue(t.dst() is None)
2041 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002042
Tim Peters855fe882002-12-22 03:43:39 +00002043 class C3(tzinfo):
2044 def utcoffset(self, dt): return timedelta(minutes=-1439)
2045 def dst(self, dt): return timedelta(minutes=1439)
2046 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002047 t = cls(1, 1, 1, tzinfo=C3())
2048 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2049 self.assertEqual(t.dst(), timedelta(minutes=1439))
2050 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002051
2052 # Wrong types.
2053 class C4(tzinfo):
2054 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002055 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002056 def tzname(self, dt): return 0
2057 t = cls(1, 1, 1, tzinfo=C4())
2058 self.assertRaises(TypeError, t.utcoffset)
2059 self.assertRaises(TypeError, t.dst)
2060 self.assertRaises(TypeError, t.tzname)
2061
2062 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002063 class C6(tzinfo):
2064 def utcoffset(self, dt): return timedelta(hours=-24)
2065 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002066 t = cls(1, 1, 1, tzinfo=C6())
2067 self.assertRaises(ValueError, t.utcoffset)
2068 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002069
2070 # Not a whole number of minutes.
2071 class C7(tzinfo):
2072 def utcoffset(self, dt): return timedelta(seconds=61)
2073 def dst(self, dt): return timedelta(microseconds=-81)
2074 t = cls(1, 1, 1, tzinfo=C7())
2075 self.assertRaises(ValueError, t.utcoffset)
2076 self.assertRaises(ValueError, t.dst)
2077
Tim Peters4c0db782002-12-26 05:01:19 +00002078 def test_aware_compare(self):
2079 cls = self.theclass
2080
Tim Peters60c76e42002-12-27 00:41:11 +00002081 # Ensure that utcoffset() gets ignored if the comparands have
2082 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002083 class OperandDependentOffset(tzinfo):
2084 def utcoffset(self, t):
2085 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002086 # d0 and d1 equal after adjustment
2087 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002088 else:
Tim Peters397301e2003-01-02 21:28:08 +00002089 # d2 off in the weeds
2090 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002091
2092 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2093 d0 = base.replace(minute=3)
2094 d1 = base.replace(minute=9)
2095 d2 = base.replace(minute=11)
2096 for x in d0, d1, d2:
2097 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002098 for op in lt, le, gt, ge, eq, ne:
2099 got = op(x, y)
2100 expected = op(x.minute, y.minute)
2101 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002102
2103 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002104 # Note that a time can't actually have an operand-depedent offset,
2105 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2106 # so skip this test for time.
2107 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002108 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2109 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2110 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2111 for x in d0, d1, d2:
2112 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002113 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002114 if (x is d0 or x is d1) and (y is d0 or y is d1):
2115 expected = 0
2116 elif x is y is d2:
2117 expected = 0
2118 elif x is d2:
2119 expected = -1
2120 else:
2121 assert y is d2
2122 expected = 1
2123 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002124
Tim Peters855fe882002-12-22 03:43:39 +00002125
Tim Peters0bf60bd2003-01-08 20:40:01 +00002126# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002127class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002128 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002129
2130 def test_empty(self):
2131 t = self.theclass()
2132 self.assertEqual(t.hour, 0)
2133 self.assertEqual(t.minute, 0)
2134 self.assertEqual(t.second, 0)
2135 self.assertEqual(t.microsecond, 0)
Georg Brandlab91fde2009-08-13 08:51:18 +00002136 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002137
Tim Peters2a799bf2002-12-16 20:18:38 +00002138 def test_zones(self):
2139 est = FixedOffset(-300, "EST", 1)
2140 utc = FixedOffset(0, "UTC", -2)
2141 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002142 t1 = time( 7, 47, tzinfo=est)
2143 t2 = time(12, 47, tzinfo=utc)
2144 t3 = time(13, 47, tzinfo=met)
2145 t4 = time(microsecond=40)
2146 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002147
2148 self.assertEqual(t1.tzinfo, est)
2149 self.assertEqual(t2.tzinfo, utc)
2150 self.assertEqual(t3.tzinfo, met)
Georg Brandlab91fde2009-08-13 08:51:18 +00002151 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002152 self.assertEqual(t5.tzinfo, utc)
2153
Tim Peters855fe882002-12-22 03:43:39 +00002154 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2155 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2156 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Georg Brandlab91fde2009-08-13 08:51:18 +00002157 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002158 self.assertRaises(TypeError, t1.utcoffset, "no args")
2159
2160 self.assertEqual(t1.tzname(), "EST")
2161 self.assertEqual(t2.tzname(), "UTC")
2162 self.assertEqual(t3.tzname(), "MET")
Georg Brandlab91fde2009-08-13 08:51:18 +00002163 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002164 self.assertRaises(TypeError, t1.tzname, "no args")
2165
Tim Peters855fe882002-12-22 03:43:39 +00002166 self.assertEqual(t1.dst(), timedelta(minutes=1))
2167 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2168 self.assertEqual(t3.dst(), timedelta(minutes=3))
Georg Brandlab91fde2009-08-13 08:51:18 +00002169 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002170 self.assertRaises(TypeError, t1.dst, "no args")
2171
2172 self.assertEqual(hash(t1), hash(t2))
2173 self.assertEqual(hash(t1), hash(t3))
2174 self.assertEqual(hash(t2), hash(t3))
2175
2176 self.assertEqual(t1, t2)
2177 self.assertEqual(t1, t3)
2178 self.assertEqual(t2, t3)
2179 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2180 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2181 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2182
2183 self.assertEqual(str(t1), "07:47:00-05:00")
2184 self.assertEqual(str(t2), "12:47:00+00:00")
2185 self.assertEqual(str(t3), "13:47:00+01:00")
2186 self.assertEqual(str(t4), "00:00:00.000040")
2187 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2188
2189 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2190 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2191 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2192 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2193 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2194
Tim Peters0bf60bd2003-01-08 20:40:01 +00002195 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002196 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2197 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2198 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2199 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2200 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2201
2202 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2203 "07:47:00 %Z=EST %z=-0500")
2204 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2205 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2206
2207 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002208 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002209 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2210 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2211
Tim Petersb92bb712002-12-21 17:44:07 +00002212 # Check that an invalid tzname result raises an exception.
2213 class Badtzname(tzinfo):
2214 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002215 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002216 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2217 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002218
2219 def test_hash_edge_cases(self):
2220 # Offsets that overflow a basic time.
2221 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2222 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2223 self.assertEqual(hash(t1), hash(t2))
2224
2225 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2226 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2227 self.assertEqual(hash(t1), hash(t2))
2228
Tim Peters2a799bf2002-12-16 20:18:38 +00002229 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002230 # Try one without a tzinfo.
2231 args = 20, 59, 16, 64**2
2232 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002233 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002234 green = pickler.dumps(orig, proto)
2235 derived = unpickler.loads(green)
2236 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002237
2238 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002239 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002240 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002241 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002242 green = pickler.dumps(orig, proto)
2243 derived = unpickler.loads(green)
2244 self.assertEqual(orig, derived)
Georg Brandlab91fde2009-08-13 08:51:18 +00002245 self.assertTrue(isinstance(derived.tzinfo, PicklableFixedOffset))
Tim Peters96940c92003-01-31 21:55:33 +00002246 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2247 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002248
2249 def test_more_bool(self):
2250 # Test cases with non-None tzinfo.
2251 cls = self.theclass
2252
2253 t = cls(0, tzinfo=FixedOffset(-300, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002254 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002255
2256 t = cls(5, tzinfo=FixedOffset(-300, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002257 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002258
2259 t = cls(5, tzinfo=FixedOffset(300, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002260 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002261
2262 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002263 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002264
2265 # Mostly ensuring this doesn't overflow internally.
2266 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002267 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002268
2269 # But this should yield a value error -- the utcoffset is bogus.
2270 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2271 self.assertRaises(ValueError, lambda: bool(t))
2272
2273 # Likewise.
2274 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2275 self.assertRaises(ValueError, lambda: bool(t))
2276
Tim Peters12bf3392002-12-24 05:41:27 +00002277 def test_replace(self):
2278 cls = self.theclass
2279 z100 = FixedOffset(100, "+100")
2280 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2281 args = [1, 2, 3, 4, z100]
2282 base = cls(*args)
2283 self.assertEqual(base, base.replace())
2284
2285 i = 0
2286 for name, newval in (("hour", 5),
2287 ("minute", 6),
2288 ("second", 7),
2289 ("microsecond", 8),
2290 ("tzinfo", zm200)):
2291 newargs = args[:]
2292 newargs[i] = newval
2293 expected = cls(*newargs)
2294 got = base.replace(**{name: newval})
2295 self.assertEqual(expected, got)
2296 i += 1
2297
2298 # Ensure we can get rid of a tzinfo.
2299 self.assertEqual(base.tzname(), "+100")
2300 base2 = base.replace(tzinfo=None)
Georg Brandlab91fde2009-08-13 08:51:18 +00002301 self.assertTrue(base2.tzinfo is None)
2302 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002303
2304 # Ensure we can add one.
2305 base3 = base2.replace(tzinfo=z100)
2306 self.assertEqual(base, base3)
Georg Brandlab91fde2009-08-13 08:51:18 +00002307 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002308
2309 # Out of bounds.
2310 base = cls(1)
2311 self.assertRaises(ValueError, base.replace, hour=24)
2312 self.assertRaises(ValueError, base.replace, minute=-1)
2313 self.assertRaises(ValueError, base.replace, second=100)
2314 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2315
Tim Peters60c76e42002-12-27 00:41:11 +00002316 def test_mixed_compare(self):
2317 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002318 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002319 self.assertEqual(t1, t2)
2320 t2 = t2.replace(tzinfo=None)
2321 self.assertEqual(t1, t2)
2322 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2323 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002324 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2325 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002326
Tim Peters0bf60bd2003-01-08 20:40:01 +00002327 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002328 class Varies(tzinfo):
2329 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002330 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002331 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002332 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002333 return self.offset
2334
2335 v = Varies()
2336 t1 = t2.replace(tzinfo=v)
2337 t2 = t2.replace(tzinfo=v)
2338 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2339 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2340 self.assertEqual(t1, t2)
2341
2342 # But if they're not identical, it isn't ignored.
2343 t2 = t2.replace(tzinfo=Varies())
Georg Brandlab91fde2009-08-13 08:51:18 +00002344 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002345
Tim Petersa98924a2003-05-17 05:55:19 +00002346 def test_subclass_timetz(self):
2347
2348 class C(self.theclass):
2349 theAnswer = 42
2350
2351 def __new__(cls, *args, **kws):
2352 temp = kws.copy()
2353 extra = temp.pop('extra')
2354 result = self.theclass.__new__(cls, *args, **temp)
2355 result.extra = extra
2356 return result
2357
2358 def newmeth(self, start):
2359 return start + self.hour + self.second
2360
2361 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2362
2363 dt1 = self.theclass(*args)
2364 dt2 = C(*args, **{'extra': 7})
2365
2366 self.assertEqual(dt2.__class__, C)
2367 self.assertEqual(dt2.theAnswer, 42)
2368 self.assertEqual(dt2.extra, 7)
2369 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2370 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2371
Tim Peters4c0db782002-12-26 05:01:19 +00002372
Tim Peters0bf60bd2003-01-08 20:40:01 +00002373# Testing datetime objects with a non-None tzinfo.
2374
Guido van Rossumd8faa362007-04-27 19:54:29 +00002375class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002376 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002377
2378 def test_trivial(self):
2379 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2380 self.assertEqual(dt.year, 1)
2381 self.assertEqual(dt.month, 2)
2382 self.assertEqual(dt.day, 3)
2383 self.assertEqual(dt.hour, 4)
2384 self.assertEqual(dt.minute, 5)
2385 self.assertEqual(dt.second, 6)
2386 self.assertEqual(dt.microsecond, 7)
2387 self.assertEqual(dt.tzinfo, None)
2388
2389 def test_even_more_compare(self):
2390 # The test_compare() and test_more_compare() inherited from TestDate
2391 # and TestDateTime covered non-tzinfo cases.
2392
2393 # Smallest possible after UTC adjustment.
2394 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2395 # Largest possible after UTC adjustment.
2396 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2397 tzinfo=FixedOffset(-1439, ""))
2398
2399 # Make sure those compare correctly, and w/o overflow.
Georg Brandlab91fde2009-08-13 08:51:18 +00002400 self.assertTrue(t1 < t2)
2401 self.assertTrue(t1 != t2)
2402 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002403
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002404 self.assertEqual(t1, t1)
2405 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002406
2407 # Equal afer adjustment.
2408 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2409 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2410 self.assertEqual(t1, t2)
2411
2412 # Change t1 not to subtract a minute, and t1 should be larger.
2413 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002414 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002415
2416 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2417 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002418 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002419
2420 # Back to the original t1, but make seconds resolve it.
2421 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2422 second=1)
Georg Brandlab91fde2009-08-13 08:51:18 +00002423 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002424
2425 # Likewise, but make microseconds resolve it.
2426 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2427 microsecond=1)
Georg Brandlab91fde2009-08-13 08:51:18 +00002428 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002429
2430 # Make t2 naive and it should fail.
2431 t2 = self.theclass.min
2432 self.assertRaises(TypeError, lambda: t1 == t2)
2433 self.assertEqual(t2, t2)
2434
2435 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2436 class Naive(tzinfo):
2437 def utcoffset(self, dt): return None
2438 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2439 self.assertRaises(TypeError, lambda: t1 == t2)
2440 self.assertEqual(t2, t2)
2441
2442 # OTOH, it's OK to compare two of these mixing the two ways of being
2443 # naive.
2444 t1 = self.theclass(5, 6, 7)
2445 self.assertEqual(t1, t2)
2446
2447 # Try a bogus uctoffset.
2448 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002449 def utcoffset(self, dt):
2450 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002451 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2452 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002453 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002454
Tim Peters2a799bf2002-12-16 20:18:38 +00002455 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002456 # Try one without a tzinfo.
2457 args = 6, 7, 23, 20, 59, 1, 64**2
2458 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002459 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002460 green = pickler.dumps(orig, proto)
2461 derived = unpickler.loads(green)
2462 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002463
2464 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002465 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002466 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002467 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002468 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002469 green = pickler.dumps(orig, proto)
2470 derived = unpickler.loads(green)
2471 self.assertEqual(orig, derived)
Georg Brandlab91fde2009-08-13 08:51:18 +00002472 self.assertTrue(isinstance(derived.tzinfo,
Tim Peters96940c92003-01-31 21:55:33 +00002473 PicklableFixedOffset))
2474 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2475 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002476
2477 def test_extreme_hashes(self):
2478 # If an attempt is made to hash these via subtracting the offset
2479 # then hashing a datetime object, OverflowError results. The
2480 # Python implementation used to blow up here.
2481 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2482 hash(t)
2483 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2484 tzinfo=FixedOffset(-1439, ""))
2485 hash(t)
2486
2487 # OTOH, an OOB offset should blow up.
2488 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2489 self.assertRaises(ValueError, hash, t)
2490
2491 def test_zones(self):
2492 est = FixedOffset(-300, "EST")
2493 utc = FixedOffset(0, "UTC")
2494 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002495 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2496 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2497 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002498 self.assertEqual(t1.tzinfo, est)
2499 self.assertEqual(t2.tzinfo, utc)
2500 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002501 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2502 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2503 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002504 self.assertEqual(t1.tzname(), "EST")
2505 self.assertEqual(t2.tzname(), "UTC")
2506 self.assertEqual(t3.tzname(), "MET")
2507 self.assertEqual(hash(t1), hash(t2))
2508 self.assertEqual(hash(t1), hash(t3))
2509 self.assertEqual(hash(t2), hash(t3))
2510 self.assertEqual(t1, t2)
2511 self.assertEqual(t1, t3)
2512 self.assertEqual(t2, t3)
2513 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2514 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2515 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002516 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002517 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2518 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2519 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2520
2521 def test_combine(self):
2522 met = FixedOffset(60, "MET")
2523 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002524 tz = time(18, 45, 3, 1234, tzinfo=met)
2525 dt = datetime.combine(d, tz)
2526 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002527 tzinfo=met))
2528
2529 def test_extract(self):
2530 met = FixedOffset(60, "MET")
2531 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2532 self.assertEqual(dt.date(), date(2002, 3, 4))
2533 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002534 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002535
2536 def test_tz_aware_arithmetic(self):
2537 import random
2538
2539 now = self.theclass.now()
2540 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002541 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002542 nowaware = self.theclass.combine(now.date(), timeaware)
Georg Brandlab91fde2009-08-13 08:51:18 +00002543 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002544 self.assertEqual(nowaware.timetz(), timeaware)
2545
2546 # Can't mix aware and non-aware.
2547 self.assertRaises(TypeError, lambda: now - nowaware)
2548 self.assertRaises(TypeError, lambda: nowaware - now)
2549
Tim Peters0bf60bd2003-01-08 20:40:01 +00002550 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002551 self.assertRaises(TypeError, lambda: now + nowaware)
2552 self.assertRaises(TypeError, lambda: nowaware + now)
2553 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2554
2555 # Subtracting should yield 0.
2556 self.assertEqual(now - now, timedelta(0))
2557 self.assertEqual(nowaware - nowaware, timedelta(0))
2558
2559 # Adding a delta should preserve tzinfo.
2560 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2561 nowawareplus = nowaware + delta
Georg Brandlab91fde2009-08-13 08:51:18 +00002562 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002563 nowawareplus2 = delta + nowaware
Georg Brandlab91fde2009-08-13 08:51:18 +00002564 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002565 self.assertEqual(nowawareplus, nowawareplus2)
2566
2567 # that - delta should be what we started with, and that - what we
2568 # started with should be delta.
2569 diff = nowawareplus - delta
Georg Brandlab91fde2009-08-13 08:51:18 +00002570 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002571 self.assertEqual(nowaware, diff)
2572 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2573 self.assertEqual(nowawareplus - nowaware, delta)
2574
2575 # Make up a random timezone.
2576 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002577 # Attach it to nowawareplus.
2578 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Georg Brandlab91fde2009-08-13 08:51:18 +00002579 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002580 # Make sure the difference takes the timezone adjustments into account.
2581 got = nowaware - nowawareplus
2582 # Expected: (nowaware base - nowaware offset) -
2583 # (nowawareplus base - nowawareplus offset) =
2584 # (nowaware base - nowawareplus base) +
2585 # (nowawareplus offset - nowaware offset) =
2586 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002587 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002588 self.assertEqual(got, expected)
2589
2590 # Try max possible difference.
2591 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2592 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2593 tzinfo=FixedOffset(-1439, "max"))
2594 maxdiff = max - min
2595 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2596 timedelta(minutes=2*1439))
2597
2598 def test_tzinfo_now(self):
2599 meth = self.theclass.now
2600 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2601 base = meth()
2602 # Try with and without naming the keyword.
2603 off42 = FixedOffset(42, "42")
2604 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002605 again = meth(tz=off42)
Georg Brandlab91fde2009-08-13 08:51:18 +00002606 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002607 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002608 # Bad argument with and w/o naming the keyword.
2609 self.assertRaises(TypeError, meth, 16)
2610 self.assertRaises(TypeError, meth, tzinfo=16)
2611 # Bad keyword name.
2612 self.assertRaises(TypeError, meth, tinfo=off42)
2613 # Too many args.
2614 self.assertRaises(TypeError, meth, off42, off42)
2615
Tim Peters10cadce2003-01-23 19:58:02 +00002616 # We don't know which time zone we're in, and don't have a tzinfo
2617 # class to represent it, so seeing whether a tz argument actually
2618 # does a conversion is tricky.
2619 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2620 utc = FixedOffset(0, "utc", 0)
2621 for dummy in range(3):
2622 now = datetime.now(weirdtz)
Georg Brandlab91fde2009-08-13 08:51:18 +00002623 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002624 utcnow = datetime.utcnow().replace(tzinfo=utc)
2625 now2 = utcnow.astimezone(weirdtz)
2626 if abs(now - now2) < timedelta(seconds=30):
2627 break
2628 # Else the code is broken, or more than 30 seconds passed between
2629 # calls; assuming the latter, just try again.
2630 else:
2631 # Three strikes and we're out.
2632 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2633
Tim Peters2a799bf2002-12-16 20:18:38 +00002634 def test_tzinfo_fromtimestamp(self):
2635 import time
2636 meth = self.theclass.fromtimestamp
2637 ts = time.time()
2638 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2639 base = meth(ts)
2640 # Try with and without naming the keyword.
2641 off42 = FixedOffset(42, "42")
2642 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002643 again = meth(ts, tz=off42)
Georg Brandlab91fde2009-08-13 08:51:18 +00002644 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002645 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002646 # Bad argument with and w/o naming the keyword.
2647 self.assertRaises(TypeError, meth, ts, 16)
2648 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2649 # Bad keyword name.
2650 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2651 # Too many args.
2652 self.assertRaises(TypeError, meth, ts, off42, off42)
2653 # Too few args.
2654 self.assertRaises(TypeError, meth)
2655
Tim Peters2a44a8d2003-01-23 20:53:10 +00002656 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002657 timestamp = 1000000000
2658 utcdatetime = datetime.utcfromtimestamp(timestamp)
2659 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2660 # But on some flavor of Mac, it's nowhere near that. So we can't have
2661 # any idea here what time that actually is, we can only test that
2662 # relative changes match.
2663 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2664 tz = FixedOffset(utcoffset, "tz", 0)
2665 expected = utcdatetime + utcoffset
2666 got = datetime.fromtimestamp(timestamp, tz)
2667 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002668
Tim Peters2a799bf2002-12-16 20:18:38 +00002669 def test_tzinfo_utcnow(self):
2670 meth = self.theclass.utcnow
2671 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2672 base = meth()
2673 # Try with and without naming the keyword; for whatever reason,
2674 # utcnow() doesn't accept a tzinfo argument.
2675 off42 = FixedOffset(42, "42")
2676 self.assertRaises(TypeError, meth, off42)
2677 self.assertRaises(TypeError, meth, tzinfo=off42)
2678
2679 def test_tzinfo_utcfromtimestamp(self):
2680 import time
2681 meth = self.theclass.utcfromtimestamp
2682 ts = time.time()
2683 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2684 base = meth(ts)
2685 # Try with and without naming the keyword; for whatever reason,
2686 # utcfromtimestamp() doesn't accept a tzinfo argument.
2687 off42 = FixedOffset(42, "42")
2688 self.assertRaises(TypeError, meth, ts, off42)
2689 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2690
2691 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002692 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002693 # DST flag.
2694 class DST(tzinfo):
2695 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002696 if isinstance(dstvalue, int):
2697 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002698 self.dstvalue = dstvalue
2699 def dst(self, dt):
2700 return self.dstvalue
2701
2702 cls = self.theclass
2703 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2704 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2705 t = d.timetuple()
2706 self.assertEqual(1, t.tm_year)
2707 self.assertEqual(1, t.tm_mon)
2708 self.assertEqual(1, t.tm_mday)
2709 self.assertEqual(10, t.tm_hour)
2710 self.assertEqual(20, t.tm_min)
2711 self.assertEqual(30, t.tm_sec)
2712 self.assertEqual(0, t.tm_wday)
2713 self.assertEqual(1, t.tm_yday)
2714 self.assertEqual(flag, t.tm_isdst)
2715
2716 # dst() returns wrong type.
2717 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2718
2719 # dst() at the edge.
2720 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2721 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2722
2723 # dst() out of range.
2724 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2725 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2726
2727 def test_utctimetuple(self):
2728 class DST(tzinfo):
2729 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002730 if isinstance(dstvalue, int):
2731 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002732 self.dstvalue = dstvalue
2733 def dst(self, dt):
2734 return self.dstvalue
2735
2736 cls = self.theclass
2737 # This can't work: DST didn't implement utcoffset.
2738 self.assertRaises(NotImplementedError,
2739 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2740
2741 class UOFS(DST):
2742 def __init__(self, uofs, dofs=None):
2743 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002744 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002745 def utcoffset(self, dt):
2746 return self.uofs
2747
2748 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2749 # in effect for a UTC time.
2750 for dstvalue in -33, 33, 0, None:
2751 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2752 t = d.utctimetuple()
2753 self.assertEqual(d.year, t.tm_year)
2754 self.assertEqual(d.month, t.tm_mon)
2755 self.assertEqual(d.day, t.tm_mday)
2756 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2757 self.assertEqual(13, t.tm_min)
2758 self.assertEqual(d.second, t.tm_sec)
2759 self.assertEqual(d.weekday(), t.tm_wday)
2760 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2761 t.tm_yday)
2762 self.assertEqual(0, t.tm_isdst)
2763
2764 # At the edges, UTC adjustment can normalize into years out-of-range
2765 # for a datetime object. Ensure that a correct timetuple is
2766 # created anyway.
2767 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2768 # That goes back 1 minute less than a full day.
2769 t = tiny.utctimetuple()
2770 self.assertEqual(t.tm_year, MINYEAR-1)
2771 self.assertEqual(t.tm_mon, 12)
2772 self.assertEqual(t.tm_mday, 31)
2773 self.assertEqual(t.tm_hour, 0)
2774 self.assertEqual(t.tm_min, 1)
2775 self.assertEqual(t.tm_sec, 37)
2776 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2777 self.assertEqual(t.tm_isdst, 0)
2778
2779 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2780 # That goes forward 1 minute less than a full day.
2781 t = huge.utctimetuple()
2782 self.assertEqual(t.tm_year, MAXYEAR+1)
2783 self.assertEqual(t.tm_mon, 1)
2784 self.assertEqual(t.tm_mday, 1)
2785 self.assertEqual(t.tm_hour, 23)
2786 self.assertEqual(t.tm_min, 58)
2787 self.assertEqual(t.tm_sec, 37)
2788 self.assertEqual(t.tm_yday, 1)
2789 self.assertEqual(t.tm_isdst, 0)
2790
2791 def test_tzinfo_isoformat(self):
2792 zero = FixedOffset(0, "+00:00")
2793 plus = FixedOffset(220, "+03:40")
2794 minus = FixedOffset(-231, "-03:51")
2795 unknown = FixedOffset(None, "")
2796
2797 cls = self.theclass
2798 datestr = '0001-02-03'
2799 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002800 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002801 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2802 timestr = '04:05:59' + (us and '.987001' or '')
2803 ofsstr = ofs is not None and d.tzname() or ''
2804 tailstr = timestr + ofsstr
2805 iso = d.isoformat()
2806 self.assertEqual(iso, datestr + 'T' + tailstr)
2807 self.assertEqual(iso, d.isoformat('T'))
2808 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002809 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002810 self.assertEqual(str(d), datestr + ' ' + tailstr)
2811
Tim Peters12bf3392002-12-24 05:41:27 +00002812 def test_replace(self):
2813 cls = self.theclass
2814 z100 = FixedOffset(100, "+100")
2815 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2816 args = [1, 2, 3, 4, 5, 6, 7, z100]
2817 base = cls(*args)
2818 self.assertEqual(base, base.replace())
2819
2820 i = 0
2821 for name, newval in (("year", 2),
2822 ("month", 3),
2823 ("day", 4),
2824 ("hour", 5),
2825 ("minute", 6),
2826 ("second", 7),
2827 ("microsecond", 8),
2828 ("tzinfo", zm200)):
2829 newargs = args[:]
2830 newargs[i] = newval
2831 expected = cls(*newargs)
2832 got = base.replace(**{name: newval})
2833 self.assertEqual(expected, got)
2834 i += 1
2835
2836 # Ensure we can get rid of a tzinfo.
2837 self.assertEqual(base.tzname(), "+100")
2838 base2 = base.replace(tzinfo=None)
Georg Brandlab91fde2009-08-13 08:51:18 +00002839 self.assertTrue(base2.tzinfo is None)
2840 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002841
2842 # Ensure we can add one.
2843 base3 = base2.replace(tzinfo=z100)
2844 self.assertEqual(base, base3)
Georg Brandlab91fde2009-08-13 08:51:18 +00002845 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002846
2847 # Out of bounds.
2848 base = cls(2000, 2, 29)
2849 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002850
Tim Peters80475bb2002-12-25 07:40:55 +00002851 def test_more_astimezone(self):
2852 # The inherited test_astimezone covered some trivial and error cases.
2853 fnone = FixedOffset(None, "None")
2854 f44m = FixedOffset(44, "44")
2855 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2856
Tim Peters10cadce2003-01-23 19:58:02 +00002857 dt = self.theclass.now(tz=f44m)
Georg Brandlab91fde2009-08-13 08:51:18 +00002858 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002859 # Replacing with degenerate tzinfo raises an exception.
2860 self.assertRaises(ValueError, dt.astimezone, fnone)
2861 # Ditto with None tz.
2862 self.assertRaises(TypeError, dt.astimezone, None)
2863 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002864 x = dt.astimezone(dt.tzinfo)
Georg Brandlab91fde2009-08-13 08:51:18 +00002865 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002866 self.assertEqual(x.date(), dt.date())
2867 self.assertEqual(x.time(), dt.time())
2868
2869 # Replacing with different tzinfo does adjust.
2870 got = dt.astimezone(fm5h)
Georg Brandlab91fde2009-08-13 08:51:18 +00002871 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002872 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2873 expected = dt - dt.utcoffset() # in effect, convert to UTC
2874 expected += fm5h.utcoffset(dt) # and from there to local time
2875 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2876 self.assertEqual(got.date(), expected.date())
2877 self.assertEqual(got.time(), expected.time())
2878 self.assertEqual(got.timetz(), expected.timetz())
Georg Brandlab91fde2009-08-13 08:51:18 +00002879 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002880 self.assertEqual(got, expected)
2881
Tim Peters4c0db782002-12-26 05:01:19 +00002882 def test_aware_subtract(self):
2883 cls = self.theclass
2884
Tim Peters60c76e42002-12-27 00:41:11 +00002885 # Ensure that utcoffset() is ignored when the operands have the
2886 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002887 class OperandDependentOffset(tzinfo):
2888 def utcoffset(self, t):
2889 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002890 # d0 and d1 equal after adjustment
2891 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002892 else:
Tim Peters397301e2003-01-02 21:28:08 +00002893 # d2 off in the weeds
2894 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002895
2896 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2897 d0 = base.replace(minute=3)
2898 d1 = base.replace(minute=9)
2899 d2 = base.replace(minute=11)
2900 for x in d0, d1, d2:
2901 for y in d0, d1, d2:
2902 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002903 expected = timedelta(minutes=x.minute - y.minute)
2904 self.assertEqual(got, expected)
2905
2906 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2907 # ignored.
2908 base = cls(8, 9, 10, 11, 12, 13, 14)
2909 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2910 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2911 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2912 for x in d0, d1, d2:
2913 for y in d0, d1, d2:
2914 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002915 if (x is d0 or x is d1) and (y is d0 or y is d1):
2916 expected = timedelta(0)
2917 elif x is y is d2:
2918 expected = timedelta(0)
2919 elif x is d2:
2920 expected = timedelta(minutes=(11-59)-0)
2921 else:
2922 assert y is d2
2923 expected = timedelta(minutes=0-(11-59))
2924 self.assertEqual(got, expected)
2925
Tim Peters60c76e42002-12-27 00:41:11 +00002926 def test_mixed_compare(self):
2927 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002928 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002929 self.assertEqual(t1, t2)
2930 t2 = t2.replace(tzinfo=None)
2931 self.assertEqual(t1, t2)
2932 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2933 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002934 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2935 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002936
Tim Peters0bf60bd2003-01-08 20:40:01 +00002937 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002938 class Varies(tzinfo):
2939 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002940 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002941 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002942 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002943 return self.offset
2944
2945 v = Varies()
2946 t1 = t2.replace(tzinfo=v)
2947 t2 = t2.replace(tzinfo=v)
2948 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2949 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2950 self.assertEqual(t1, t2)
2951
2952 # But if they're not identical, it isn't ignored.
2953 t2 = t2.replace(tzinfo=Varies())
Georg Brandlab91fde2009-08-13 08:51:18 +00002954 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002955
Tim Petersa98924a2003-05-17 05:55:19 +00002956 def test_subclass_datetimetz(self):
2957
2958 class C(self.theclass):
2959 theAnswer = 42
2960
2961 def __new__(cls, *args, **kws):
2962 temp = kws.copy()
2963 extra = temp.pop('extra')
2964 result = self.theclass.__new__(cls, *args, **temp)
2965 result.extra = extra
2966 return result
2967
2968 def newmeth(self, start):
2969 return start + self.hour + self.year
2970
2971 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2972
2973 dt1 = self.theclass(*args)
2974 dt2 = C(*args, **{'extra': 7})
2975
2976 self.assertEqual(dt2.__class__, C)
2977 self.assertEqual(dt2.theAnswer, 42)
2978 self.assertEqual(dt2.extra, 7)
2979 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2980 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2981
Tim Peters621818b2002-12-29 23:44:49 +00002982# Pain to set up DST-aware tzinfo classes.
2983
2984def first_sunday_on_or_after(dt):
2985 days_to_go = 6 - dt.weekday()
2986 if days_to_go:
2987 dt += timedelta(days_to_go)
2988 return dt
2989
2990ZERO = timedelta(0)
2991HOUR = timedelta(hours=1)
2992DAY = timedelta(days=1)
2993# In the US, DST starts at 2am (standard time) on the first Sunday in April.
2994DSTSTART = datetime(1, 4, 1, 2)
2995# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00002996# which is the first Sunday on or after Oct 25. Because we view 1:MM as
2997# being standard time on that day, there is no spelling in local time of
2998# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2999DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003000
3001class USTimeZone(tzinfo):
3002
3003 def __init__(self, hours, reprname, stdname, dstname):
3004 self.stdoffset = timedelta(hours=hours)
3005 self.reprname = reprname
3006 self.stdname = stdname
3007 self.dstname = dstname
3008
3009 def __repr__(self):
3010 return self.reprname
3011
3012 def tzname(self, dt):
3013 if self.dst(dt):
3014 return self.dstname
3015 else:
3016 return self.stdname
3017
3018 def utcoffset(self, dt):
3019 return self.stdoffset + self.dst(dt)
3020
3021 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003022 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003023 # An exception instead may be sensible here, in one or more of
3024 # the cases.
3025 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003026 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003027
3028 # Find first Sunday in April.
3029 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3030 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3031
3032 # Find last Sunday in October.
3033 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3034 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3035
Tim Peters621818b2002-12-29 23:44:49 +00003036 # Can't compare naive to aware objects, so strip the timezone from
3037 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003038 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003039 return HOUR
3040 else:
3041 return ZERO
3042
Tim Peters521fc152002-12-31 17:36:56 +00003043Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3044Central = USTimeZone(-6, "Central", "CST", "CDT")
3045Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3046Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003047utc_real = FixedOffset(0, "UTC", 0)
3048# For better test coverage, we want another flavor of UTC that's west of
3049# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003050utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003051
3052class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003053 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003054 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003055 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003056
Tim Peters0bf60bd2003-01-08 20:40:01 +00003057 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003058
Tim Peters521fc152002-12-31 17:36:56 +00003059 # Check a time that's inside DST.
3060 def checkinside(self, dt, tz, utc, dston, dstoff):
3061 self.assertEqual(dt.dst(), HOUR)
3062
3063 # Conversion to our own timezone is always an identity.
3064 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003065
3066 asutc = dt.astimezone(utc)
3067 there_and_back = asutc.astimezone(tz)
3068
3069 # Conversion to UTC and back isn't always an identity here,
3070 # because there are redundant spellings (in local time) of
3071 # UTC time when DST begins: the clock jumps from 1:59:59
3072 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3073 # make sense then. The classes above treat 2:MM:SS as
3074 # daylight time then (it's "after 2am"), really an alias
3075 # for 1:MM:SS standard time. The latter form is what
3076 # conversion back from UTC produces.
3077 if dt.date() == dston.date() and dt.hour == 2:
3078 # We're in the redundant hour, and coming back from
3079 # UTC gives the 1:MM:SS standard-time spelling.
3080 self.assertEqual(there_and_back + HOUR, dt)
3081 # Although during was considered to be in daylight
3082 # time, there_and_back is not.
3083 self.assertEqual(there_and_back.dst(), ZERO)
3084 # They're the same times in UTC.
3085 self.assertEqual(there_and_back.astimezone(utc),
3086 dt.astimezone(utc))
3087 else:
3088 # We're not in the redundant hour.
3089 self.assertEqual(dt, there_and_back)
3090
Tim Peters327098a2003-01-20 22:54:38 +00003091 # Because we have a redundant spelling when DST begins, there is
3092 # (unforunately) an hour when DST ends that can't be spelled at all in
3093 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3094 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3095 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3096 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3097 # expressed in local time. Nevertheless, we want conversion back
3098 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003099 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003100 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003101 if dt.date() == dstoff.date() and dt.hour == 0:
3102 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003103 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003104 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3105 nexthour_utc += HOUR
3106 nexthour_tz = nexthour_utc.astimezone(tz)
3107 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003108 else:
Tim Peters327098a2003-01-20 22:54:38 +00003109 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003110
3111 # Check a time that's outside DST.
3112 def checkoutside(self, dt, tz, utc):
3113 self.assertEqual(dt.dst(), ZERO)
3114
3115 # Conversion to our own timezone is always an identity.
3116 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003117
3118 # Converting to UTC and back is an identity too.
3119 asutc = dt.astimezone(utc)
3120 there_and_back = asutc.astimezone(tz)
3121 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003122
Tim Peters1024bf82002-12-30 17:09:40 +00003123 def convert_between_tz_and_utc(self, tz, utc):
3124 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003125 # Because 1:MM on the day DST ends is taken as being standard time,
3126 # there is no spelling in tz for the last hour of daylight time.
3127 # For purposes of the test, the last hour of DST is 0:MM, which is
3128 # taken as being daylight time (and 1:MM is taken as being standard
3129 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003130 dstoff = self.dstoff.replace(tzinfo=tz)
3131 for delta in (timedelta(weeks=13),
3132 DAY,
3133 HOUR,
3134 timedelta(minutes=1),
3135 timedelta(microseconds=1)):
3136
Tim Peters521fc152002-12-31 17:36:56 +00003137 self.checkinside(dston, tz, utc, dston, dstoff)
3138 for during in dston + delta, dstoff - delta:
3139 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003140
Tim Peters521fc152002-12-31 17:36:56 +00003141 self.checkoutside(dstoff, tz, utc)
3142 for outside in dston - delta, dstoff + delta:
3143 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003144
Tim Peters621818b2002-12-29 23:44:49 +00003145 def test_easy(self):
3146 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003147 self.convert_between_tz_and_utc(Eastern, utc_real)
3148 self.convert_between_tz_and_utc(Pacific, utc_real)
3149 self.convert_between_tz_and_utc(Eastern, utc_fake)
3150 self.convert_between_tz_and_utc(Pacific, utc_fake)
3151 # The next is really dancing near the edge. It works because
3152 # Pacific and Eastern are far enough apart that their "problem
3153 # hours" don't overlap.
3154 self.convert_between_tz_and_utc(Eastern, Pacific)
3155 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003156 # OTOH, these fail! Don't enable them. The difficulty is that
3157 # the edge case tests assume that every hour is representable in
3158 # the "utc" class. This is always true for a fixed-offset tzinfo
3159 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3160 # For these adjacent DST-aware time zones, the range of time offsets
3161 # tested ends up creating hours in the one that aren't representable
3162 # in the other. For the same reason, we would see failures in the
3163 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3164 # offset deltas in convert_between_tz_and_utc().
3165 #
3166 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3167 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003168
Tim Petersf3615152003-01-01 21:51:37 +00003169 def test_tricky(self):
3170 # 22:00 on day before daylight starts.
3171 fourback = self.dston - timedelta(hours=4)
3172 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003173 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003174 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3175 # 2", we should get the 3 spelling.
3176 # If we plug 22:00 the day before into Eastern, it "looks like std
3177 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3178 # to 22:00 lands on 2:00, which makes no sense in local time (the
3179 # local clock jumps from 1 to 3). The point here is to make sure we
3180 # get the 3 spelling.
3181 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003182 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003183 self.assertEqual(expected, got)
3184
3185 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3186 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003187 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003188 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3189 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3190 # spelling.
3191 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003192 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003193 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003194
Tim Petersadf64202003-01-04 06:03:15 +00003195 # Now on the day DST ends, we want "repeat an hour" behavior.
3196 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3197 # EST 23:MM 0:MM 1:MM 2:MM
3198 # EDT 0:MM 1:MM 2:MM 3:MM
3199 # wall 0:MM 1:MM 1:MM 2:MM against these
3200 for utc in utc_real, utc_fake:
3201 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003202 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003203 # Convert that to UTC.
3204 first_std_hour -= tz.utcoffset(None)
3205 # Adjust for possibly fake UTC.
3206 asutc = first_std_hour + utc.utcoffset(None)
3207 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3208 # tz=Eastern.
3209 asutcbase = asutc.replace(tzinfo=utc)
3210 for tzhour in (0, 1, 1, 2):
3211 expectedbase = self.dstoff.replace(hour=tzhour)
3212 for minute in 0, 30, 59:
3213 expected = expectedbase.replace(minute=minute)
3214 asutc = asutcbase.replace(minute=minute)
3215 astz = asutc.astimezone(tz)
3216 self.assertEqual(astz.replace(tzinfo=None), expected)
3217 asutcbase += HOUR
3218
3219
Tim Peters710fb152003-01-02 19:35:54 +00003220 def test_bogus_dst(self):
3221 class ok(tzinfo):
3222 def utcoffset(self, dt): return HOUR
3223 def dst(self, dt): return HOUR
3224
3225 now = self.theclass.now().replace(tzinfo=utc_real)
3226 # Doesn't blow up.
3227 now.astimezone(ok())
3228
3229 # Does blow up.
3230 class notok(ok):
3231 def dst(self, dt): return None
3232 self.assertRaises(ValueError, now.astimezone, notok())
3233
Tim Peters52dcce22003-01-23 16:36:11 +00003234 def test_fromutc(self):
3235 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3236 now = datetime.utcnow().replace(tzinfo=utc_real)
3237 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3238 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3239 enow = Eastern.fromutc(now) # doesn't blow up
3240 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3241 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3242 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3243
3244 # Always converts UTC to standard time.
3245 class FauxUSTimeZone(USTimeZone):
3246 def fromutc(self, dt):
3247 return dt + self.stdoffset
3248 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3249
3250 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3251 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3252 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3253
3254 # Check around DST start.
3255 start = self.dston.replace(hour=4, tzinfo=Eastern)
3256 fstart = start.replace(tzinfo=FEastern)
3257 for wall in 23, 0, 1, 3, 4, 5:
3258 expected = start.replace(hour=wall)
3259 if wall == 23:
3260 expected -= timedelta(days=1)
3261 got = Eastern.fromutc(start)
3262 self.assertEqual(expected, got)
3263
3264 expected = fstart + FEastern.stdoffset
3265 got = FEastern.fromutc(fstart)
3266 self.assertEqual(expected, got)
3267
3268 # Ensure astimezone() calls fromutc() too.
3269 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3270 self.assertEqual(expected, got)
3271
3272 start += HOUR
3273 fstart += HOUR
3274
3275 # Check around DST end.
3276 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3277 fstart = start.replace(tzinfo=FEastern)
3278 for wall in 0, 1, 1, 2, 3, 4:
3279 expected = start.replace(hour=wall)
3280 got = Eastern.fromutc(start)
3281 self.assertEqual(expected, got)
3282
3283 expected = fstart + FEastern.stdoffset
3284 got = FEastern.fromutc(fstart)
3285 self.assertEqual(expected, got)
3286
3287 # Ensure astimezone() calls fromutc() too.
3288 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3289 self.assertEqual(expected, got)
3290
3291 start += HOUR
3292 fstart += HOUR
3293
Tim Peters710fb152003-01-02 19:35:54 +00003294
Tim Peters528ca532004-09-16 01:30:50 +00003295#############################################################################
3296# oddballs
3297
3298class Oddballs(unittest.TestCase):
3299
3300 def test_bug_1028306(self):
3301 # Trying to compare a date to a datetime should act like a mixed-
3302 # type comparison, despite that datetime is a subclass of date.
3303 as_date = date.today()
3304 as_datetime = datetime.combine(as_date, time())
Georg Brandlab91fde2009-08-13 08:51:18 +00003305 self.assertTrue(as_date != as_datetime)
3306 self.assertTrue(as_datetime != as_date)
3307 self.assertTrue(not as_date == as_datetime)
3308 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003309 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3310 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3311 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3312 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3313 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3314 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3315 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3316 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3317
3318 # Neverthelss, comparison should work with the base-class (date)
3319 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003320 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003321 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003322 as_different = as_datetime.replace(day= different_day)
3323 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003324
3325 # And date should compare with other subclasses of date. If a
3326 # subclass wants to stop this, it's up to the subclass to do so.
3327 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3328 self.assertEqual(as_date, date_sc)
3329 self.assertEqual(date_sc, as_date)
3330
3331 # Ditto for datetimes.
3332 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3333 as_date.day, 0, 0, 0)
3334 self.assertEqual(as_datetime, datetime_sc)
3335 self.assertEqual(datetime_sc, as_datetime)
3336
Tim Peters2a799bf2002-12-16 20:18:38 +00003337def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003338 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003339
3340if __name__ == "__main__":
3341 test_main()