blob: d983d6e566d5424f7ae81c4625cd7cdbc21628d6 [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))
Alexander Belopolskyf4b552e2010-06-22 14:43:47 +0000202 eq(b.__rsub__(a), td(6, 24*3600 - 60))
Tim Peters2a799bf2002-12-16 20:18:38 +0000203 eq(-a, td(-7))
204 eq(+a, td(7))
205 eq(-b, td(-1, 24*3600 - 60))
206 eq(-c, td(-1, 24*3600 - 1, 999000))
207 eq(abs(a), a)
208 eq(abs(-a), a)
209 eq(td(6, 24*3600), a)
210 eq(td(0, 0, 60*1000000), b)
211 eq(a*10, td(70))
212 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000213 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000214 eq(b*10, td(0, 600))
215 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000216 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000217 eq(c*10, td(0, 0, 10000))
218 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000219 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000220 eq(a*-1, -a)
221 eq(b*-2, -b-b)
222 eq(c*-2, -c+-c)
223 eq(b*(60*24), (b*60)*24)
224 eq(b*(60*24), (60*b)*24)
225 eq(c*1000, td(0, 1))
226 eq(1000*c, td(0, 1))
227 eq(a//7, td(1))
228 eq(b//10, td(0, 6))
229 eq(c//1000, td(0, 0, 1))
230 eq(a//10, td(0, 7*24*360))
231 eq(a//3600000, td(0, 0, 7*24*1000))
232
233 def test_disallowed_computations(self):
234 a = timedelta(42)
235
236 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000237 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000238 self.assertRaises(TypeError, lambda: a+i)
239 self.assertRaises(TypeError, lambda: a-i)
240 self.assertRaises(TypeError, lambda: i+a)
241 self.assertRaises(TypeError, lambda: i-a)
242
243 # Mul/div by float isn't supported.
244 x = 2.3
245 self.assertRaises(TypeError, lambda: a*x)
246 self.assertRaises(TypeError, lambda: x*a)
247 self.assertRaises(TypeError, lambda: a/x)
248 self.assertRaises(TypeError, lambda: x/a)
249 self.assertRaises(TypeError, lambda: a // x)
250 self.assertRaises(TypeError, lambda: x // a)
251
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000252 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000253 # Division by zero doesn't make sense.
Guido van Rossume2a383d2007-01-15 16:59:06 +0000254 for zero in 0, 0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000255 self.assertRaises(TypeError, lambda: zero // a)
256 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Alexander Belopolskyf4b552e2010-06-22 14:43:47 +0000257 self.assertRaises(TypeError, lambda: a / '')
Tim Peters2a799bf2002-12-16 20:18:38 +0000258
259 def test_basic_attributes(self):
260 days, seconds, us = 1, 7, 31
261 td = timedelta(days, seconds, us)
262 self.assertEqual(td.days, days)
263 self.assertEqual(td.seconds, seconds)
264 self.assertEqual(td.microseconds, us)
265
266 def test_carries(self):
267 t1 = timedelta(days=100,
268 weeks=-7,
269 hours=-24*(100-49),
270 minutes=-3,
271 seconds=12,
272 microseconds=(3*60 - 12) * 1e6 + 1)
273 t2 = timedelta(microseconds=1)
274 self.assertEqual(t1, t2)
275
276 def test_hash_equality(self):
277 t1 = timedelta(days=100,
278 weeks=-7,
279 hours=-24*(100-49),
280 minutes=-3,
281 seconds=12,
282 microseconds=(3*60 - 12) * 1000000)
283 t2 = timedelta()
284 self.assertEqual(hash(t1), hash(t2))
285
286 t1 += timedelta(weeks=7)
287 t2 += timedelta(days=7*7)
288 self.assertEqual(t1, t2)
289 self.assertEqual(hash(t1), hash(t2))
290
291 d = {t1: 1}
292 d[t2] = 2
293 self.assertEqual(len(d), 1)
294 self.assertEqual(d[t1], 2)
295
296 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000297 args = 12, 34, 56
298 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000299 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000300 green = pickler.dumps(orig, proto)
301 derived = unpickler.loads(green)
302 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000303
304 def test_compare(self):
305 t1 = timedelta(2, 3, 4)
306 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000307 self.assertEqual(t1, t2)
Georg Brandlab91fde2009-08-13 08:51:18 +0000308 self.assertTrue(t1 <= t2)
309 self.assertTrue(t1 >= t2)
310 self.assertTrue(not t1 != t2)
311 self.assertTrue(not t1 < t2)
312 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000313
314 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
315 t2 = timedelta(*args) # this is larger than t1
Georg Brandlab91fde2009-08-13 08:51:18 +0000316 self.assertTrue(t1 < t2)
317 self.assertTrue(t2 > t1)
318 self.assertTrue(t1 <= t2)
319 self.assertTrue(t2 >= t1)
320 self.assertTrue(t1 != t2)
321 self.assertTrue(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)
326 self.assertTrue(not t1 >= t2)
327 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000328
Tim Peters68124bb2003-02-08 03:46:31 +0000329 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000330 self.assertEqual(t1 == badarg, False)
331 self.assertEqual(t1 != badarg, True)
332 self.assertEqual(badarg == t1, False)
333 self.assertEqual(badarg != t1, True)
334
Tim Peters2a799bf2002-12-16 20:18:38 +0000335 self.assertRaises(TypeError, lambda: t1 <= badarg)
336 self.assertRaises(TypeError, lambda: t1 < badarg)
337 self.assertRaises(TypeError, lambda: t1 > badarg)
338 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000339 self.assertRaises(TypeError, lambda: badarg <= t1)
340 self.assertRaises(TypeError, lambda: badarg < t1)
341 self.assertRaises(TypeError, lambda: badarg > t1)
342 self.assertRaises(TypeError, lambda: badarg >= t1)
343
344 def test_str(self):
345 td = timedelta
346 eq = self.assertEqual
347
348 eq(str(td(1)), "1 day, 0:00:00")
349 eq(str(td(-1)), "-1 day, 0:00:00")
350 eq(str(td(2)), "2 days, 0:00:00")
351 eq(str(td(-2)), "-2 days, 0:00:00")
352
353 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
354 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
355 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
356 "-210 days, 23:12:34")
357
358 eq(str(td(milliseconds=1)), "0:00:00.001000")
359 eq(str(td(microseconds=3)), "0:00:00.000003")
360
361 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
362 microseconds=999999)),
363 "999999999 days, 23:59:59.999999")
364
Alexander Belopolskyf4b552e2010-06-22 14:43:47 +0000365 def test_repr(self):
366 name = 'datetime.' + self.theclass.__name__
367 self.assertEqual(repr(self.theclass(1)),
368 "%s(1)" % name)
369 self.assertEqual(repr(self.theclass(10, 2)),
370 "%s(10, 2)" % name)
371 self.assertEqual(repr(self.theclass(-10, 2, 400000)),
372 "%s(-10, 2, 400000)" % name)
373
Tim Peters2a799bf2002-12-16 20:18:38 +0000374 def test_roundtrip(self):
375 for td in (timedelta(days=999999999, hours=23, minutes=59,
376 seconds=59, microseconds=999999),
377 timedelta(days=-999999999),
Alexander Belopolskyf4b552e2010-06-22 14:43:47 +0000378 timedelta(days=-999999999, seconds=1),
Tim Peters2a799bf2002-12-16 20:18:38 +0000379 timedelta(days=1, seconds=2, microseconds=3)):
380
381 # Verify td -> string -> td identity.
382 s = repr(td)
Georg Brandlab91fde2009-08-13 08:51:18 +0000383 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000384 s = s[9:]
385 td2 = eval(s)
386 self.assertEqual(td, td2)
387
388 # Verify identity via reconstructing from pieces.
389 td2 = timedelta(td.days, td.seconds, td.microseconds)
390 self.assertEqual(td, td2)
391
392 def test_resolution_info(self):
Georg Brandlab91fde2009-08-13 08:51:18 +0000393 self.assertTrue(isinstance(timedelta.min, timedelta))
394 self.assertTrue(isinstance(timedelta.max, timedelta))
395 self.assertTrue(isinstance(timedelta.resolution, timedelta))
396 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000397 self.assertEqual(timedelta.min, timedelta(-999999999))
398 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
399 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
400
401 def test_overflow(self):
402 tiny = timedelta.resolution
403
404 td = timedelta.min + tiny
405 td -= tiny # no problem
406 self.assertRaises(OverflowError, td.__sub__, tiny)
407 self.assertRaises(OverflowError, td.__add__, -tiny)
408
409 td = timedelta.max - tiny
410 td += tiny # no problem
411 self.assertRaises(OverflowError, td.__add__, tiny)
412 self.assertRaises(OverflowError, td.__sub__, -tiny)
413
414 self.assertRaises(OverflowError, lambda: -timedelta.max)
415
416 def test_microsecond_rounding(self):
417 td = timedelta
418 eq = self.assertEqual
419
420 # Single-field rounding.
421 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
422 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
423 eq(td(milliseconds=0.6/1000), td(microseconds=1))
424 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
425
426 # Rounding due to contributions from more than one field.
427 us_per_hour = 3600e6
428 us_per_day = us_per_hour * 24
429 eq(td(days=.4/us_per_day), td(0))
430 eq(td(hours=.2/us_per_hour), td(0))
431 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
432
433 eq(td(days=-.4/us_per_day), td(0))
434 eq(td(hours=-.2/us_per_hour), td(0))
435 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
436
437 def test_massive_normalization(self):
438 td = timedelta(microseconds=-1)
439 self.assertEqual((td.days, td.seconds, td.microseconds),
440 (-1, 24*3600-1, 999999))
441
442 def test_bool(self):
Georg Brandlab91fde2009-08-13 08:51:18 +0000443 self.assertTrue(timedelta(1))
444 self.assertTrue(timedelta(0, 1))
445 self.assertTrue(timedelta(0, 0, 1))
446 self.assertTrue(timedelta(microseconds=1))
447 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000448
Tim Petersb0c854d2003-05-17 15:57:00 +0000449 def test_subclass_timedelta(self):
450
451 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000452 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000453 def from_td(td):
454 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000455
456 def as_hours(self):
457 sum = (self.days * 24 +
458 self.seconds / 3600.0 +
459 self.microseconds / 3600e6)
460 return round(sum)
461
462 t1 = T(days=1)
Georg Brandlab91fde2009-08-13 08:51:18 +0000463 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000464 self.assertEqual(t1.as_hours(), 24)
465
466 t2 = T(days=-1, seconds=-3600)
Georg Brandlab91fde2009-08-13 08:51:18 +0000467 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000468 self.assertEqual(t2.as_hours(), -25)
469
470 t3 = t1 + t2
Georg Brandlab91fde2009-08-13 08:51:18 +0000471 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000472 t4 = T.from_td(t3)
Georg Brandlab91fde2009-08-13 08:51:18 +0000473 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000474 self.assertEqual(t3.days, t4.days)
475 self.assertEqual(t3.seconds, t4.seconds)
476 self.assertEqual(t3.microseconds, t4.microseconds)
477 self.assertEqual(str(t3), str(t4))
478 self.assertEqual(t4.as_hours(), -1)
479
Tim Peters2a799bf2002-12-16 20:18:38 +0000480#############################################################################
481# date tests
482
483class TestDateOnly(unittest.TestCase):
484 # Tests here won't pass if also run on datetime objects, so don't
485 # subclass this to test datetimes too.
486
487 def test_delta_non_days_ignored(self):
488 dt = date(2000, 1, 2)
489 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
490 microseconds=5)
491 days = timedelta(delta.days)
492 self.assertEqual(days, timedelta(1))
493
494 dt2 = dt + delta
495 self.assertEqual(dt2, dt + days)
496
497 dt2 = delta + dt
498 self.assertEqual(dt2, dt + days)
499
500 dt2 = dt - delta
501 self.assertEqual(dt2, dt - days)
502
503 delta = -delta
504 days = timedelta(delta.days)
505 self.assertEqual(days, timedelta(-2))
506
507 dt2 = dt + delta
508 self.assertEqual(dt2, dt + days)
509
510 dt2 = delta + dt
511 self.assertEqual(dt2, dt + days)
512
513 dt2 = dt - delta
514 self.assertEqual(dt2, dt - days)
515
Tim Peters604c0132004-06-07 23:04:33 +0000516class SubclassDate(date):
517 sub_var = 1
518
Guido van Rossumd8faa362007-04-27 19:54:29 +0000519class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000520 # Tests here should pass for both dates and datetimes, except for a
521 # few tests that TestDateTime overrides.
522
523 theclass = date
524
525 def test_basic_attributes(self):
526 dt = self.theclass(2002, 3, 1)
527 self.assertEqual(dt.year, 2002)
528 self.assertEqual(dt.month, 3)
529 self.assertEqual(dt.day, 1)
530
531 def test_roundtrip(self):
532 for dt in (self.theclass(1, 2, 3),
533 self.theclass.today()):
534 # Verify dt -> string -> date identity.
535 s = repr(dt)
Georg Brandlab91fde2009-08-13 08:51:18 +0000536 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000537 s = s[9:]
538 dt2 = eval(s)
539 self.assertEqual(dt, dt2)
540
541 # Verify identity via reconstructing from pieces.
542 dt2 = self.theclass(dt.year, dt.month, dt.day)
543 self.assertEqual(dt, dt2)
544
545 def test_ordinal_conversions(self):
546 # Check some fixed values.
547 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
548 (1, 12, 31, 365),
549 (2, 1, 1, 366),
550 # first example from "Calendrical Calculations"
551 (1945, 11, 12, 710347)]:
552 d = self.theclass(y, m, d)
553 self.assertEqual(n, d.toordinal())
554 fromord = self.theclass.fromordinal(n)
555 self.assertEqual(d, fromord)
556 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000557 # if we're checking something fancier than a date, verify
558 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000559 self.assertEqual(fromord.hour, 0)
560 self.assertEqual(fromord.minute, 0)
561 self.assertEqual(fromord.second, 0)
562 self.assertEqual(fromord.microsecond, 0)
563
Tim Peters0bf60bd2003-01-08 20:40:01 +0000564 # Check first and last days of year spottily across the whole
565 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000566 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000567 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
568 d = self.theclass(year, 1, 1)
569 n = d.toordinal()
570 d2 = self.theclass.fromordinal(n)
571 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000572 # Verify that moving back a day gets to the end of year-1.
573 if year > 1:
574 d = self.theclass.fromordinal(n-1)
575 d2 = self.theclass(year-1, 12, 31)
576 self.assertEqual(d, d2)
577 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000578
579 # Test every day in a leap-year and a non-leap year.
580 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
581 for year, isleap in (2000, True), (2002, False):
582 n = self.theclass(year, 1, 1).toordinal()
583 for month, maxday in zip(range(1, 13), dim):
584 if month == 2 and isleap:
585 maxday += 1
586 for day in range(1, maxday+1):
587 d = self.theclass(year, month, day)
588 self.assertEqual(d.toordinal(), n)
589 self.assertEqual(d, self.theclass.fromordinal(n))
590 n += 1
591
592 def test_extreme_ordinals(self):
593 a = self.theclass.min
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 a = self.theclass.max
606 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
607 aord = a.toordinal()
608 b = a.fromordinal(aord)
609 self.assertEqual(a, b)
610
611 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
612
613 b = a - timedelta(days=1)
614 self.assertEqual(b.toordinal(), aord - 1)
615 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
616
617 def test_bad_constructor_arguments(self):
618 # bad years
619 self.theclass(MINYEAR, 1, 1) # no exception
620 self.theclass(MAXYEAR, 1, 1) # no exception
621 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
622 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
623 # bad months
624 self.theclass(2000, 1, 1) # no exception
625 self.theclass(2000, 12, 1) # no exception
626 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
627 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
628 # bad days
629 self.theclass(2000, 2, 29) # no exception
630 self.theclass(2004, 2, 29) # no exception
631 self.theclass(2400, 2, 29) # no exception
632 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
633 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
634 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
635 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
636 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
637 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
638
639 def test_hash_equality(self):
640 d = self.theclass(2000, 12, 31)
641 # same thing
642 e = self.theclass(2000, 12, 31)
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 d = self.theclass(2001, 1, 1)
653 # same thing
654 e = self.theclass(2001, 1, 1)
655 self.assertEqual(d, e)
656 self.assertEqual(hash(d), hash(e))
657
658 dic = {d: 1}
659 dic[e] = 2
660 self.assertEqual(len(dic), 1)
661 self.assertEqual(dic[d], 2)
662 self.assertEqual(dic[e], 2)
663
664 def test_computations(self):
665 a = self.theclass(2002, 1, 31)
666 b = self.theclass(1956, 1, 31)
667
668 diff = a-b
669 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
670 self.assertEqual(diff.seconds, 0)
671 self.assertEqual(diff.microseconds, 0)
672
673 day = timedelta(1)
674 week = timedelta(7)
675 a = self.theclass(2002, 3, 2)
676 self.assertEqual(a + day, self.theclass(2002, 3, 3))
677 self.assertEqual(day + a, self.theclass(2002, 3, 3))
678 self.assertEqual(a - day, self.theclass(2002, 3, 1))
679 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
680 self.assertEqual(a + week, self.theclass(2002, 3, 9))
681 self.assertEqual(a - week, self.theclass(2002, 2, 23))
682 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
683 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
684 self.assertEqual((a + week) - a, week)
685 self.assertEqual((a + day) - a, day)
686 self.assertEqual((a - week) - a, -week)
687 self.assertEqual((a - day) - a, -day)
688 self.assertEqual(a - (a + week), -week)
689 self.assertEqual(a - (a + day), -day)
690 self.assertEqual(a - (a - week), week)
691 self.assertEqual(a - (a - day), day)
692
693 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +0000694 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000695 self.assertRaises(TypeError, lambda: a+i)
696 self.assertRaises(TypeError, lambda: a-i)
697 self.assertRaises(TypeError, lambda: i+a)
698 self.assertRaises(TypeError, lambda: i-a)
699
700 # delta - date is senseless.
701 self.assertRaises(TypeError, lambda: day - a)
702 # mixing date and (delta or date) via * or // is senseless
703 self.assertRaises(TypeError, lambda: day * a)
704 self.assertRaises(TypeError, lambda: a * day)
705 self.assertRaises(TypeError, lambda: day // a)
706 self.assertRaises(TypeError, lambda: a // day)
707 self.assertRaises(TypeError, lambda: a * a)
708 self.assertRaises(TypeError, lambda: a // a)
709 # date + date is senseless
710 self.assertRaises(TypeError, lambda: a + a)
711
712 def test_overflow(self):
713 tiny = self.theclass.resolution
714
Alexander Belopolsky3efc2fd2010-05-27 22:03:53 +0000715 for delta in [tiny, timedelta(1), timedelta(2)]:
716 dt = self.theclass.min + delta
717 dt -= delta # no problem
718 self.assertRaises(OverflowError, dt.__sub__, delta)
719 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000720
Alexander Belopolsky3efc2fd2010-05-27 22:03:53 +0000721 dt = self.theclass.max - delta
722 dt += delta # no problem
723 self.assertRaises(OverflowError, dt.__add__, delta)
724 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000725
726 def test_fromtimestamp(self):
727 import time
728
729 # Try an arbitrary fixed value.
730 year, month, day = 1999, 9, 19
731 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
732 d = self.theclass.fromtimestamp(ts)
733 self.assertEqual(d.year, year)
734 self.assertEqual(d.month, month)
735 self.assertEqual(d.day, day)
736
Tim Peters1b6f7a92004-06-20 02:50:16 +0000737 def test_insane_fromtimestamp(self):
738 # It's possible that some platform maps time_t to double,
739 # and that this test will fail there. This test should
740 # exempt such platforms (provided they return reasonable
741 # results!).
742 for insane in -1e200, 1e200:
743 self.assertRaises(ValueError, self.theclass.fromtimestamp,
744 insane)
745
Tim Peters2a799bf2002-12-16 20:18:38 +0000746 def test_today(self):
747 import time
748
749 # We claim that today() is like fromtimestamp(time.time()), so
750 # prove it.
751 for dummy in range(3):
752 today = self.theclass.today()
753 ts = time.time()
754 todayagain = self.theclass.fromtimestamp(ts)
755 if today == todayagain:
756 break
757 # There are several legit reasons that could fail:
758 # 1. It recently became midnight, between the today() and the
759 # time() calls.
760 # 2. The platform time() has such fine resolution that we'll
761 # never get the same value twice.
762 # 3. The platform time() has poor resolution, and we just
763 # happened to call today() right before a resolution quantum
764 # boundary.
765 # 4. The system clock got fiddled between calls.
766 # In any case, wait a little while and try again.
767 time.sleep(0.1)
768
769 # It worked or it didn't. If it didn't, assume it's reason #2, and
770 # let the test pass if they're within half a second of each other.
Georg Brandlab91fde2009-08-13 08:51:18 +0000771 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000772 abs(todayagain - today) < timedelta(seconds=0.5))
773
774 def test_weekday(self):
775 for i in range(7):
776 # March 4, 2002 is a Monday
777 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
778 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
779 # January 2, 1956 is a Monday
780 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
781 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
782
783 def test_isocalendar(self):
784 # Check examples from
785 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
786 for i in range(7):
787 d = self.theclass(2003, 12, 22+i)
788 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
789 d = self.theclass(2003, 12, 29) + timedelta(i)
790 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
791 d = self.theclass(2004, 1, 5+i)
792 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
793 d = self.theclass(2009, 12, 21+i)
794 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
795 d = self.theclass(2009, 12, 28) + timedelta(i)
796 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
797 d = self.theclass(2010, 1, 4+i)
798 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
799
800 def test_iso_long_years(self):
801 # Calculate long ISO years and compare to table from
802 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
803 ISO_LONG_YEARS_TABLE = """
804 4 32 60 88
805 9 37 65 93
806 15 43 71 99
807 20 48 76
808 26 54 82
809
810 105 133 161 189
811 111 139 167 195
812 116 144 172
813 122 150 178
814 128 156 184
815
816 201 229 257 285
817 207 235 263 291
818 212 240 268 296
819 218 246 274
820 224 252 280
821
822 303 331 359 387
823 308 336 364 392
824 314 342 370 398
825 320 348 376
826 325 353 381
827 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000828 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +0000829 L = []
830 for i in range(400):
831 d = self.theclass(2000+i, 12, 31)
832 d1 = self.theclass(1600+i, 12, 31)
833 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
834 if d.isocalendar()[1] == 53:
835 L.append(i)
836 self.assertEqual(L, iso_long_years)
837
838 def test_isoformat(self):
839 t = self.theclass(2, 3, 2)
840 self.assertEqual(t.isoformat(), "0002-03-02")
841
842 def test_ctime(self):
843 t = self.theclass(2002, 3, 2)
844 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
845
846 def test_strftime(self):
847 t = self.theclass(2005, 3, 2)
848 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000849 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +0000850 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000851
852 self.assertRaises(TypeError, t.strftime) # needs an arg
853 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
854 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
855
Georg Brandlf78e02b2008-06-10 17:40:04 +0000856 # test that unicode input is allowed (issue 2782)
857 self.assertEqual(t.strftime("%m"), "03")
858
Tim Peters2a799bf2002-12-16 20:18:38 +0000859 # A naive object replaces %z and %Z w/ empty strings.
860 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
861
Benjamin Petersone1cdfd72009-01-18 21:02:37 +0000862 #make sure that invalid format specifiers are handled correctly
863 #self.assertRaises(ValueError, t.strftime, "%e")
864 #self.assertRaises(ValueError, t.strftime, "%")
865 #self.assertRaises(ValueError, t.strftime, "%#")
866
867 #oh well, some systems just ignore those invalid ones.
868 #at least, excercise them to make sure that no crashes
869 #are generated
870 for f in ["%e", "%", "%#"]:
871 try:
872 t.strftime(f)
873 except ValueError:
874 pass
875
876 #check that this standard extension works
877 t.strftime("%f")
878
Georg Brandlf78e02b2008-06-10 17:40:04 +0000879
Eric Smith1ba31142007-09-11 18:06:02 +0000880 def test_format(self):
881 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000882 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000883
884 # check that a derived class's __str__() gets called
885 class A(self.theclass):
886 def __str__(self):
887 return 'A'
888 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000889 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +0000890
891 # check that a derived class's strftime gets called
892 class B(self.theclass):
893 def strftime(self, format_spec):
894 return 'B'
895 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +0000896 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +0000897
898 for fmt in ["m:%m d:%d y:%y",
899 "m:%m d:%d y:%y H:%H M:%M S:%S",
900 "%z %Z",
901 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +0000902 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
903 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
904 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +0000905
Tim Peters2a799bf2002-12-16 20:18:38 +0000906 def test_resolution_info(self):
Georg Brandlab91fde2009-08-13 08:51:18 +0000907 self.assertTrue(isinstance(self.theclass.min, self.theclass))
908 self.assertTrue(isinstance(self.theclass.max, self.theclass))
909 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
910 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000911
912 def test_extreme_timedelta(self):
913 big = self.theclass.max - self.theclass.min
914 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
915 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
916 # n == 315537897599999999 ~= 2**58.13
917 justasbig = timedelta(0, 0, n)
918 self.assertEqual(big, justasbig)
919 self.assertEqual(self.theclass.min + big, self.theclass.max)
920 self.assertEqual(self.theclass.max - big, self.theclass.min)
921
922 def test_timetuple(self):
923 for i in range(7):
924 # January 2, 1956 is a Monday (0)
925 d = self.theclass(1956, 1, 2+i)
926 t = d.timetuple()
927 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
928 # February 1, 1956 is a Wednesday (2)
929 d = self.theclass(1956, 2, 1+i)
930 t = d.timetuple()
931 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
932 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
933 # of the year.
934 d = self.theclass(1956, 3, 1+i)
935 t = d.timetuple()
936 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
937 self.assertEqual(t.tm_year, 1956)
938 self.assertEqual(t.tm_mon, 3)
939 self.assertEqual(t.tm_mday, 1+i)
940 self.assertEqual(t.tm_hour, 0)
941 self.assertEqual(t.tm_min, 0)
942 self.assertEqual(t.tm_sec, 0)
943 self.assertEqual(t.tm_wday, (3+i)%7)
944 self.assertEqual(t.tm_yday, 61+i)
945 self.assertEqual(t.tm_isdst, -1)
946
947 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000948 args = 6, 7, 23
949 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000950 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000951 green = pickler.dumps(orig, proto)
952 derived = unpickler.loads(green)
953 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000954
955 def test_compare(self):
956 t1 = self.theclass(2, 3, 4)
957 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000958 self.assertEqual(t1, t2)
Georg Brandlab91fde2009-08-13 08:51:18 +0000959 self.assertTrue(t1 <= t2)
960 self.assertTrue(t1 >= t2)
961 self.assertTrue(not t1 != t2)
962 self.assertTrue(not t1 < t2)
963 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000964
965 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
966 t2 = self.theclass(*args) # this is larger than t1
Georg Brandlab91fde2009-08-13 08:51:18 +0000967 self.assertTrue(t1 < t2)
968 self.assertTrue(t2 > t1)
969 self.assertTrue(t1 <= t2)
970 self.assertTrue(t2 >= t1)
971 self.assertTrue(t1 != t2)
972 self.assertTrue(t2 != t1)
973 self.assertTrue(not t1 == t2)
974 self.assertTrue(not t2 == t1)
975 self.assertTrue(not t1 > t2)
976 self.assertTrue(not t2 < t1)
977 self.assertTrue(not t1 >= t2)
978 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000979
Tim Peters68124bb2003-02-08 03:46:31 +0000980 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000981 self.assertEqual(t1 == badarg, False)
982 self.assertEqual(t1 != badarg, True)
983 self.assertEqual(badarg == t1, False)
984 self.assertEqual(badarg != t1, True)
985
Tim Peters2a799bf2002-12-16 20:18:38 +0000986 self.assertRaises(TypeError, lambda: t1 < badarg)
987 self.assertRaises(TypeError, lambda: t1 > badarg)
988 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000989 self.assertRaises(TypeError, lambda: badarg <= t1)
990 self.assertRaises(TypeError, lambda: badarg < t1)
991 self.assertRaises(TypeError, lambda: badarg > t1)
992 self.assertRaises(TypeError, lambda: badarg >= t1)
993
Tim Peters8d81a012003-01-24 22:36:34 +0000994 def test_mixed_compare(self):
995 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +0000996
997 # Our class can be compared for equality to other classes
998 self.assertEqual(our == 1, False)
999 self.assertEqual(1 == our, False)
1000 self.assertEqual(our != 1, True)
1001 self.assertEqual(1 != our, True)
1002
1003 # But the ordering is undefined
1004 self.assertRaises(TypeError, lambda: our < 1)
1005 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001006
Guido van Rossum19960592006-08-24 17:29:38 +00001007 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001008
Guido van Rossum19960592006-08-24 17:29:38 +00001009 class SomeClass:
1010 pass
1011
1012 their = SomeClass()
1013 self.assertEqual(our == their, False)
1014 self.assertEqual(their == our, False)
1015 self.assertEqual(our != their, True)
1016 self.assertEqual(their != our, True)
1017 self.assertRaises(TypeError, lambda: our < their)
1018 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001019
Guido van Rossum19960592006-08-24 17:29:38 +00001020 # However, if the other class explicitly defines ordering
1021 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001022
Guido van Rossum19960592006-08-24 17:29:38 +00001023 class LargerThanAnything:
1024 def __lt__(self, other):
1025 return False
1026 def __le__(self, other):
1027 return isinstance(other, LargerThanAnything)
1028 def __eq__(self, other):
1029 return isinstance(other, LargerThanAnything)
1030 def __ne__(self, other):
1031 return not isinstance(other, LargerThanAnything)
1032 def __gt__(self, other):
1033 return not isinstance(other, LargerThanAnything)
1034 def __ge__(self, other):
1035 return True
1036
1037 their = LargerThanAnything()
1038 self.assertEqual(our == their, False)
1039 self.assertEqual(their == our, False)
1040 self.assertEqual(our != their, True)
1041 self.assertEqual(their != our, True)
1042 self.assertEqual(our < their, True)
1043 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001044
Tim Peters2a799bf2002-12-16 20:18:38 +00001045 def test_bool(self):
1046 # All dates are considered true.
Georg Brandlab91fde2009-08-13 08:51:18 +00001047 self.assertTrue(self.theclass.min)
1048 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001049
Guido van Rossum04110fb2007-08-24 16:32:05 +00001050 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001051 # For nasty technical reasons, we can't handle years before 1900.
1052 cls = self.theclass
1053 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1054 for y in 1, 49, 51, 99, 100, 1000, 1899:
1055 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001056
1057 def test_replace(self):
1058 cls = self.theclass
1059 args = [1, 2, 3]
1060 base = cls(*args)
1061 self.assertEqual(base, base.replace())
1062
1063 i = 0
1064 for name, newval in (("year", 2),
1065 ("month", 3),
1066 ("day", 4)):
1067 newargs = args[:]
1068 newargs[i] = newval
1069 expected = cls(*newargs)
1070 got = base.replace(**{name: newval})
1071 self.assertEqual(expected, got)
1072 i += 1
1073
1074 # Out of bounds.
1075 base = cls(2000, 2, 29)
1076 self.assertRaises(ValueError, base.replace, year=2001)
1077
Tim Petersa98924a2003-05-17 05:55:19 +00001078 def test_subclass_date(self):
1079
1080 class C(self.theclass):
1081 theAnswer = 42
1082
1083 def __new__(cls, *args, **kws):
1084 temp = kws.copy()
1085 extra = temp.pop('extra')
1086 result = self.theclass.__new__(cls, *args, **temp)
1087 result.extra = extra
1088 return result
1089
1090 def newmeth(self, start):
1091 return start + self.year + self.month
1092
1093 args = 2003, 4, 14
1094
1095 dt1 = self.theclass(*args)
1096 dt2 = C(*args, **{'extra': 7})
1097
1098 self.assertEqual(dt2.__class__, C)
1099 self.assertEqual(dt2.theAnswer, 42)
1100 self.assertEqual(dt2.extra, 7)
1101 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1102 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1103
Tim Peters604c0132004-06-07 23:04:33 +00001104 def test_pickling_subclass_date(self):
1105
1106 args = 6, 7, 23
1107 orig = SubclassDate(*args)
1108 for pickler, unpickler, proto in pickle_choices:
1109 green = pickler.dumps(orig, proto)
1110 derived = unpickler.loads(green)
1111 self.assertEqual(orig, derived)
1112
Tim Peters3f606292004-03-21 23:38:41 +00001113 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001114 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001115 # This is a low-overhead backdoor. A user can (by intent or
1116 # mistake) pass a string directly, which (if it's the right length)
1117 # will get treated like a pickle, and bypass the normal sanity
1118 # checks in the constructor. This can create insane objects.
1119 # The constructor doesn't want to burn the time to validate all
1120 # fields, but does check the month field. This stops, e.g.,
1121 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001122 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001123 if not issubclass(self.theclass, datetime):
1124 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001125 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001126 self.assertRaises(TypeError, self.theclass,
1127 base[:2] + month_byte + base[3:])
1128 for ord_byte in range(1, 13):
1129 # This shouldn't blow up because of the month byte alone. If
1130 # the implementation changes to do more-careful checking, it may
1131 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001132 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001133
Tim Peters2a799bf2002-12-16 20:18:38 +00001134#############################################################################
1135# datetime tests
1136
Tim Peters604c0132004-06-07 23:04:33 +00001137class SubclassDatetime(datetime):
1138 sub_var = 1
1139
Tim Peters2a799bf2002-12-16 20:18:38 +00001140class TestDateTime(TestDate):
1141
1142 theclass = datetime
1143
1144 def test_basic_attributes(self):
1145 dt = self.theclass(2002, 3, 1, 12, 0)
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, 0)
1151 self.assertEqual(dt.second, 0)
1152 self.assertEqual(dt.microsecond, 0)
1153
1154 def test_basic_attributes_nonzero(self):
1155 # Make sure all attributes are non-zero so bugs in
1156 # bit-shifting access show up.
1157 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1158 self.assertEqual(dt.year, 2002)
1159 self.assertEqual(dt.month, 3)
1160 self.assertEqual(dt.day, 1)
1161 self.assertEqual(dt.hour, 12)
1162 self.assertEqual(dt.minute, 59)
1163 self.assertEqual(dt.second, 59)
1164 self.assertEqual(dt.microsecond, 8000)
1165
1166 def test_roundtrip(self):
1167 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1168 self.theclass.now()):
1169 # Verify dt -> string -> datetime identity.
1170 s = repr(dt)
Georg Brandlab91fde2009-08-13 08:51:18 +00001171 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001172 s = s[9:]
1173 dt2 = eval(s)
1174 self.assertEqual(dt, dt2)
1175
1176 # Verify identity via reconstructing from pieces.
1177 dt2 = self.theclass(dt.year, dt.month, dt.day,
1178 dt.hour, dt.minute, dt.second,
1179 dt.microsecond)
1180 self.assertEqual(dt, dt2)
1181
1182 def test_isoformat(self):
1183 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1184 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1185 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1186 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1187 # str is ISO format with the separator forced to a blank.
1188 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1189
1190 t = self.theclass(2, 3, 2)
1191 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1192 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1193 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1194 # str is ISO format with the separator forced to a blank.
1195 self.assertEqual(str(t), "0002-03-02 00:00:00")
1196
Eric Smith1ba31142007-09-11 18:06:02 +00001197 def test_format(self):
1198 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001199 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001200
1201 # check that a derived class's __str__() gets called
1202 class A(self.theclass):
1203 def __str__(self):
1204 return 'A'
1205 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001206 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001207
1208 # check that a derived class's strftime gets called
1209 class B(self.theclass):
1210 def strftime(self, format_spec):
1211 return 'B'
1212 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001213 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001214
1215 for fmt in ["m:%m d:%d y:%y",
1216 "m:%m d:%d y:%y H:%H M:%M S:%S",
1217 "%z %Z",
1218 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001219 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1220 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1221 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001222
Tim Peters2a799bf2002-12-16 20:18:38 +00001223 def test_more_ctime(self):
1224 # Test fields that TestDate doesn't touch.
1225 import time
1226
1227 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1228 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1229 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1230 # out. The difference is that t.ctime() produces " 2" for the day,
1231 # but platform ctime() produces "02" for the day. According to
1232 # C99, t.ctime() is correct here.
1233 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1234
1235 # So test a case where that difference doesn't matter.
1236 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1237 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1238
1239 def test_tz_independent_comparing(self):
1240 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1241 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1242 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1243 self.assertEqual(dt1, dt3)
Georg Brandlab91fde2009-08-13 08:51:18 +00001244 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001245
1246 # Make sure comparison doesn't forget microseconds, and isn't done
1247 # via comparing a float timestamp (an IEEE double doesn't have enough
1248 # precision to span microsecond resolution across years 1 thru 9999,
1249 # so comparing via timestamp necessarily calls some distinct values
1250 # equal).
1251 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1252 us = timedelta(microseconds=1)
1253 dt2 = dt1 + us
1254 self.assertEqual(dt2 - dt1, us)
Georg Brandlab91fde2009-08-13 08:51:18 +00001255 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001256
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001257 def test_strftime_with_bad_tzname_replace(self):
1258 # verify ok if tzinfo.tzname().replace() returns a non-string
1259 class MyTzInfo(FixedOffset):
1260 def tzname(self, dt):
1261 class MyStr(str):
1262 def replace(self, *args):
1263 return None
1264 return MyStr('name')
1265 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1266 self.assertRaises(TypeError, t.strftime, '%Z')
1267
Tim Peters2a799bf2002-12-16 20:18:38 +00001268 def test_bad_constructor_arguments(self):
1269 # bad years
1270 self.theclass(MINYEAR, 1, 1) # no exception
1271 self.theclass(MAXYEAR, 1, 1) # no exception
1272 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1273 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1274 # bad months
1275 self.theclass(2000, 1, 1) # no exception
1276 self.theclass(2000, 12, 1) # no exception
1277 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1278 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1279 # bad days
1280 self.theclass(2000, 2, 29) # no exception
1281 self.theclass(2004, 2, 29) # no exception
1282 self.theclass(2400, 2, 29) # no exception
1283 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1284 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1285 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1286 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1287 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1288 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1289 # bad hours
1290 self.theclass(2000, 1, 31, 0) # no exception
1291 self.theclass(2000, 1, 31, 23) # no exception
1292 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1293 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1294 # bad minutes
1295 self.theclass(2000, 1, 31, 23, 0) # no exception
1296 self.theclass(2000, 1, 31, 23, 59) # no exception
1297 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1298 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1299 # bad seconds
1300 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1301 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1302 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1303 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1304 # bad microseconds
1305 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1306 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1307 self.assertRaises(ValueError, self.theclass,
1308 2000, 1, 31, 23, 59, 59, -1)
1309 self.assertRaises(ValueError, self.theclass,
1310 2000, 1, 31, 23, 59, 59,
1311 1000000)
1312
1313 def test_hash_equality(self):
1314 d = self.theclass(2000, 12, 31, 23, 30, 17)
1315 e = self.theclass(2000, 12, 31, 23, 30, 17)
1316 self.assertEqual(d, e)
1317 self.assertEqual(hash(d), hash(e))
1318
1319 dic = {d: 1}
1320 dic[e] = 2
1321 self.assertEqual(len(dic), 1)
1322 self.assertEqual(dic[d], 2)
1323 self.assertEqual(dic[e], 2)
1324
1325 d = self.theclass(2001, 1, 1, 0, 5, 17)
1326 e = self.theclass(2001, 1, 1, 0, 5, 17)
1327 self.assertEqual(d, e)
1328 self.assertEqual(hash(d), hash(e))
1329
1330 dic = {d: 1}
1331 dic[e] = 2
1332 self.assertEqual(len(dic), 1)
1333 self.assertEqual(dic[d], 2)
1334 self.assertEqual(dic[e], 2)
1335
1336 def test_computations(self):
1337 a = self.theclass(2002, 1, 31)
1338 b = self.theclass(1956, 1, 31)
1339 diff = a-b
1340 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1341 self.assertEqual(diff.seconds, 0)
1342 self.assertEqual(diff.microseconds, 0)
1343 a = self.theclass(2002, 3, 2, 17, 6)
1344 millisec = timedelta(0, 0, 1000)
1345 hour = timedelta(0, 3600)
1346 day = timedelta(1)
1347 week = timedelta(7)
1348 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1349 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1350 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1351 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1352 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1353 self.assertEqual(a - hour, a + -hour)
1354 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1355 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1356 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1357 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1358 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1359 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1360 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1361 self.assertEqual((a + week) - a, week)
1362 self.assertEqual((a + day) - a, day)
1363 self.assertEqual((a + hour) - a, hour)
1364 self.assertEqual((a + millisec) - a, millisec)
1365 self.assertEqual((a - week) - a, -week)
1366 self.assertEqual((a - day) - a, -day)
1367 self.assertEqual((a - hour) - a, -hour)
1368 self.assertEqual((a - millisec) - a, -millisec)
1369 self.assertEqual(a - (a + week), -week)
1370 self.assertEqual(a - (a + day), -day)
1371 self.assertEqual(a - (a + hour), -hour)
1372 self.assertEqual(a - (a + millisec), -millisec)
1373 self.assertEqual(a - (a - week), week)
1374 self.assertEqual(a - (a - day), day)
1375 self.assertEqual(a - (a - hour), hour)
1376 self.assertEqual(a - (a - millisec), millisec)
1377 self.assertEqual(a + (week + day + hour + millisec),
1378 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1379 self.assertEqual(a + (week + day + hour + millisec),
1380 (((a + week) + day) + hour) + millisec)
1381 self.assertEqual(a - (week + day + hour + millisec),
1382 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1383 self.assertEqual(a - (week + day + hour + millisec),
1384 (((a - week) - day) - hour) - millisec)
1385 # Add/sub ints, longs, floats should be illegal
Guido van Rossume2a383d2007-01-15 16:59:06 +00001386 for i in 1, 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001387 self.assertRaises(TypeError, lambda: a+i)
1388 self.assertRaises(TypeError, lambda: a-i)
1389 self.assertRaises(TypeError, lambda: i+a)
1390 self.assertRaises(TypeError, lambda: i-a)
1391
1392 # delta - datetime is senseless.
1393 self.assertRaises(TypeError, lambda: day - a)
1394 # mixing datetime and (delta or datetime) via * or // is senseless
1395 self.assertRaises(TypeError, lambda: day * a)
1396 self.assertRaises(TypeError, lambda: a * day)
1397 self.assertRaises(TypeError, lambda: day // a)
1398 self.assertRaises(TypeError, lambda: a // day)
1399 self.assertRaises(TypeError, lambda: a * a)
1400 self.assertRaises(TypeError, lambda: a // a)
1401 # datetime + datetime is senseless
1402 self.assertRaises(TypeError, lambda: a + a)
1403
1404 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001405 args = 6, 7, 23, 20, 59, 1, 64**2
1406 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001407 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001408 green = pickler.dumps(orig, proto)
1409 derived = unpickler.loads(green)
1410 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001411
Guido van Rossum275666f2003-02-07 21:49:01 +00001412 def test_more_pickling(self):
1413 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1414 s = pickle.dumps(a)
1415 b = pickle.loads(s)
1416 self.assertEqual(b.year, 2003)
1417 self.assertEqual(b.month, 2)
1418 self.assertEqual(b.day, 7)
1419
Tim Peters604c0132004-06-07 23:04:33 +00001420 def test_pickling_subclass_datetime(self):
1421 args = 6, 7, 23, 20, 59, 1, 64**2
1422 orig = SubclassDatetime(*args)
1423 for pickler, unpickler, proto in pickle_choices:
1424 green = pickler.dumps(orig, proto)
1425 derived = unpickler.loads(green)
1426 self.assertEqual(orig, derived)
1427
Tim Peters2a799bf2002-12-16 20:18:38 +00001428 def test_more_compare(self):
1429 # The test_compare() inherited from TestDate covers the error cases.
1430 # We just want to test lexicographic ordering on the members datetime
1431 # has that date lacks.
1432 args = [2000, 11, 29, 20, 58, 16, 999998]
1433 t1 = self.theclass(*args)
1434 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001435 self.assertEqual(t1, t2)
Georg Brandlab91fde2009-08-13 08:51:18 +00001436 self.assertTrue(t1 <= t2)
1437 self.assertTrue(t1 >= t2)
1438 self.assertTrue(not t1 != t2)
1439 self.assertTrue(not t1 < t2)
1440 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001441
1442 for i in range(len(args)):
1443 newargs = args[:]
1444 newargs[i] = args[i] + 1
1445 t2 = self.theclass(*newargs) # this is larger than t1
Georg Brandlab91fde2009-08-13 08:51:18 +00001446 self.assertTrue(t1 < t2)
1447 self.assertTrue(t2 > t1)
1448 self.assertTrue(t1 <= t2)
1449 self.assertTrue(t2 >= t1)
1450 self.assertTrue(t1 != t2)
1451 self.assertTrue(t2 != t1)
1452 self.assertTrue(not t1 == t2)
1453 self.assertTrue(not t2 == t1)
1454 self.assertTrue(not t1 > t2)
1455 self.assertTrue(not t2 < t1)
1456 self.assertTrue(not t1 >= t2)
1457 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001458
1459
1460 # A helper for timestamp constructor tests.
1461 def verify_field_equality(self, expected, got):
1462 self.assertEqual(expected.tm_year, got.year)
1463 self.assertEqual(expected.tm_mon, got.month)
1464 self.assertEqual(expected.tm_mday, got.day)
1465 self.assertEqual(expected.tm_hour, got.hour)
1466 self.assertEqual(expected.tm_min, got.minute)
1467 self.assertEqual(expected.tm_sec, got.second)
1468
1469 def test_fromtimestamp(self):
1470 import time
1471
1472 ts = time.time()
1473 expected = time.localtime(ts)
1474 got = self.theclass.fromtimestamp(ts)
1475 self.verify_field_equality(expected, got)
1476
1477 def test_utcfromtimestamp(self):
1478 import time
1479
1480 ts = time.time()
1481 expected = time.gmtime(ts)
1482 got = self.theclass.utcfromtimestamp(ts)
1483 self.verify_field_equality(expected, got)
1484
Thomas Wouters477c8d52006-05-27 19:21:47 +00001485 def test_microsecond_rounding(self):
1486 # Test whether fromtimestamp "rounds up" floats that are less
1487 # than one microsecond smaller than an integer.
Ezio Melotti19f2aeb2010-11-21 01:30:29 +00001488 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1489 self.theclass.fromtimestamp(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001490
Tim Peters1b6f7a92004-06-20 02:50:16 +00001491 def test_insane_fromtimestamp(self):
1492 # It's possible that some platform maps time_t to double,
1493 # and that this test will fail there. This test should
1494 # exempt such platforms (provided they return reasonable
1495 # results!).
1496 for insane in -1e200, 1e200:
1497 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1498 insane)
1499
1500 def test_insane_utcfromtimestamp(self):
1501 # It's possible that some platform maps time_t to double,
1502 # and that this test will fail there. This test should
1503 # exempt such platforms (provided they return reasonable
1504 # results!).
1505 for insane in -1e200, 1e200:
1506 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1507 insane)
Alexander Belopolsky5ca31ed2010-05-31 16:21:02 +00001508 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001509 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001510 # The result is tz-dependent; at least test that this doesn't
1511 # fail (like it did before bug 1646728 was fixed).
1512 self.theclass.fromtimestamp(-1.05)
1513
Alexander Belopolsky5ca31ed2010-05-31 16:21:02 +00001514 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001515 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001516 d = self.theclass.utcfromtimestamp(-1.05)
Ezio Melotti19f2aeb2010-11-21 01:30:29 +00001517 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001518
Tim Peters2a799bf2002-12-16 20:18:38 +00001519 def test_utcnow(self):
1520 import time
1521
1522 # Call it a success if utcnow() and utcfromtimestamp() are within
1523 # a second of each other.
1524 tolerance = timedelta(seconds=1)
1525 for dummy in range(3):
1526 from_now = self.theclass.utcnow()
1527 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1528 if abs(from_timestamp - from_now) <= tolerance:
1529 break
1530 # Else try again a few times.
Georg Brandlab91fde2009-08-13 08:51:18 +00001531 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001532
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001533 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001534 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001535
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001536 string = '2004-12-01 13:02:47.197'
1537 format = '%Y-%m-%d %H:%M:%S.%f'
1538 result, frac = _strptime._strptime(string, format)
1539 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001540 got = self.theclass.strptime(string, format)
1541 self.assertEqual(expected, got)
1542
Tim Peters2a799bf2002-12-16 20:18:38 +00001543 def test_more_timetuple(self):
1544 # This tests fields beyond those tested by the TestDate.test_timetuple.
1545 t = self.theclass(2004, 12, 31, 6, 22, 33)
1546 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1547 self.assertEqual(t.timetuple(),
1548 (t.year, t.month, t.day,
1549 t.hour, t.minute, t.second,
1550 t.weekday(),
1551 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1552 -1))
1553 tt = t.timetuple()
1554 self.assertEqual(tt.tm_year, t.year)
1555 self.assertEqual(tt.tm_mon, t.month)
1556 self.assertEqual(tt.tm_mday, t.day)
1557 self.assertEqual(tt.tm_hour, t.hour)
1558 self.assertEqual(tt.tm_min, t.minute)
1559 self.assertEqual(tt.tm_sec, t.second)
1560 self.assertEqual(tt.tm_wday, t.weekday())
1561 self.assertEqual(tt.tm_yday, t.toordinal() -
1562 date(t.year, 1, 1).toordinal() + 1)
1563 self.assertEqual(tt.tm_isdst, -1)
1564
1565 def test_more_strftime(self):
1566 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001567 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1568 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1569 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001570
1571 def test_extract(self):
1572 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1573 self.assertEqual(dt.date(), date(2002, 3, 4))
1574 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1575
1576 def test_combine(self):
1577 d = date(2002, 3, 4)
1578 t = time(18, 45, 3, 1234)
1579 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1580 combine = self.theclass.combine
1581 dt = combine(d, t)
1582 self.assertEqual(dt, expected)
1583
1584 dt = combine(time=t, date=d)
1585 self.assertEqual(dt, expected)
1586
1587 self.assertEqual(d, dt.date())
1588 self.assertEqual(t, dt.time())
1589 self.assertEqual(dt, combine(dt.date(), dt.time()))
1590
1591 self.assertRaises(TypeError, combine) # need an arg
1592 self.assertRaises(TypeError, combine, d) # need two args
1593 self.assertRaises(TypeError, combine, t, d) # args reversed
1594 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1595 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1596
Tim Peters12bf3392002-12-24 05:41:27 +00001597 def test_replace(self):
1598 cls = self.theclass
1599 args = [1, 2, 3, 4, 5, 6, 7]
1600 base = cls(*args)
1601 self.assertEqual(base, base.replace())
1602
1603 i = 0
1604 for name, newval in (("year", 2),
1605 ("month", 3),
1606 ("day", 4),
1607 ("hour", 5),
1608 ("minute", 6),
1609 ("second", 7),
1610 ("microsecond", 8)):
1611 newargs = args[:]
1612 newargs[i] = newval
1613 expected = cls(*newargs)
1614 got = base.replace(**{name: newval})
1615 self.assertEqual(expected, got)
1616 i += 1
1617
1618 # Out of bounds.
1619 base = cls(2000, 2, 29)
1620 self.assertRaises(ValueError, base.replace, year=2001)
1621
Tim Peters80475bb2002-12-25 07:40:55 +00001622 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001623 # Pretty boring! The TZ test is more interesting here. astimezone()
1624 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001625 dt = self.theclass.now()
1626 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001627 self.assertRaises(TypeError, dt.astimezone) # not enough args
1628 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1629 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001630 self.assertRaises(ValueError, dt.astimezone, f) # naive
1631 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001632
Tim Peters52dcce22003-01-23 16:36:11 +00001633 class Bogus(tzinfo):
1634 def utcoffset(self, dt): return None
1635 def dst(self, dt): return timedelta(0)
1636 bog = Bogus()
1637 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1638
1639 class AlsoBogus(tzinfo):
1640 def utcoffset(self, dt): return timedelta(0)
1641 def dst(self, dt): return None
1642 alsobog = AlsoBogus()
1643 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001644
Tim Petersa98924a2003-05-17 05:55:19 +00001645 def test_subclass_datetime(self):
1646
1647 class C(self.theclass):
1648 theAnswer = 42
1649
1650 def __new__(cls, *args, **kws):
1651 temp = kws.copy()
1652 extra = temp.pop('extra')
1653 result = self.theclass.__new__(cls, *args, **temp)
1654 result.extra = extra
1655 return result
1656
1657 def newmeth(self, start):
1658 return start + self.year + self.month + self.second
1659
1660 args = 2003, 4, 14, 12, 13, 41
1661
1662 dt1 = self.theclass(*args)
1663 dt2 = C(*args, **{'extra': 7})
1664
1665 self.assertEqual(dt2.__class__, C)
1666 self.assertEqual(dt2.theAnswer, 42)
1667 self.assertEqual(dt2.extra, 7)
1668 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1669 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1670 dt1.second - 7)
1671
Tim Peters604c0132004-06-07 23:04:33 +00001672class SubclassTime(time):
1673 sub_var = 1
1674
Guido van Rossumd8faa362007-04-27 19:54:29 +00001675class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001676
1677 theclass = time
1678
1679 def test_basic_attributes(self):
1680 t = self.theclass(12, 0)
1681 self.assertEqual(t.hour, 12)
1682 self.assertEqual(t.minute, 0)
1683 self.assertEqual(t.second, 0)
1684 self.assertEqual(t.microsecond, 0)
1685
1686 def test_basic_attributes_nonzero(self):
1687 # Make sure all attributes are non-zero so bugs in
1688 # bit-shifting access show up.
1689 t = self.theclass(12, 59, 59, 8000)
1690 self.assertEqual(t.hour, 12)
1691 self.assertEqual(t.minute, 59)
1692 self.assertEqual(t.second, 59)
1693 self.assertEqual(t.microsecond, 8000)
1694
1695 def test_roundtrip(self):
1696 t = self.theclass(1, 2, 3, 4)
1697
1698 # Verify t -> string -> time identity.
1699 s = repr(t)
Georg Brandlab91fde2009-08-13 08:51:18 +00001700 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001701 s = s[9:]
1702 t2 = eval(s)
1703 self.assertEqual(t, t2)
1704
1705 # Verify identity via reconstructing from pieces.
1706 t2 = self.theclass(t.hour, t.minute, t.second,
1707 t.microsecond)
1708 self.assertEqual(t, t2)
1709
1710 def test_comparing(self):
1711 args = [1, 2, 3, 4]
1712 t1 = self.theclass(*args)
1713 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001714 self.assertEqual(t1, t2)
Georg Brandlab91fde2009-08-13 08:51:18 +00001715 self.assertTrue(t1 <= t2)
1716 self.assertTrue(t1 >= t2)
1717 self.assertTrue(not t1 != t2)
1718 self.assertTrue(not t1 < t2)
1719 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001720
1721 for i in range(len(args)):
1722 newargs = args[:]
1723 newargs[i] = args[i] + 1
1724 t2 = self.theclass(*newargs) # this is larger than t1
Georg Brandlab91fde2009-08-13 08:51:18 +00001725 self.assertTrue(t1 < t2)
1726 self.assertTrue(t2 > t1)
1727 self.assertTrue(t1 <= t2)
1728 self.assertTrue(t2 >= t1)
1729 self.assertTrue(t1 != t2)
1730 self.assertTrue(t2 != t1)
1731 self.assertTrue(not t1 == t2)
1732 self.assertTrue(not t2 == t1)
1733 self.assertTrue(not t1 > t2)
1734 self.assertTrue(not t2 < t1)
1735 self.assertTrue(not t1 >= t2)
1736 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001737
Tim Peters68124bb2003-02-08 03:46:31 +00001738 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001739 self.assertEqual(t1 == badarg, False)
1740 self.assertEqual(t1 != badarg, True)
1741 self.assertEqual(badarg == t1, False)
1742 self.assertEqual(badarg != t1, True)
1743
Tim Peters2a799bf2002-12-16 20:18:38 +00001744 self.assertRaises(TypeError, lambda: t1 <= badarg)
1745 self.assertRaises(TypeError, lambda: t1 < badarg)
1746 self.assertRaises(TypeError, lambda: t1 > badarg)
1747 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001748 self.assertRaises(TypeError, lambda: badarg <= t1)
1749 self.assertRaises(TypeError, lambda: badarg < t1)
1750 self.assertRaises(TypeError, lambda: badarg > t1)
1751 self.assertRaises(TypeError, lambda: badarg >= t1)
1752
1753 def test_bad_constructor_arguments(self):
1754 # bad hours
1755 self.theclass(0, 0) # no exception
1756 self.theclass(23, 0) # no exception
1757 self.assertRaises(ValueError, self.theclass, -1, 0)
1758 self.assertRaises(ValueError, self.theclass, 24, 0)
1759 # bad minutes
1760 self.theclass(23, 0) # no exception
1761 self.theclass(23, 59) # no exception
1762 self.assertRaises(ValueError, self.theclass, 23, -1)
1763 self.assertRaises(ValueError, self.theclass, 23, 60)
1764 # bad seconds
1765 self.theclass(23, 59, 0) # no exception
1766 self.theclass(23, 59, 59) # no exception
1767 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1768 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1769 # bad microseconds
1770 self.theclass(23, 59, 59, 0) # no exception
1771 self.theclass(23, 59, 59, 999999) # no exception
1772 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1773 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1774
1775 def test_hash_equality(self):
1776 d = self.theclass(23, 30, 17)
1777 e = self.theclass(23, 30, 17)
1778 self.assertEqual(d, e)
1779 self.assertEqual(hash(d), hash(e))
1780
1781 dic = {d: 1}
1782 dic[e] = 2
1783 self.assertEqual(len(dic), 1)
1784 self.assertEqual(dic[d], 2)
1785 self.assertEqual(dic[e], 2)
1786
1787 d = self.theclass(0, 5, 17)
1788 e = self.theclass(0, 5, 17)
1789 self.assertEqual(d, e)
1790 self.assertEqual(hash(d), hash(e))
1791
1792 dic = {d: 1}
1793 dic[e] = 2
1794 self.assertEqual(len(dic), 1)
1795 self.assertEqual(dic[d], 2)
1796 self.assertEqual(dic[e], 2)
1797
1798 def test_isoformat(self):
1799 t = self.theclass(4, 5, 1, 123)
1800 self.assertEqual(t.isoformat(), "04:05:01.000123")
1801 self.assertEqual(t.isoformat(), str(t))
1802
1803 t = self.theclass()
1804 self.assertEqual(t.isoformat(), "00:00:00")
1805 self.assertEqual(t.isoformat(), str(t))
1806
1807 t = self.theclass(microsecond=1)
1808 self.assertEqual(t.isoformat(), "00:00:00.000001")
1809 self.assertEqual(t.isoformat(), str(t))
1810
1811 t = self.theclass(microsecond=10)
1812 self.assertEqual(t.isoformat(), "00:00:00.000010")
1813 self.assertEqual(t.isoformat(), str(t))
1814
1815 t = self.theclass(microsecond=100)
1816 self.assertEqual(t.isoformat(), "00:00:00.000100")
1817 self.assertEqual(t.isoformat(), str(t))
1818
1819 t = self.theclass(microsecond=1000)
1820 self.assertEqual(t.isoformat(), "00:00:00.001000")
1821 self.assertEqual(t.isoformat(), str(t))
1822
1823 t = self.theclass(microsecond=10000)
1824 self.assertEqual(t.isoformat(), "00:00:00.010000")
1825 self.assertEqual(t.isoformat(), str(t))
1826
1827 t = self.theclass(microsecond=100000)
1828 self.assertEqual(t.isoformat(), "00:00:00.100000")
1829 self.assertEqual(t.isoformat(), str(t))
1830
Thomas Wouterscf297e42007-02-23 15:07:44 +00001831 def test_1653736(self):
1832 # verify it doesn't accept extra keyword arguments
1833 t = self.theclass(second=1)
1834 self.assertRaises(TypeError, t.isoformat, foo=3)
1835
Tim Peters2a799bf2002-12-16 20:18:38 +00001836 def test_strftime(self):
1837 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001838 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001839 # A naive object replaces %z and %Z with empty strings.
1840 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1841
Eric Smith1ba31142007-09-11 18:06:02 +00001842 def test_format(self):
1843 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001844 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001845
1846 # check that a derived class's __str__() gets called
1847 class A(self.theclass):
1848 def __str__(self):
1849 return 'A'
1850 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001851 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001852
1853 # check that a derived class's strftime gets called
1854 class B(self.theclass):
1855 def strftime(self, format_spec):
1856 return 'B'
1857 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001858 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00001859
1860 for fmt in ['%H %M %S',
1861 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001862 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1863 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1864 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001865
Tim Peters2a799bf2002-12-16 20:18:38 +00001866 def test_str(self):
1867 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1868 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1869 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1870 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1871 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1872
1873 def test_repr(self):
1874 name = 'datetime.' + self.theclass.__name__
1875 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1876 "%s(1, 2, 3, 4)" % name)
1877 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1878 "%s(10, 2, 3, 4000)" % name)
1879 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1880 "%s(0, 2, 3, 400000)" % name)
1881 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1882 "%s(12, 2, 3)" % name)
1883 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1884 "%s(23, 15)" % name)
1885
1886 def test_resolution_info(self):
Georg Brandlab91fde2009-08-13 08:51:18 +00001887 self.assertTrue(isinstance(self.theclass.min, self.theclass))
1888 self.assertTrue(isinstance(self.theclass.max, self.theclass))
1889 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
1890 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001891
1892 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001893 args = 20, 59, 16, 64**2
1894 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001895 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001896 green = pickler.dumps(orig, proto)
1897 derived = unpickler.loads(green)
1898 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001899
Tim Peters604c0132004-06-07 23:04:33 +00001900 def test_pickling_subclass_time(self):
1901 args = 20, 59, 16, 64**2
1902 orig = SubclassTime(*args)
1903 for pickler, unpickler, proto in pickle_choices:
1904 green = pickler.dumps(orig, proto)
1905 derived = unpickler.loads(green)
1906 self.assertEqual(orig, derived)
1907
Tim Peters2a799bf2002-12-16 20:18:38 +00001908 def test_bool(self):
1909 cls = self.theclass
Georg Brandlab91fde2009-08-13 08:51:18 +00001910 self.assertTrue(cls(1))
1911 self.assertTrue(cls(0, 1))
1912 self.assertTrue(cls(0, 0, 1))
1913 self.assertTrue(cls(0, 0, 0, 1))
1914 self.assertTrue(not cls(0))
1915 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001916
Tim Peters12bf3392002-12-24 05:41:27 +00001917 def test_replace(self):
1918 cls = self.theclass
1919 args = [1, 2, 3, 4]
1920 base = cls(*args)
1921 self.assertEqual(base, base.replace())
1922
1923 i = 0
1924 for name, newval in (("hour", 5),
1925 ("minute", 6),
1926 ("second", 7),
1927 ("microsecond", 8)):
1928 newargs = args[:]
1929 newargs[i] = newval
1930 expected = cls(*newargs)
1931 got = base.replace(**{name: newval})
1932 self.assertEqual(expected, got)
1933 i += 1
1934
1935 # Out of bounds.
1936 base = cls(1)
1937 self.assertRaises(ValueError, base.replace, hour=24)
1938 self.assertRaises(ValueError, base.replace, minute=-1)
1939 self.assertRaises(ValueError, base.replace, second=100)
1940 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1941
Tim Petersa98924a2003-05-17 05:55:19 +00001942 def test_subclass_time(self):
1943
1944 class C(self.theclass):
1945 theAnswer = 42
1946
1947 def __new__(cls, *args, **kws):
1948 temp = kws.copy()
1949 extra = temp.pop('extra')
1950 result = self.theclass.__new__(cls, *args, **temp)
1951 result.extra = extra
1952 return result
1953
1954 def newmeth(self, start):
1955 return start + self.hour + self.second
1956
1957 args = 4, 5, 6
1958
1959 dt1 = self.theclass(*args)
1960 dt2 = C(*args, **{'extra': 7})
1961
1962 self.assertEqual(dt2.__class__, C)
1963 self.assertEqual(dt2.theAnswer, 42)
1964 self.assertEqual(dt2.extra, 7)
1965 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1966 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1967
Armin Rigof4afb212005-11-07 07:15:48 +00001968 def test_backdoor_resistance(self):
1969 # see TestDate.test_backdoor_resistance().
1970 base = '2:59.0'
1971 for hour_byte in ' ', '9', chr(24), '\xff':
1972 self.assertRaises(TypeError, self.theclass,
1973 hour_byte + base[1:])
1974
Tim Peters855fe882002-12-22 03:43:39 +00001975# A mixin for classes with a tzinfo= argument. Subclasses must define
1976# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001977# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00001978class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001979
Tim Petersbad8ff02002-12-30 20:52:32 +00001980 def test_argument_passing(self):
1981 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001982 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001983 class introspective(tzinfo):
1984 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001985 def utcoffset(self, dt):
1986 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001987 dst = utcoffset
1988
1989 obj = cls(1, 2, 3, tzinfo=introspective())
1990
Tim Peters0bf60bd2003-01-08 20:40:01 +00001991 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001992 self.assertEqual(obj.tzname(), expected)
1993
Tim Peters0bf60bd2003-01-08 20:40:01 +00001994 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001995 self.assertEqual(obj.utcoffset(), expected)
1996 self.assertEqual(obj.dst(), expected)
1997
Tim Peters855fe882002-12-22 03:43:39 +00001998 def test_bad_tzinfo_classes(self):
1999 cls = self.theclass
2000 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002001
Tim Peters855fe882002-12-22 03:43:39 +00002002 class NiceTry(object):
2003 def __init__(self): pass
2004 def utcoffset(self, dt): pass
2005 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2006
2007 class BetterTry(tzinfo):
2008 def __init__(self): pass
2009 def utcoffset(self, dt): pass
2010 b = BetterTry()
2011 t = cls(1, 1, 1, tzinfo=b)
Georg Brandlab91fde2009-08-13 08:51:18 +00002012 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002013
2014 def test_utc_offset_out_of_bounds(self):
2015 class Edgy(tzinfo):
2016 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002017 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002018 def utcoffset(self, dt):
2019 return self.offset
2020
2021 cls = self.theclass
2022 for offset, legit in ((-1440, False),
2023 (-1439, True),
2024 (1439, True),
2025 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002026 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002027 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002028 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002029 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002030 else:
2031 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002032 if legit:
2033 aofs = abs(offset)
2034 h, m = divmod(aofs, 60)
2035 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002036 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002037 t = t.timetz()
2038 self.assertEqual(str(t), "01:02:03" + tag)
2039 else:
2040 self.assertRaises(ValueError, str, t)
2041
2042 def test_tzinfo_classes(self):
2043 cls = self.theclass
2044 class C1(tzinfo):
2045 def utcoffset(self, dt): return None
2046 def dst(self, dt): return None
2047 def tzname(self, dt): return None
2048 for t in (cls(1, 1, 1),
2049 cls(1, 1, 1, tzinfo=None),
2050 cls(1, 1, 1, tzinfo=C1())):
Georg Brandlab91fde2009-08-13 08:51:18 +00002051 self.assertTrue(t.utcoffset() is None)
2052 self.assertTrue(t.dst() is None)
2053 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002054
Tim Peters855fe882002-12-22 03:43:39 +00002055 class C3(tzinfo):
2056 def utcoffset(self, dt): return timedelta(minutes=-1439)
2057 def dst(self, dt): return timedelta(minutes=1439)
2058 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002059 t = cls(1, 1, 1, tzinfo=C3())
2060 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2061 self.assertEqual(t.dst(), timedelta(minutes=1439))
2062 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002063
2064 # Wrong types.
2065 class C4(tzinfo):
2066 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002067 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002068 def tzname(self, dt): return 0
2069 t = cls(1, 1, 1, tzinfo=C4())
2070 self.assertRaises(TypeError, t.utcoffset)
2071 self.assertRaises(TypeError, t.dst)
2072 self.assertRaises(TypeError, t.tzname)
2073
2074 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002075 class C6(tzinfo):
2076 def utcoffset(self, dt): return timedelta(hours=-24)
2077 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002078 t = cls(1, 1, 1, tzinfo=C6())
2079 self.assertRaises(ValueError, t.utcoffset)
2080 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002081
2082 # Not a whole number of minutes.
2083 class C7(tzinfo):
2084 def utcoffset(self, dt): return timedelta(seconds=61)
2085 def dst(self, dt): return timedelta(microseconds=-81)
2086 t = cls(1, 1, 1, tzinfo=C7())
2087 self.assertRaises(ValueError, t.utcoffset)
2088 self.assertRaises(ValueError, t.dst)
2089
Tim Peters4c0db782002-12-26 05:01:19 +00002090 def test_aware_compare(self):
2091 cls = self.theclass
2092
Tim Peters60c76e42002-12-27 00:41:11 +00002093 # Ensure that utcoffset() gets ignored if the comparands have
2094 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002095 class OperandDependentOffset(tzinfo):
2096 def utcoffset(self, t):
2097 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002098 # d0 and d1 equal after adjustment
2099 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002100 else:
Tim Peters397301e2003-01-02 21:28:08 +00002101 # d2 off in the weeds
2102 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002103
2104 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2105 d0 = base.replace(minute=3)
2106 d1 = base.replace(minute=9)
2107 d2 = base.replace(minute=11)
2108 for x in d0, d1, d2:
2109 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002110 for op in lt, le, gt, ge, eq, ne:
2111 got = op(x, y)
2112 expected = op(x.minute, y.minute)
2113 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002114
2115 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002116 # Note that a time can't actually have an operand-depedent offset,
2117 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2118 # so skip this test for time.
2119 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002120 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2121 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2122 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2123 for x in d0, d1, d2:
2124 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002125 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002126 if (x is d0 or x is d1) and (y is d0 or y is d1):
2127 expected = 0
2128 elif x is y is d2:
2129 expected = 0
2130 elif x is d2:
2131 expected = -1
2132 else:
2133 assert y is d2
2134 expected = 1
2135 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002136
Tim Peters855fe882002-12-22 03:43:39 +00002137
Tim Peters0bf60bd2003-01-08 20:40:01 +00002138# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002139class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002140 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002141
2142 def test_empty(self):
2143 t = self.theclass()
2144 self.assertEqual(t.hour, 0)
2145 self.assertEqual(t.minute, 0)
2146 self.assertEqual(t.second, 0)
2147 self.assertEqual(t.microsecond, 0)
Georg Brandlab91fde2009-08-13 08:51:18 +00002148 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002149
Tim Peters2a799bf2002-12-16 20:18:38 +00002150 def test_zones(self):
2151 est = FixedOffset(-300, "EST", 1)
2152 utc = FixedOffset(0, "UTC", -2)
2153 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002154 t1 = time( 7, 47, tzinfo=est)
2155 t2 = time(12, 47, tzinfo=utc)
2156 t3 = time(13, 47, tzinfo=met)
2157 t4 = time(microsecond=40)
2158 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002159
2160 self.assertEqual(t1.tzinfo, est)
2161 self.assertEqual(t2.tzinfo, utc)
2162 self.assertEqual(t3.tzinfo, met)
Georg Brandlab91fde2009-08-13 08:51:18 +00002163 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002164 self.assertEqual(t5.tzinfo, utc)
2165
Tim Peters855fe882002-12-22 03:43:39 +00002166 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2167 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2168 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Georg Brandlab91fde2009-08-13 08:51:18 +00002169 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002170 self.assertRaises(TypeError, t1.utcoffset, "no args")
2171
2172 self.assertEqual(t1.tzname(), "EST")
2173 self.assertEqual(t2.tzname(), "UTC")
2174 self.assertEqual(t3.tzname(), "MET")
Georg Brandlab91fde2009-08-13 08:51:18 +00002175 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002176 self.assertRaises(TypeError, t1.tzname, "no args")
2177
Tim Peters855fe882002-12-22 03:43:39 +00002178 self.assertEqual(t1.dst(), timedelta(minutes=1))
2179 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2180 self.assertEqual(t3.dst(), timedelta(minutes=3))
Georg Brandlab91fde2009-08-13 08:51:18 +00002181 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002182 self.assertRaises(TypeError, t1.dst, "no args")
2183
2184 self.assertEqual(hash(t1), hash(t2))
2185 self.assertEqual(hash(t1), hash(t3))
2186 self.assertEqual(hash(t2), hash(t3))
2187
2188 self.assertEqual(t1, t2)
2189 self.assertEqual(t1, t3)
2190 self.assertEqual(t2, t3)
2191 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2192 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2193 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2194
2195 self.assertEqual(str(t1), "07:47:00-05:00")
2196 self.assertEqual(str(t2), "12:47:00+00:00")
2197 self.assertEqual(str(t3), "13:47:00+01:00")
2198 self.assertEqual(str(t4), "00:00:00.000040")
2199 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2200
2201 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2202 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2203 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2204 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2205 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2206
Tim Peters0bf60bd2003-01-08 20:40:01 +00002207 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002208 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2209 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2210 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2211 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2212 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2213
2214 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2215 "07:47:00 %Z=EST %z=-0500")
2216 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2217 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2218
2219 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002220 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002221 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2222 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2223
Tim Petersb92bb712002-12-21 17:44:07 +00002224 # Check that an invalid tzname result raises an exception.
2225 class Badtzname(tzinfo):
2226 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002227 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002228 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2229 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002230
2231 def test_hash_edge_cases(self):
2232 # Offsets that overflow a basic time.
2233 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2234 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2235 self.assertEqual(hash(t1), hash(t2))
2236
2237 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2238 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2239 self.assertEqual(hash(t1), hash(t2))
2240
Tim Peters2a799bf2002-12-16 20:18:38 +00002241 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002242 # Try one without a tzinfo.
2243 args = 20, 59, 16, 64**2
2244 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002245 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002246 green = pickler.dumps(orig, proto)
2247 derived = unpickler.loads(green)
2248 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002249
2250 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002251 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002252 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002253 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002254 green = pickler.dumps(orig, proto)
2255 derived = unpickler.loads(green)
2256 self.assertEqual(orig, derived)
Georg Brandlab91fde2009-08-13 08:51:18 +00002257 self.assertTrue(isinstance(derived.tzinfo, PicklableFixedOffset))
Tim Peters96940c92003-01-31 21:55:33 +00002258 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2259 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002260
2261 def test_more_bool(self):
2262 # Test cases with non-None tzinfo.
2263 cls = self.theclass
2264
2265 t = cls(0, tzinfo=FixedOffset(-300, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002266 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002267
2268 t = cls(5, tzinfo=FixedOffset(-300, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002269 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002270
2271 t = cls(5, tzinfo=FixedOffset(300, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002272 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002273
2274 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002275 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002276
2277 # Mostly ensuring this doesn't overflow internally.
2278 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002279 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002280
2281 # But this should yield a value error -- the utcoffset is bogus.
2282 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2283 self.assertRaises(ValueError, lambda: bool(t))
2284
2285 # Likewise.
2286 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2287 self.assertRaises(ValueError, lambda: bool(t))
2288
Tim Peters12bf3392002-12-24 05:41:27 +00002289 def test_replace(self):
2290 cls = self.theclass
2291 z100 = FixedOffset(100, "+100")
2292 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2293 args = [1, 2, 3, 4, z100]
2294 base = cls(*args)
2295 self.assertEqual(base, base.replace())
2296
2297 i = 0
2298 for name, newval in (("hour", 5),
2299 ("minute", 6),
2300 ("second", 7),
2301 ("microsecond", 8),
2302 ("tzinfo", zm200)):
2303 newargs = args[:]
2304 newargs[i] = newval
2305 expected = cls(*newargs)
2306 got = base.replace(**{name: newval})
2307 self.assertEqual(expected, got)
2308 i += 1
2309
2310 # Ensure we can get rid of a tzinfo.
2311 self.assertEqual(base.tzname(), "+100")
2312 base2 = base.replace(tzinfo=None)
Georg Brandlab91fde2009-08-13 08:51:18 +00002313 self.assertTrue(base2.tzinfo is None)
2314 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002315
2316 # Ensure we can add one.
2317 base3 = base2.replace(tzinfo=z100)
2318 self.assertEqual(base, base3)
Georg Brandlab91fde2009-08-13 08:51:18 +00002319 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002320
2321 # Out of bounds.
2322 base = cls(1)
2323 self.assertRaises(ValueError, base.replace, hour=24)
2324 self.assertRaises(ValueError, base.replace, minute=-1)
2325 self.assertRaises(ValueError, base.replace, second=100)
2326 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2327
Tim Peters60c76e42002-12-27 00:41:11 +00002328 def test_mixed_compare(self):
2329 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002330 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002331 self.assertEqual(t1, t2)
2332 t2 = t2.replace(tzinfo=None)
2333 self.assertEqual(t1, t2)
2334 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2335 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002336 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2337 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002338
Tim Peters0bf60bd2003-01-08 20:40:01 +00002339 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002340 class Varies(tzinfo):
2341 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002342 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002343 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002344 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002345 return self.offset
2346
2347 v = Varies()
2348 t1 = t2.replace(tzinfo=v)
2349 t2 = t2.replace(tzinfo=v)
2350 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2351 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2352 self.assertEqual(t1, t2)
2353
2354 # But if they're not identical, it isn't ignored.
2355 t2 = t2.replace(tzinfo=Varies())
Georg Brandlab91fde2009-08-13 08:51:18 +00002356 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002357
Tim Petersa98924a2003-05-17 05:55:19 +00002358 def test_subclass_timetz(self):
2359
2360 class C(self.theclass):
2361 theAnswer = 42
2362
2363 def __new__(cls, *args, **kws):
2364 temp = kws.copy()
2365 extra = temp.pop('extra')
2366 result = self.theclass.__new__(cls, *args, **temp)
2367 result.extra = extra
2368 return result
2369
2370 def newmeth(self, start):
2371 return start + self.hour + self.second
2372
2373 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2374
2375 dt1 = self.theclass(*args)
2376 dt2 = C(*args, **{'extra': 7})
2377
2378 self.assertEqual(dt2.__class__, C)
2379 self.assertEqual(dt2.theAnswer, 42)
2380 self.assertEqual(dt2.extra, 7)
2381 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2382 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2383
Tim Peters4c0db782002-12-26 05:01:19 +00002384
Tim Peters0bf60bd2003-01-08 20:40:01 +00002385# Testing datetime objects with a non-None tzinfo.
2386
Guido van Rossumd8faa362007-04-27 19:54:29 +00002387class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002388 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002389
2390 def test_trivial(self):
2391 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2392 self.assertEqual(dt.year, 1)
2393 self.assertEqual(dt.month, 2)
2394 self.assertEqual(dt.day, 3)
2395 self.assertEqual(dt.hour, 4)
2396 self.assertEqual(dt.minute, 5)
2397 self.assertEqual(dt.second, 6)
2398 self.assertEqual(dt.microsecond, 7)
2399 self.assertEqual(dt.tzinfo, None)
2400
2401 def test_even_more_compare(self):
2402 # The test_compare() and test_more_compare() inherited from TestDate
2403 # and TestDateTime covered non-tzinfo cases.
2404
2405 # Smallest possible after UTC adjustment.
2406 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2407 # Largest possible after UTC adjustment.
2408 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2409 tzinfo=FixedOffset(-1439, ""))
2410
2411 # Make sure those compare correctly, and w/o overflow.
Georg Brandlab91fde2009-08-13 08:51:18 +00002412 self.assertTrue(t1 < t2)
2413 self.assertTrue(t1 != t2)
2414 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002415
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002416 self.assertEqual(t1, t1)
2417 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002418
2419 # Equal afer adjustment.
2420 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2421 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2422 self.assertEqual(t1, t2)
2423
2424 # Change t1 not to subtract a minute, and t1 should be larger.
2425 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002426 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002427
2428 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2429 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Georg Brandlab91fde2009-08-13 08:51:18 +00002430 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002431
2432 # Back to the original t1, but make seconds resolve it.
2433 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2434 second=1)
Georg Brandlab91fde2009-08-13 08:51:18 +00002435 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002436
2437 # Likewise, but make microseconds resolve it.
2438 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2439 microsecond=1)
Georg Brandlab91fde2009-08-13 08:51:18 +00002440 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002441
2442 # Make t2 naive and it should fail.
2443 t2 = self.theclass.min
2444 self.assertRaises(TypeError, lambda: t1 == t2)
2445 self.assertEqual(t2, t2)
2446
2447 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2448 class Naive(tzinfo):
2449 def utcoffset(self, dt): return None
2450 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2451 self.assertRaises(TypeError, lambda: t1 == t2)
2452 self.assertEqual(t2, t2)
2453
2454 # OTOH, it's OK to compare two of these mixing the two ways of being
2455 # naive.
2456 t1 = self.theclass(5, 6, 7)
2457 self.assertEqual(t1, t2)
2458
2459 # Try a bogus uctoffset.
2460 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002461 def utcoffset(self, dt):
2462 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002463 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2464 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002465 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002466
Tim Peters2a799bf2002-12-16 20:18:38 +00002467 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002468 # Try one without a tzinfo.
2469 args = 6, 7, 23, 20, 59, 1, 64**2
2470 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002471 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002472 green = pickler.dumps(orig, proto)
2473 derived = unpickler.loads(green)
2474 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002475
2476 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002477 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002478 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002479 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002480 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002481 green = pickler.dumps(orig, proto)
2482 derived = unpickler.loads(green)
2483 self.assertEqual(orig, derived)
Georg Brandlab91fde2009-08-13 08:51:18 +00002484 self.assertTrue(isinstance(derived.tzinfo,
Tim Peters96940c92003-01-31 21:55:33 +00002485 PicklableFixedOffset))
2486 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2487 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002488
2489 def test_extreme_hashes(self):
2490 # If an attempt is made to hash these via subtracting the offset
2491 # then hashing a datetime object, OverflowError results. The
2492 # Python implementation used to blow up here.
2493 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2494 hash(t)
2495 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2496 tzinfo=FixedOffset(-1439, ""))
2497 hash(t)
2498
2499 # OTOH, an OOB offset should blow up.
2500 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2501 self.assertRaises(ValueError, hash, t)
2502
2503 def test_zones(self):
2504 est = FixedOffset(-300, "EST")
2505 utc = FixedOffset(0, "UTC")
2506 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002507 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2508 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2509 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002510 self.assertEqual(t1.tzinfo, est)
2511 self.assertEqual(t2.tzinfo, utc)
2512 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002513 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2514 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2515 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002516 self.assertEqual(t1.tzname(), "EST")
2517 self.assertEqual(t2.tzname(), "UTC")
2518 self.assertEqual(t3.tzname(), "MET")
2519 self.assertEqual(hash(t1), hash(t2))
2520 self.assertEqual(hash(t1), hash(t3))
2521 self.assertEqual(hash(t2), hash(t3))
2522 self.assertEqual(t1, t2)
2523 self.assertEqual(t1, t3)
2524 self.assertEqual(t2, t3)
2525 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2526 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2527 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002528 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002529 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2530 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2531 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2532
2533 def test_combine(self):
2534 met = FixedOffset(60, "MET")
2535 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002536 tz = time(18, 45, 3, 1234, tzinfo=met)
2537 dt = datetime.combine(d, tz)
2538 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002539 tzinfo=met))
2540
2541 def test_extract(self):
2542 met = FixedOffset(60, "MET")
2543 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2544 self.assertEqual(dt.date(), date(2002, 3, 4))
2545 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002546 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002547
2548 def test_tz_aware_arithmetic(self):
2549 import random
2550
2551 now = self.theclass.now()
2552 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002553 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002554 nowaware = self.theclass.combine(now.date(), timeaware)
Georg Brandlab91fde2009-08-13 08:51:18 +00002555 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002556 self.assertEqual(nowaware.timetz(), timeaware)
2557
2558 # Can't mix aware and non-aware.
2559 self.assertRaises(TypeError, lambda: now - nowaware)
2560 self.assertRaises(TypeError, lambda: nowaware - now)
2561
Tim Peters0bf60bd2003-01-08 20:40:01 +00002562 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002563 self.assertRaises(TypeError, lambda: now + nowaware)
2564 self.assertRaises(TypeError, lambda: nowaware + now)
2565 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2566
2567 # Subtracting should yield 0.
2568 self.assertEqual(now - now, timedelta(0))
2569 self.assertEqual(nowaware - nowaware, timedelta(0))
2570
2571 # Adding a delta should preserve tzinfo.
2572 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2573 nowawareplus = nowaware + delta
Georg Brandlab91fde2009-08-13 08:51:18 +00002574 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002575 nowawareplus2 = delta + nowaware
Georg Brandlab91fde2009-08-13 08:51:18 +00002576 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002577 self.assertEqual(nowawareplus, nowawareplus2)
2578
2579 # that - delta should be what we started with, and that - what we
2580 # started with should be delta.
2581 diff = nowawareplus - delta
Georg Brandlab91fde2009-08-13 08:51:18 +00002582 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002583 self.assertEqual(nowaware, diff)
2584 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2585 self.assertEqual(nowawareplus - nowaware, delta)
2586
2587 # Make up a random timezone.
2588 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002589 # Attach it to nowawareplus.
2590 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Georg Brandlab91fde2009-08-13 08:51:18 +00002591 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002592 # Make sure the difference takes the timezone adjustments into account.
2593 got = nowaware - nowawareplus
2594 # Expected: (nowaware base - nowaware offset) -
2595 # (nowawareplus base - nowawareplus offset) =
2596 # (nowaware base - nowawareplus base) +
2597 # (nowawareplus offset - nowaware offset) =
2598 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002599 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002600 self.assertEqual(got, expected)
2601
2602 # Try max possible difference.
2603 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2604 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2605 tzinfo=FixedOffset(-1439, "max"))
2606 maxdiff = max - min
2607 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2608 timedelta(minutes=2*1439))
2609
2610 def test_tzinfo_now(self):
2611 meth = self.theclass.now
2612 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2613 base = meth()
2614 # Try with and without naming the keyword.
2615 off42 = FixedOffset(42, "42")
2616 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002617 again = meth(tz=off42)
Georg Brandlab91fde2009-08-13 08:51:18 +00002618 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002619 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002620 # Bad argument with and w/o naming the keyword.
2621 self.assertRaises(TypeError, meth, 16)
2622 self.assertRaises(TypeError, meth, tzinfo=16)
2623 # Bad keyword name.
2624 self.assertRaises(TypeError, meth, tinfo=off42)
2625 # Too many args.
2626 self.assertRaises(TypeError, meth, off42, off42)
2627
Tim Peters10cadce2003-01-23 19:58:02 +00002628 # We don't know which time zone we're in, and don't have a tzinfo
2629 # class to represent it, so seeing whether a tz argument actually
2630 # does a conversion is tricky.
2631 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2632 utc = FixedOffset(0, "utc", 0)
2633 for dummy in range(3):
2634 now = datetime.now(weirdtz)
Georg Brandlab91fde2009-08-13 08:51:18 +00002635 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002636 utcnow = datetime.utcnow().replace(tzinfo=utc)
2637 now2 = utcnow.astimezone(weirdtz)
2638 if abs(now - now2) < timedelta(seconds=30):
2639 break
2640 # Else the code is broken, or more than 30 seconds passed between
2641 # calls; assuming the latter, just try again.
2642 else:
2643 # Three strikes and we're out.
2644 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2645
Tim Peters2a799bf2002-12-16 20:18:38 +00002646 def test_tzinfo_fromtimestamp(self):
2647 import time
2648 meth = self.theclass.fromtimestamp
2649 ts = time.time()
2650 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2651 base = meth(ts)
2652 # Try with and without naming the keyword.
2653 off42 = FixedOffset(42, "42")
2654 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002655 again = meth(ts, tz=off42)
Georg Brandlab91fde2009-08-13 08:51:18 +00002656 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002657 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002658 # Bad argument with and w/o naming the keyword.
2659 self.assertRaises(TypeError, meth, ts, 16)
2660 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2661 # Bad keyword name.
2662 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2663 # Too many args.
2664 self.assertRaises(TypeError, meth, ts, off42, off42)
2665 # Too few args.
2666 self.assertRaises(TypeError, meth)
2667
Tim Peters2a44a8d2003-01-23 20:53:10 +00002668 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002669 timestamp = 1000000000
2670 utcdatetime = datetime.utcfromtimestamp(timestamp)
2671 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2672 # But on some flavor of Mac, it's nowhere near that. So we can't have
2673 # any idea here what time that actually is, we can only test that
2674 # relative changes match.
2675 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2676 tz = FixedOffset(utcoffset, "tz", 0)
2677 expected = utcdatetime + utcoffset
2678 got = datetime.fromtimestamp(timestamp, tz)
2679 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002680
Tim Peters2a799bf2002-12-16 20:18:38 +00002681 def test_tzinfo_utcnow(self):
2682 meth = self.theclass.utcnow
2683 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2684 base = meth()
2685 # Try with and without naming the keyword; for whatever reason,
2686 # utcnow() doesn't accept a tzinfo argument.
2687 off42 = FixedOffset(42, "42")
2688 self.assertRaises(TypeError, meth, off42)
2689 self.assertRaises(TypeError, meth, tzinfo=off42)
2690
2691 def test_tzinfo_utcfromtimestamp(self):
2692 import time
2693 meth = self.theclass.utcfromtimestamp
2694 ts = time.time()
2695 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2696 base = meth(ts)
2697 # Try with and without naming the keyword; for whatever reason,
2698 # utcfromtimestamp() doesn't accept a tzinfo argument.
2699 off42 = FixedOffset(42, "42")
2700 self.assertRaises(TypeError, meth, ts, off42)
2701 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2702
2703 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002704 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002705 # DST flag.
2706 class DST(tzinfo):
2707 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002708 if isinstance(dstvalue, int):
2709 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002710 self.dstvalue = dstvalue
2711 def dst(self, dt):
2712 return self.dstvalue
2713
2714 cls = self.theclass
2715 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2716 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2717 t = d.timetuple()
2718 self.assertEqual(1, t.tm_year)
2719 self.assertEqual(1, t.tm_mon)
2720 self.assertEqual(1, t.tm_mday)
2721 self.assertEqual(10, t.tm_hour)
2722 self.assertEqual(20, t.tm_min)
2723 self.assertEqual(30, t.tm_sec)
2724 self.assertEqual(0, t.tm_wday)
2725 self.assertEqual(1, t.tm_yday)
2726 self.assertEqual(flag, t.tm_isdst)
2727
2728 # dst() returns wrong type.
2729 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2730
2731 # dst() at the edge.
2732 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2733 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2734
2735 # dst() out of range.
2736 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2737 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2738
2739 def test_utctimetuple(self):
2740 class DST(tzinfo):
Alexander Belopolskyf5c7bc82010-07-05 15:26:36 +00002741 def __init__(self, dstvalue=0):
Tim Peters397301e2003-01-02 21:28:08 +00002742 if isinstance(dstvalue, int):
2743 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002744 self.dstvalue = dstvalue
2745 def dst(self, dt):
2746 return self.dstvalue
2747
2748 cls = self.theclass
2749 # This can't work: DST didn't implement utcoffset.
2750 self.assertRaises(NotImplementedError,
2751 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2752
2753 class UOFS(DST):
2754 def __init__(self, uofs, dofs=None):
2755 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002756 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002757 def utcoffset(self, dt):
2758 return self.uofs
2759
2760 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2761 # in effect for a UTC time.
2762 for dstvalue in -33, 33, 0, None:
2763 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2764 t = d.utctimetuple()
2765 self.assertEqual(d.year, t.tm_year)
2766 self.assertEqual(d.month, t.tm_mon)
2767 self.assertEqual(d.day, t.tm_mday)
2768 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2769 self.assertEqual(13, t.tm_min)
2770 self.assertEqual(d.second, t.tm_sec)
2771 self.assertEqual(d.weekday(), t.tm_wday)
2772 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2773 t.tm_yday)
2774 self.assertEqual(0, t.tm_isdst)
Alexander Belopolskyf5c7bc82010-07-05 15:26:36 +00002775 # For naive datetime, utctimetuple == timetuple except for isdst
2776 d = cls(1, 2, 3, 10, 20, 30, 40)
2777 t = d.utctimetuple()
2778 self.assertEqual(t[:-1], d.timetuple()[:-1])
2779 self.assertEqual(0, t.tm_isdst)
2780 # Same if utcoffset is None
2781 class NOFS(DST):
2782 def utcoffset(self, dt):
2783 return None
2784 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=NOFS())
2785 t = d.utctimetuple()
2786 self.assertEqual(t[:-1], d.timetuple()[:-1])
2787 self.assertEqual(0, t.tm_isdst)
2788 # Check that bad tzinfo is detected
2789 class BOFS(DST):
2790 def utcoffset(self, dt):
2791 return "EST"
2792 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=BOFS())
2793 self.assertRaises(TypeError, d.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00002794
2795 # At the edges, UTC adjustment can normalize into years out-of-range
2796 # for a datetime object. Ensure that a correct timetuple is
2797 # created anyway.
2798 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2799 # That goes back 1 minute less than a full day.
2800 t = tiny.utctimetuple()
2801 self.assertEqual(t.tm_year, MINYEAR-1)
2802 self.assertEqual(t.tm_mon, 12)
2803 self.assertEqual(t.tm_mday, 31)
2804 self.assertEqual(t.tm_hour, 0)
2805 self.assertEqual(t.tm_min, 1)
2806 self.assertEqual(t.tm_sec, 37)
2807 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2808 self.assertEqual(t.tm_isdst, 0)
2809
2810 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2811 # That goes forward 1 minute less than a full day.
2812 t = huge.utctimetuple()
2813 self.assertEqual(t.tm_year, MAXYEAR+1)
2814 self.assertEqual(t.tm_mon, 1)
2815 self.assertEqual(t.tm_mday, 1)
2816 self.assertEqual(t.tm_hour, 23)
2817 self.assertEqual(t.tm_min, 58)
2818 self.assertEqual(t.tm_sec, 37)
2819 self.assertEqual(t.tm_yday, 1)
2820 self.assertEqual(t.tm_isdst, 0)
2821
2822 def test_tzinfo_isoformat(self):
2823 zero = FixedOffset(0, "+00:00")
2824 plus = FixedOffset(220, "+03:40")
2825 minus = FixedOffset(-231, "-03:51")
2826 unknown = FixedOffset(None, "")
2827
2828 cls = self.theclass
2829 datestr = '0001-02-03'
2830 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002831 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002832 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2833 timestr = '04:05:59' + (us and '.987001' or '')
2834 ofsstr = ofs is not None and d.tzname() or ''
2835 tailstr = timestr + ofsstr
2836 iso = d.isoformat()
2837 self.assertEqual(iso, datestr + 'T' + tailstr)
2838 self.assertEqual(iso, d.isoformat('T'))
2839 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00002840 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002841 self.assertEqual(str(d), datestr + ' ' + tailstr)
2842
Tim Peters12bf3392002-12-24 05:41:27 +00002843 def test_replace(self):
2844 cls = self.theclass
2845 z100 = FixedOffset(100, "+100")
2846 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2847 args = [1, 2, 3, 4, 5, 6, 7, z100]
2848 base = cls(*args)
2849 self.assertEqual(base, base.replace())
2850
2851 i = 0
2852 for name, newval in (("year", 2),
2853 ("month", 3),
2854 ("day", 4),
2855 ("hour", 5),
2856 ("minute", 6),
2857 ("second", 7),
2858 ("microsecond", 8),
2859 ("tzinfo", zm200)):
2860 newargs = args[:]
2861 newargs[i] = newval
2862 expected = cls(*newargs)
2863 got = base.replace(**{name: newval})
2864 self.assertEqual(expected, got)
2865 i += 1
2866
2867 # Ensure we can get rid of a tzinfo.
2868 self.assertEqual(base.tzname(), "+100")
2869 base2 = base.replace(tzinfo=None)
Georg Brandlab91fde2009-08-13 08:51:18 +00002870 self.assertTrue(base2.tzinfo is None)
2871 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002872
2873 # Ensure we can add one.
2874 base3 = base2.replace(tzinfo=z100)
2875 self.assertEqual(base, base3)
Georg Brandlab91fde2009-08-13 08:51:18 +00002876 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002877
2878 # Out of bounds.
2879 base = cls(2000, 2, 29)
2880 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002881
Tim Peters80475bb2002-12-25 07:40:55 +00002882 def test_more_astimezone(self):
2883 # The inherited test_astimezone covered some trivial and error cases.
2884 fnone = FixedOffset(None, "None")
2885 f44m = FixedOffset(44, "44")
2886 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2887
Tim Peters10cadce2003-01-23 19:58:02 +00002888 dt = self.theclass.now(tz=f44m)
Georg Brandlab91fde2009-08-13 08:51:18 +00002889 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002890 # Replacing with degenerate tzinfo raises an exception.
2891 self.assertRaises(ValueError, dt.astimezone, fnone)
2892 # Ditto with None tz.
2893 self.assertRaises(TypeError, dt.astimezone, None)
2894 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002895 x = dt.astimezone(dt.tzinfo)
Georg Brandlab91fde2009-08-13 08:51:18 +00002896 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002897 self.assertEqual(x.date(), dt.date())
2898 self.assertEqual(x.time(), dt.time())
2899
2900 # Replacing with different tzinfo does adjust.
2901 got = dt.astimezone(fm5h)
Georg Brandlab91fde2009-08-13 08:51:18 +00002902 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002903 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2904 expected = dt - dt.utcoffset() # in effect, convert to UTC
2905 expected += fm5h.utcoffset(dt) # and from there to local time
2906 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2907 self.assertEqual(got.date(), expected.date())
2908 self.assertEqual(got.time(), expected.time())
2909 self.assertEqual(got.timetz(), expected.timetz())
Georg Brandlab91fde2009-08-13 08:51:18 +00002910 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002911 self.assertEqual(got, expected)
2912
Tim Peters4c0db782002-12-26 05:01:19 +00002913 def test_aware_subtract(self):
2914 cls = self.theclass
2915
Tim Peters60c76e42002-12-27 00:41:11 +00002916 # Ensure that utcoffset() is ignored when the operands have the
2917 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002918 class OperandDependentOffset(tzinfo):
2919 def utcoffset(self, t):
2920 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002921 # d0 and d1 equal after adjustment
2922 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002923 else:
Tim Peters397301e2003-01-02 21:28:08 +00002924 # d2 off in the weeds
2925 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002926
2927 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2928 d0 = base.replace(minute=3)
2929 d1 = base.replace(minute=9)
2930 d2 = base.replace(minute=11)
2931 for x in d0, d1, d2:
2932 for y in d0, d1, d2:
2933 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002934 expected = timedelta(minutes=x.minute - y.minute)
2935 self.assertEqual(got, expected)
2936
2937 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2938 # ignored.
2939 base = cls(8, 9, 10, 11, 12, 13, 14)
2940 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2941 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2942 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2943 for x in d0, d1, d2:
2944 for y in d0, d1, d2:
2945 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002946 if (x is d0 or x is d1) and (y is d0 or y is d1):
2947 expected = timedelta(0)
2948 elif x is y is d2:
2949 expected = timedelta(0)
2950 elif x is d2:
2951 expected = timedelta(minutes=(11-59)-0)
2952 else:
2953 assert y is d2
2954 expected = timedelta(minutes=0-(11-59))
2955 self.assertEqual(got, expected)
2956
Tim Peters60c76e42002-12-27 00:41:11 +00002957 def test_mixed_compare(self):
2958 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002959 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002960 self.assertEqual(t1, t2)
2961 t2 = t2.replace(tzinfo=None)
2962 self.assertEqual(t1, t2)
2963 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2964 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002965 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2966 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002967
Tim Peters0bf60bd2003-01-08 20:40:01 +00002968 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002969 class Varies(tzinfo):
2970 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002971 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002972 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002973 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002974 return self.offset
2975
2976 v = Varies()
2977 t1 = t2.replace(tzinfo=v)
2978 t2 = t2.replace(tzinfo=v)
2979 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2980 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2981 self.assertEqual(t1, t2)
2982
2983 # But if they're not identical, it isn't ignored.
2984 t2 = t2.replace(tzinfo=Varies())
Georg Brandlab91fde2009-08-13 08:51:18 +00002985 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002986
Tim Petersa98924a2003-05-17 05:55:19 +00002987 def test_subclass_datetimetz(self):
2988
2989 class C(self.theclass):
2990 theAnswer = 42
2991
2992 def __new__(cls, *args, **kws):
2993 temp = kws.copy()
2994 extra = temp.pop('extra')
2995 result = self.theclass.__new__(cls, *args, **temp)
2996 result.extra = extra
2997 return result
2998
2999 def newmeth(self, start):
3000 return start + self.hour + self.year
3001
3002 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3003
3004 dt1 = self.theclass(*args)
3005 dt2 = C(*args, **{'extra': 7})
3006
3007 self.assertEqual(dt2.__class__, C)
3008 self.assertEqual(dt2.theAnswer, 42)
3009 self.assertEqual(dt2.extra, 7)
3010 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3011 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3012
Tim Peters621818b2002-12-29 23:44:49 +00003013# Pain to set up DST-aware tzinfo classes.
3014
3015def first_sunday_on_or_after(dt):
3016 days_to_go = 6 - dt.weekday()
3017 if days_to_go:
3018 dt += timedelta(days_to_go)
3019 return dt
3020
3021ZERO = timedelta(0)
3022HOUR = timedelta(hours=1)
3023DAY = timedelta(days=1)
3024# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3025DSTSTART = datetime(1, 4, 1, 2)
3026# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003027# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3028# being standard time on that day, there is no spelling in local time of
3029# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3030DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003031
3032class USTimeZone(tzinfo):
3033
3034 def __init__(self, hours, reprname, stdname, dstname):
3035 self.stdoffset = timedelta(hours=hours)
3036 self.reprname = reprname
3037 self.stdname = stdname
3038 self.dstname = dstname
3039
3040 def __repr__(self):
3041 return self.reprname
3042
3043 def tzname(self, dt):
3044 if self.dst(dt):
3045 return self.dstname
3046 else:
3047 return self.stdname
3048
3049 def utcoffset(self, dt):
3050 return self.stdoffset + self.dst(dt)
3051
3052 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003053 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003054 # An exception instead may be sensible here, in one or more of
3055 # the cases.
3056 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003057 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003058
3059 # Find first Sunday in April.
3060 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3061 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3062
3063 # Find last Sunday in October.
3064 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3065 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3066
Tim Peters621818b2002-12-29 23:44:49 +00003067 # Can't compare naive to aware objects, so strip the timezone from
3068 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003069 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003070 return HOUR
3071 else:
3072 return ZERO
3073
Tim Peters521fc152002-12-31 17:36:56 +00003074Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3075Central = USTimeZone(-6, "Central", "CST", "CDT")
3076Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3077Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003078utc_real = FixedOffset(0, "UTC", 0)
3079# For better test coverage, we want another flavor of UTC that's west of
3080# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003081utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003082
3083class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003084 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003085 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003086 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003087
Tim Peters0bf60bd2003-01-08 20:40:01 +00003088 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003089
Tim Peters521fc152002-12-31 17:36:56 +00003090 # Check a time that's inside DST.
3091 def checkinside(self, dt, tz, utc, dston, dstoff):
3092 self.assertEqual(dt.dst(), HOUR)
3093
3094 # Conversion to our own timezone is always an identity.
3095 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003096
3097 asutc = dt.astimezone(utc)
3098 there_and_back = asutc.astimezone(tz)
3099
3100 # Conversion to UTC and back isn't always an identity here,
3101 # because there are redundant spellings (in local time) of
3102 # UTC time when DST begins: the clock jumps from 1:59:59
3103 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3104 # make sense then. The classes above treat 2:MM:SS as
3105 # daylight time then (it's "after 2am"), really an alias
3106 # for 1:MM:SS standard time. The latter form is what
3107 # conversion back from UTC produces.
3108 if dt.date() == dston.date() and dt.hour == 2:
3109 # We're in the redundant hour, and coming back from
3110 # UTC gives the 1:MM:SS standard-time spelling.
3111 self.assertEqual(there_and_back + HOUR, dt)
3112 # Although during was considered to be in daylight
3113 # time, there_and_back is not.
3114 self.assertEqual(there_and_back.dst(), ZERO)
3115 # They're the same times in UTC.
3116 self.assertEqual(there_and_back.astimezone(utc),
3117 dt.astimezone(utc))
3118 else:
3119 # We're not in the redundant hour.
3120 self.assertEqual(dt, there_and_back)
3121
Tim Peters327098a2003-01-20 22:54:38 +00003122 # Because we have a redundant spelling when DST begins, there is
3123 # (unforunately) an hour when DST ends that can't be spelled at all in
3124 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3125 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3126 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3127 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3128 # expressed in local time. Nevertheless, we want conversion back
3129 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003130 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003131 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003132 if dt.date() == dstoff.date() and dt.hour == 0:
3133 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003134 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003135 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3136 nexthour_utc += HOUR
3137 nexthour_tz = nexthour_utc.astimezone(tz)
3138 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003139 else:
Tim Peters327098a2003-01-20 22:54:38 +00003140 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003141
3142 # Check a time that's outside DST.
3143 def checkoutside(self, dt, tz, utc):
3144 self.assertEqual(dt.dst(), ZERO)
3145
3146 # Conversion to our own timezone is always an identity.
3147 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003148
3149 # Converting to UTC and back is an identity too.
3150 asutc = dt.astimezone(utc)
3151 there_and_back = asutc.astimezone(tz)
3152 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003153
Tim Peters1024bf82002-12-30 17:09:40 +00003154 def convert_between_tz_and_utc(self, tz, utc):
3155 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003156 # Because 1:MM on the day DST ends is taken as being standard time,
3157 # there is no spelling in tz for the last hour of daylight time.
3158 # For purposes of the test, the last hour of DST is 0:MM, which is
3159 # taken as being daylight time (and 1:MM is taken as being standard
3160 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003161 dstoff = self.dstoff.replace(tzinfo=tz)
3162 for delta in (timedelta(weeks=13),
3163 DAY,
3164 HOUR,
3165 timedelta(minutes=1),
3166 timedelta(microseconds=1)):
3167
Tim Peters521fc152002-12-31 17:36:56 +00003168 self.checkinside(dston, tz, utc, dston, dstoff)
3169 for during in dston + delta, dstoff - delta:
3170 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003171
Tim Peters521fc152002-12-31 17:36:56 +00003172 self.checkoutside(dstoff, tz, utc)
3173 for outside in dston - delta, dstoff + delta:
3174 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003175
Tim Peters621818b2002-12-29 23:44:49 +00003176 def test_easy(self):
3177 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003178 self.convert_between_tz_and_utc(Eastern, utc_real)
3179 self.convert_between_tz_and_utc(Pacific, utc_real)
3180 self.convert_between_tz_and_utc(Eastern, utc_fake)
3181 self.convert_between_tz_and_utc(Pacific, utc_fake)
3182 # The next is really dancing near the edge. It works because
3183 # Pacific and Eastern are far enough apart that their "problem
3184 # hours" don't overlap.
3185 self.convert_between_tz_and_utc(Eastern, Pacific)
3186 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003187 # OTOH, these fail! Don't enable them. The difficulty is that
3188 # the edge case tests assume that every hour is representable in
3189 # the "utc" class. This is always true for a fixed-offset tzinfo
3190 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3191 # For these adjacent DST-aware time zones, the range of time offsets
3192 # tested ends up creating hours in the one that aren't representable
3193 # in the other. For the same reason, we would see failures in the
3194 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3195 # offset deltas in convert_between_tz_and_utc().
3196 #
3197 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3198 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003199
Tim Petersf3615152003-01-01 21:51:37 +00003200 def test_tricky(self):
3201 # 22:00 on day before daylight starts.
3202 fourback = self.dston - timedelta(hours=4)
3203 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003204 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003205 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3206 # 2", we should get the 3 spelling.
3207 # If we plug 22:00 the day before into Eastern, it "looks like std
3208 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3209 # to 22:00 lands on 2:00, which makes no sense in local time (the
3210 # local clock jumps from 1 to 3). The point here is to make sure we
3211 # get the 3 spelling.
3212 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003213 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003214 self.assertEqual(expected, got)
3215
3216 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3217 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003218 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003219 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3220 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3221 # spelling.
3222 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003223 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003224 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003225
Tim Petersadf64202003-01-04 06:03:15 +00003226 # Now on the day DST ends, we want "repeat an hour" behavior.
3227 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3228 # EST 23:MM 0:MM 1:MM 2:MM
3229 # EDT 0:MM 1:MM 2:MM 3:MM
3230 # wall 0:MM 1:MM 1:MM 2:MM against these
3231 for utc in utc_real, utc_fake:
3232 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003233 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003234 # Convert that to UTC.
3235 first_std_hour -= tz.utcoffset(None)
3236 # Adjust for possibly fake UTC.
3237 asutc = first_std_hour + utc.utcoffset(None)
3238 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3239 # tz=Eastern.
3240 asutcbase = asutc.replace(tzinfo=utc)
3241 for tzhour in (0, 1, 1, 2):
3242 expectedbase = self.dstoff.replace(hour=tzhour)
3243 for minute in 0, 30, 59:
3244 expected = expectedbase.replace(minute=minute)
3245 asutc = asutcbase.replace(minute=minute)
3246 astz = asutc.astimezone(tz)
3247 self.assertEqual(astz.replace(tzinfo=None), expected)
3248 asutcbase += HOUR
3249
3250
Tim Peters710fb152003-01-02 19:35:54 +00003251 def test_bogus_dst(self):
3252 class ok(tzinfo):
3253 def utcoffset(self, dt): return HOUR
3254 def dst(self, dt): return HOUR
3255
3256 now = self.theclass.now().replace(tzinfo=utc_real)
3257 # Doesn't blow up.
3258 now.astimezone(ok())
3259
3260 # Does blow up.
3261 class notok(ok):
3262 def dst(self, dt): return None
3263 self.assertRaises(ValueError, now.astimezone, notok())
3264
Alexander Belopolskyf4b552e2010-06-22 14:43:47 +00003265 # Sometimes blow up. In the following, tzinfo.dst()
3266 # implementation may return None or not Nonedepending on
3267 # whether DST is assumed to be in effect. In this situation,
3268 # a ValueError should be raised by astimezone().
3269 class tricky_notok(ok):
3270 def dst(self, dt):
3271 if dt.year == 2000:
3272 return None
3273 else:
3274 return 10*HOUR
3275 dt = self.theclass(2001, 1, 1).replace(tzinfo=utc_real)
3276 self.assertRaises(ValueError, dt.astimezone, tricky_notok())
3277
Tim Peters52dcce22003-01-23 16:36:11 +00003278 def test_fromutc(self):
3279 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3280 now = datetime.utcnow().replace(tzinfo=utc_real)
3281 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3282 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3283 enow = Eastern.fromutc(now) # doesn't blow up
3284 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3285 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3286 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3287
3288 # Always converts UTC to standard time.
3289 class FauxUSTimeZone(USTimeZone):
3290 def fromutc(self, dt):
3291 return dt + self.stdoffset
3292 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3293
3294 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3295 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3296 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3297
3298 # Check around DST start.
3299 start = self.dston.replace(hour=4, tzinfo=Eastern)
3300 fstart = start.replace(tzinfo=FEastern)
3301 for wall in 23, 0, 1, 3, 4, 5:
3302 expected = start.replace(hour=wall)
3303 if wall == 23:
3304 expected -= timedelta(days=1)
3305 got = Eastern.fromutc(start)
3306 self.assertEqual(expected, got)
3307
3308 expected = fstart + FEastern.stdoffset
3309 got = FEastern.fromutc(fstart)
3310 self.assertEqual(expected, got)
3311
3312 # Ensure astimezone() calls fromutc() too.
3313 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3314 self.assertEqual(expected, got)
3315
3316 start += HOUR
3317 fstart += HOUR
3318
3319 # Check around DST end.
3320 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3321 fstart = start.replace(tzinfo=FEastern)
3322 for wall in 0, 1, 1, 2, 3, 4:
3323 expected = start.replace(hour=wall)
3324 got = Eastern.fromutc(start)
3325 self.assertEqual(expected, got)
3326
3327 expected = fstart + FEastern.stdoffset
3328 got = FEastern.fromutc(fstart)
3329 self.assertEqual(expected, got)
3330
3331 # Ensure astimezone() calls fromutc() too.
3332 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3333 self.assertEqual(expected, got)
3334
3335 start += HOUR
3336 fstart += HOUR
3337
Tim Peters710fb152003-01-02 19:35:54 +00003338
Tim Peters528ca532004-09-16 01:30:50 +00003339#############################################################################
3340# oddballs
3341
3342class Oddballs(unittest.TestCase):
3343
3344 def test_bug_1028306(self):
3345 # Trying to compare a date to a datetime should act like a mixed-
3346 # type comparison, despite that datetime is a subclass of date.
3347 as_date = date.today()
3348 as_datetime = datetime.combine(as_date, time())
Georg Brandlab91fde2009-08-13 08:51:18 +00003349 self.assertTrue(as_date != as_datetime)
3350 self.assertTrue(as_datetime != as_date)
3351 self.assertTrue(not as_date == as_datetime)
3352 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003353 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3354 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3355 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3356 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3357 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3358 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3359 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3360 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3361
3362 # Neverthelss, comparison should work with the base-class (date)
3363 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003364 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003365 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003366 as_different = as_datetime.replace(day= different_day)
3367 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003368
3369 # And date should compare with other subclasses of date. If a
3370 # subclass wants to stop this, it's up to the subclass to do so.
3371 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3372 self.assertEqual(as_date, date_sc)
3373 self.assertEqual(date_sc, as_date)
3374
3375 # Ditto for datetimes.
3376 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3377 as_date.day, 0, 0, 0)
3378 self.assertEqual(as_datetime, datetime_sc)
3379 self.assertEqual(datetime_sc, as_datetime)
3380
Tim Peters2a799bf2002-12-16 20:18:38 +00003381def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003382 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003383
3384if __name__ == "__main__":
3385 test_main()