blob: f56c3f5fe3de7b6dcd48347299ad6677ffe0c3d7 [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"""
Mark Dickinson7000e9e2010-05-09 09:30:06 +00005from __future__ import division
Alexander Belopolskya26cf462010-05-26 19:43:16 +00006import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
8import cPickle
Tim Peters2a799bf2002-12-16 20:18:38 +00009import unittest
10
11from test import test_support
12
13from datetime import MINYEAR, MAXYEAR
14from datetime import timedelta
15from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000016from datetime import time
17from datetime import date, datetime
18
Tim Peters35ad6412003-02-05 04:08:07 +000019pickle_choices = [(pickler, unpickler, proto)
20 for pickler in pickle, cPickle
21 for unpickler in pickle, cPickle
22 for proto in range(3)]
23assert len(pickle_choices) == 2*2*3
Guido van Rossum177e41a2003-01-30 22:06:23 +000024
Tim Peters68124bb2003-02-08 03:46:31 +000025# An arbitrary collection of objects of non-datetime types, for testing
26# mixed-type comparisons.
27OTHERSTUFF = (10, 10L, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000028
Tim Peters2a799bf2002-12-16 20:18:38 +000029
30#############################################################################
31# module tests
32
33class TestModule(unittest.TestCase):
34
35 def test_constants(self):
36 import datetime
37 self.assertEqual(datetime.MINYEAR, 1)
38 self.assertEqual(datetime.MAXYEAR, 9999)
39
40#############################################################################
41# tzinfo tests
42
43class FixedOffset(tzinfo):
44 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000045 if isinstance(offset, int):
46 offset = timedelta(minutes=offset)
47 if isinstance(dstoffset, int):
48 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000049 self.__offset = offset
50 self.__name = name
51 self.__dstoffset = dstoffset
52 def __repr__(self):
53 return self.__name.lower()
54 def utcoffset(self, dt):
55 return self.__offset
56 def tzname(self, dt):
57 return self.__name
58 def dst(self, dt):
59 return self.__dstoffset
60
Tim Petersfb8472c2002-12-21 05:04:42 +000061class PicklableFixedOffset(FixedOffset):
62 def __init__(self, offset=None, name=None, dstoffset=None):
63 FixedOffset.__init__(self, offset, name, dstoffset)
64
Tim Peters2a799bf2002-12-16 20:18:38 +000065class TestTZInfo(unittest.TestCase):
66
67 def test_non_abstractness(self):
68 # In order to allow subclasses to get pickled, the C implementation
69 # wasn't able to get away with having __init__ raise
70 # NotImplementedError.
71 useless = tzinfo()
72 dt = datetime.max
73 self.assertRaises(NotImplementedError, useless.tzname, dt)
74 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
75 self.assertRaises(NotImplementedError, useless.dst, dt)
76
77 def test_subclass_must_override(self):
78 class NotEnough(tzinfo):
79 def __init__(self, offset, name):
80 self.__offset = offset
81 self.__name = name
Benjamin Peterson5c8da862009-06-30 22:57:08 +000082 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000083 ne = NotEnough(3, "NotByALongShot")
Ezio Melottib0f5adc2010-01-24 16:58:36 +000084 self.assertIsInstance(ne, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000085
86 dt = datetime.now()
87 self.assertRaises(NotImplementedError, ne.tzname, dt)
88 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
89 self.assertRaises(NotImplementedError, ne.dst, dt)
90
91 def test_normal(self):
92 fo = FixedOffset(3, "Three")
Ezio Melottib0f5adc2010-01-24 16:58:36 +000093 self.assertIsInstance(fo, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000094 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +000095 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +000096 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +000097 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +000098
99 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000100 # There's no point to pickling tzinfo objects on their own (they
101 # carry no data), but they need to be picklable anyway else
102 # concrete subclasses can't be pickled.
103 orig = tzinfo.__new__(tzinfo)
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200104 self.assertIs(type(orig), tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000105 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000106 green = pickler.dumps(orig, proto)
107 derived = unpickler.loads(green)
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200108 self.assertIs(type(derived), tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000109
110 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000111 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000112 offset = timedelta(minutes=-300)
113 orig = PicklableFixedOffset(offset, 'cookie')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000114 self.assertIsInstance(orig, tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000115 self.assertTrue(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000116 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000117 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000118 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000119 green = pickler.dumps(orig, proto)
120 derived = unpickler.loads(green)
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000121 self.assertIsInstance(derived, tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000122 self.assertTrue(type(derived) is PicklableFixedOffset)
Tim Petersf2715e02003-02-19 02:35:07 +0000123 self.assertEqual(derived.utcoffset(None), offset)
124 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000125
126#############################################################################
Ezio Melotti419e23c2013-08-17 16:56:09 +0300127# Base class for testing a particular aspect of timedelta, time, date and
Tim Peters07534a62003-02-07 22:50:28 +0000128# datetime comparisons.
129
Collin Winterc2898c52007-04-25 17:29:52 +0000130class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000131 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
132
133 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
134 # legit constructor.
135
136 def test_harmless_mixed_comparison(self):
137 me = self.theclass(1, 1, 1)
138
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000139 self.assertFalse(me == ())
140 self.assertTrue(me != ())
141 self.assertFalse(() == me)
142 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000143
Ezio Melottiaa980582010-01-23 23:04:36 +0000144 self.assertIn(me, [1, 20L, [], me])
145 self.assertIn([], [me, 1, 20L, []])
Tim Peters07534a62003-02-07 22:50:28 +0000146
147 def test_harmful_mixed_comparison(self):
148 me = self.theclass(1, 1, 1)
149
150 self.assertRaises(TypeError, lambda: me < ())
151 self.assertRaises(TypeError, lambda: me <= ())
152 self.assertRaises(TypeError, lambda: me > ())
153 self.assertRaises(TypeError, lambda: me >= ())
154
155 self.assertRaises(TypeError, lambda: () < me)
156 self.assertRaises(TypeError, lambda: () <= me)
157 self.assertRaises(TypeError, lambda: () > me)
158 self.assertRaises(TypeError, lambda: () >= me)
159
160 self.assertRaises(TypeError, cmp, (), me)
161 self.assertRaises(TypeError, cmp, me, ())
162
163#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000164# timedelta tests
165
Collin Winterc2898c52007-04-25 17:29:52 +0000166class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000167
168 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000169
170 def test_constructor(self):
171 eq = self.assertEqual
172 td = timedelta
173
174 # Check keyword args to constructor
175 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
176 milliseconds=0, microseconds=0))
177 eq(td(1), td(days=1))
178 eq(td(0, 1), td(seconds=1))
179 eq(td(0, 0, 1), td(microseconds=1))
180 eq(td(weeks=1), td(days=7))
181 eq(td(days=1), td(hours=24))
182 eq(td(hours=1), td(minutes=60))
183 eq(td(minutes=1), td(seconds=60))
184 eq(td(seconds=1), td(milliseconds=1000))
185 eq(td(milliseconds=1), td(microseconds=1000))
186
187 # Check float args to constructor
188 eq(td(weeks=1.0/7), td(days=1))
189 eq(td(days=1.0/24), td(hours=1))
190 eq(td(hours=1.0/60), td(minutes=1))
191 eq(td(minutes=1.0/60), td(seconds=1))
192 eq(td(seconds=0.001), td(milliseconds=1))
193 eq(td(milliseconds=0.001), td(microseconds=1))
194
195 def test_computations(self):
196 eq = self.assertEqual
197 td = timedelta
198
199 a = td(7) # One week
200 b = td(0, 60) # One minute
201 c = td(0, 0, 1000) # One millisecond
202 eq(a+b+c, td(7, 60, 1000))
203 eq(a-b, td(6, 24*3600 - 60))
204 eq(-a, td(-7))
205 eq(+a, td(7))
206 eq(-b, td(-1, 24*3600 - 60))
207 eq(-c, td(-1, 24*3600 - 1, 999000))
208 eq(abs(a), a)
209 eq(abs(-a), a)
210 eq(td(6, 24*3600), a)
211 eq(td(0, 0, 60*1000000), b)
212 eq(a*10, td(70))
213 eq(a*10, 10*a)
214 eq(a*10L, 10*a)
215 eq(b*10, td(0, 600))
216 eq(10*b, td(0, 600))
217 eq(b*10L, td(0, 600))
218 eq(c*10, td(0, 0, 10000))
219 eq(10*c, td(0, 0, 10000))
220 eq(c*10L, td(0, 0, 10000))
221 eq(a*-1, -a)
222 eq(b*-2, -b-b)
223 eq(c*-2, -c+-c)
224 eq(b*(60*24), (b*60)*24)
225 eq(b*(60*24), (60*b)*24)
226 eq(c*1000, td(0, 1))
227 eq(1000*c, td(0, 1))
228 eq(a//7, td(1))
229 eq(b//10, td(0, 6))
230 eq(c//1000, td(0, 0, 1))
231 eq(a//10, td(0, 7*24*360))
232 eq(a//3600000, td(0, 0, 7*24*1000))
233
Alexander Belopolsky07019bc2011-04-05 22:12:22 -0400234 # Issue #11576
235 eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998),
236 td(0, 0, 1))
237 eq(td(999999999, 1, 1) - td(999999999, 1, 0),
238 td(0, 0, 1))
239
240
Tim Peters2a799bf2002-12-16 20:18:38 +0000241 def test_disallowed_computations(self):
242 a = timedelta(42)
243
244 # Add/sub ints, longs, floats should be illegal
245 for i in 1, 1L, 1.0:
246 self.assertRaises(TypeError, lambda: a+i)
247 self.assertRaises(TypeError, lambda: a-i)
248 self.assertRaises(TypeError, lambda: i+a)
249 self.assertRaises(TypeError, lambda: i-a)
250
251 # Mul/div by float isn't supported.
252 x = 2.3
253 self.assertRaises(TypeError, lambda: a*x)
254 self.assertRaises(TypeError, lambda: x*a)
255 self.assertRaises(TypeError, lambda: a/x)
256 self.assertRaises(TypeError, lambda: x/a)
257 self.assertRaises(TypeError, lambda: a // x)
258 self.assertRaises(TypeError, lambda: x // a)
259
Andrew M. Kuchling081bb452008-10-03 16:42:52 +0000260 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000261 # Division by zero doesn't make sense.
262 for zero in 0, 0L:
263 self.assertRaises(TypeError, lambda: zero // a)
264 self.assertRaises(ZeroDivisionError, lambda: a // zero)
265
266 def test_basic_attributes(self):
267 days, seconds, us = 1, 7, 31
268 td = timedelta(days, seconds, us)
269 self.assertEqual(td.days, days)
270 self.assertEqual(td.seconds, seconds)
271 self.assertEqual(td.microseconds, us)
272
Antoine Pitroubcfaf802009-11-25 22:59:36 +0000273 def test_total_seconds(self):
274 td = timedelta(days=365)
275 self.assertEqual(td.total_seconds(), 31536000.0)
276 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
277 td = timedelta(seconds=total_seconds)
278 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson7000e9e2010-05-09 09:30:06 +0000279 # Issue8644: Test that td.total_seconds() has the same
280 # accuracy as td / timedelta(seconds=1).
281 for ms in [-1, -2, -123]:
282 td = timedelta(microseconds=ms)
283 self.assertEqual(td.total_seconds(),
284 ((24*3600*td.days + td.seconds)*10**6
285 + td.microseconds)/10**6)
Antoine Pitroubcfaf802009-11-25 22:59:36 +0000286
Tim Peters2a799bf2002-12-16 20:18:38 +0000287 def test_carries(self):
288 t1 = timedelta(days=100,
289 weeks=-7,
290 hours=-24*(100-49),
291 minutes=-3,
292 seconds=12,
293 microseconds=(3*60 - 12) * 1e6 + 1)
294 t2 = timedelta(microseconds=1)
295 self.assertEqual(t1, t2)
296
297 def test_hash_equality(self):
298 t1 = timedelta(days=100,
299 weeks=-7,
300 hours=-24*(100-49),
301 minutes=-3,
302 seconds=12,
303 microseconds=(3*60 - 12) * 1000000)
304 t2 = timedelta()
305 self.assertEqual(hash(t1), hash(t2))
306
307 t1 += timedelta(weeks=7)
308 t2 += timedelta(days=7*7)
309 self.assertEqual(t1, t2)
310 self.assertEqual(hash(t1), hash(t2))
311
312 d = {t1: 1}
313 d[t2] = 2
314 self.assertEqual(len(d), 1)
315 self.assertEqual(d[t1], 2)
316
317 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000318 args = 12, 34, 56
319 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000320 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000321 green = pickler.dumps(orig, proto)
322 derived = unpickler.loads(green)
323 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000324
325 def test_compare(self):
326 t1 = timedelta(2, 3, 4)
327 t2 = timedelta(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000328 self.assertTrue(t1 == t2)
329 self.assertTrue(t1 <= t2)
330 self.assertTrue(t1 >= t2)
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200331 self.assertFalse(t1 != t2)
332 self.assertFalse(t1 < t2)
333 self.assertFalse(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000334 self.assertEqual(cmp(t1, t2), 0)
335 self.assertEqual(cmp(t2, t1), 0)
336
337 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
338 t2 = timedelta(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000339 self.assertTrue(t1 < t2)
340 self.assertTrue(t2 > t1)
341 self.assertTrue(t1 <= t2)
342 self.assertTrue(t2 >= t1)
343 self.assertTrue(t1 != t2)
344 self.assertTrue(t2 != t1)
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200345 self.assertFalse(t1 == t2)
346 self.assertFalse(t2 == t1)
347 self.assertFalse(t1 > t2)
348 self.assertFalse(t2 < t1)
349 self.assertFalse(t1 >= t2)
350 self.assertFalse(t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000351 self.assertEqual(cmp(t1, t2), -1)
352 self.assertEqual(cmp(t2, t1), 1)
353
Tim Peters68124bb2003-02-08 03:46:31 +0000354 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000355 self.assertEqual(t1 == badarg, False)
356 self.assertEqual(t1 != badarg, True)
357 self.assertEqual(badarg == t1, False)
358 self.assertEqual(badarg != t1, True)
359
Tim Peters2a799bf2002-12-16 20:18:38 +0000360 self.assertRaises(TypeError, lambda: t1 <= badarg)
361 self.assertRaises(TypeError, lambda: t1 < badarg)
362 self.assertRaises(TypeError, lambda: t1 > badarg)
363 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000364 self.assertRaises(TypeError, lambda: badarg <= t1)
365 self.assertRaises(TypeError, lambda: badarg < t1)
366 self.assertRaises(TypeError, lambda: badarg > t1)
367 self.assertRaises(TypeError, lambda: badarg >= t1)
368
369 def test_str(self):
370 td = timedelta
371 eq = self.assertEqual
372
373 eq(str(td(1)), "1 day, 0:00:00")
374 eq(str(td(-1)), "-1 day, 0:00:00")
375 eq(str(td(2)), "2 days, 0:00:00")
376 eq(str(td(-2)), "-2 days, 0:00:00")
377
378 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
379 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
380 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
381 "-210 days, 23:12:34")
382
383 eq(str(td(milliseconds=1)), "0:00:00.001000")
384 eq(str(td(microseconds=3)), "0:00:00.000003")
385
386 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
387 microseconds=999999)),
388 "999999999 days, 23:59:59.999999")
389
390 def test_roundtrip(self):
391 for td in (timedelta(days=999999999, hours=23, minutes=59,
392 seconds=59, microseconds=999999),
393 timedelta(days=-999999999),
394 timedelta(days=1, seconds=2, microseconds=3)):
395
396 # Verify td -> string -> td identity.
397 s = repr(td)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000398 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000399 s = s[9:]
400 td2 = eval(s)
401 self.assertEqual(td, td2)
402
403 # Verify identity via reconstructing from pieces.
404 td2 = timedelta(td.days, td.seconds, td.microseconds)
405 self.assertEqual(td, td2)
406
407 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000408 self.assertIsInstance(timedelta.min, timedelta)
409 self.assertIsInstance(timedelta.max, timedelta)
410 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000411 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000412 self.assertEqual(timedelta.min, timedelta(-999999999))
413 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
414 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
415
416 def test_overflow(self):
417 tiny = timedelta.resolution
418
419 td = timedelta.min + tiny
420 td -= tiny # no problem
421 self.assertRaises(OverflowError, td.__sub__, tiny)
422 self.assertRaises(OverflowError, td.__add__, -tiny)
423
424 td = timedelta.max - tiny
425 td += tiny # no problem
426 self.assertRaises(OverflowError, td.__add__, tiny)
427 self.assertRaises(OverflowError, td.__sub__, -tiny)
428
429 self.assertRaises(OverflowError, lambda: -timedelta.max)
430
431 def test_microsecond_rounding(self):
432 td = timedelta
433 eq = self.assertEqual
434
435 # Single-field rounding.
436 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
437 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
438 eq(td(milliseconds=0.6/1000), td(microseconds=1))
439 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
440
441 # Rounding due to contributions from more than one field.
442 us_per_hour = 3600e6
443 us_per_day = us_per_hour * 24
444 eq(td(days=.4/us_per_day), td(0))
445 eq(td(hours=.2/us_per_hour), td(0))
446 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
447
448 eq(td(days=-.4/us_per_day), td(0))
449 eq(td(hours=-.2/us_per_hour), td(0))
450 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
451
452 def test_massive_normalization(self):
453 td = timedelta(microseconds=-1)
454 self.assertEqual((td.days, td.seconds, td.microseconds),
455 (-1, 24*3600-1, 999999))
456
457 def test_bool(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000458 self.assertTrue(timedelta(1))
459 self.assertTrue(timedelta(0, 1))
460 self.assertTrue(timedelta(0, 0, 1))
461 self.assertTrue(timedelta(microseconds=1))
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200462 self.assertFalse(timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000463
Tim Petersb0c854d2003-05-17 15:57:00 +0000464 def test_subclass_timedelta(self):
465
466 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000467 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000468 def from_td(td):
469 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000470
471 def as_hours(self):
472 sum = (self.days * 24 +
473 self.seconds / 3600.0 +
474 self.microseconds / 3600e6)
475 return round(sum)
476
477 t1 = T(days=1)
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200478 self.assertIs(type(t1), T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000479 self.assertEqual(t1.as_hours(), 24)
480
481 t2 = T(days=-1, seconds=-3600)
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200482 self.assertIs(type(t2), T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000483 self.assertEqual(t2.as_hours(), -25)
484
485 t3 = t1 + t2
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200486 self.assertIs(type(t3), timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000487 t4 = T.from_td(t3)
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200488 self.assertIs(type(t4), T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000489 self.assertEqual(t3.days, t4.days)
490 self.assertEqual(t3.seconds, t4.seconds)
491 self.assertEqual(t3.microseconds, t4.microseconds)
492 self.assertEqual(str(t3), str(t4))
493 self.assertEqual(t4.as_hours(), -1)
494
Serhiy Storchaka5ef883b2017-10-23 19:57:04 +0300495 def test_issue31752(self):
496 # The interpreter shouldn't crash because divmod() returns negative
497 # remainder.
498 class BadInt(int):
499 def __mul__(self, other):
500 return Prod()
501
502 class BadLong(long):
503 def __mul__(self, other):
504 return Prod()
505
506 class Prod:
507 def __radd__(self, other):
508 return Sum()
509
510 class Sum(int):
511 def __divmod__(self, other):
512 # negative remainder
513 return (0, -1)
514
515 timedelta(microseconds=BadInt(1))
516 timedelta(hours=BadInt(1))
517 timedelta(weeks=BadInt(1))
518 timedelta(microseconds=BadLong(1))
519 timedelta(hours=BadLong(1))
520 timedelta(weeks=BadLong(1))
521
522 class Sum(long):
523 def __divmod__(self, other):
524 # negative remainder
525 return (0, -1)
526
527 timedelta(microseconds=BadInt(1))
528 timedelta(hours=BadInt(1))
529 timedelta(weeks=BadInt(1))
530 timedelta(microseconds=BadLong(1))
531 timedelta(hours=BadLong(1))
532 timedelta(weeks=BadLong(1))
533
534
Tim Peters2a799bf2002-12-16 20:18:38 +0000535#############################################################################
536# date tests
537
538class TestDateOnly(unittest.TestCase):
539 # Tests here won't pass if also run on datetime objects, so don't
540 # subclass this to test datetimes too.
541
542 def test_delta_non_days_ignored(self):
543 dt = date(2000, 1, 2)
544 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
545 microseconds=5)
546 days = timedelta(delta.days)
547 self.assertEqual(days, timedelta(1))
548
549 dt2 = dt + delta
550 self.assertEqual(dt2, dt + days)
551
552 dt2 = delta + dt
553 self.assertEqual(dt2, dt + days)
554
555 dt2 = dt - delta
556 self.assertEqual(dt2, dt - days)
557
558 delta = -delta
559 days = timedelta(delta.days)
560 self.assertEqual(days, timedelta(-2))
561
562 dt2 = dt + delta
563 self.assertEqual(dt2, dt + days)
564
565 dt2 = delta + dt
566 self.assertEqual(dt2, dt + days)
567
568 dt2 = dt - delta
569 self.assertEqual(dt2, dt - days)
570
Tim Peters604c0132004-06-07 23:04:33 +0000571class SubclassDate(date):
572 sub_var = 1
573
Collin Winterc2898c52007-04-25 17:29:52 +0000574class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000575 # Tests here should pass for both dates and datetimes, except for a
576 # few tests that TestDateTime overrides.
577
578 theclass = date
579
580 def test_basic_attributes(self):
581 dt = self.theclass(2002, 3, 1)
582 self.assertEqual(dt.year, 2002)
583 self.assertEqual(dt.month, 3)
584 self.assertEqual(dt.day, 1)
585
586 def test_roundtrip(self):
587 for dt in (self.theclass(1, 2, 3),
588 self.theclass.today()):
589 # Verify dt -> string -> date identity.
590 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000591 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000592 s = s[9:]
593 dt2 = eval(s)
594 self.assertEqual(dt, dt2)
595
596 # Verify identity via reconstructing from pieces.
597 dt2 = self.theclass(dt.year, dt.month, dt.day)
598 self.assertEqual(dt, dt2)
599
600 def test_ordinal_conversions(self):
601 # Check some fixed values.
602 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
603 (1, 12, 31, 365),
604 (2, 1, 1, 366),
605 # first example from "Calendrical Calculations"
606 (1945, 11, 12, 710347)]:
607 d = self.theclass(y, m, d)
608 self.assertEqual(n, d.toordinal())
609 fromord = self.theclass.fromordinal(n)
610 self.assertEqual(d, fromord)
611 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000612 # if we're checking something fancier than a date, verify
613 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000614 self.assertEqual(fromord.hour, 0)
615 self.assertEqual(fromord.minute, 0)
616 self.assertEqual(fromord.second, 0)
617 self.assertEqual(fromord.microsecond, 0)
618
Tim Peters0bf60bd2003-01-08 20:40:01 +0000619 # Check first and last days of year spottily across the whole
620 # range of years supported.
621 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000622 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
623 d = self.theclass(year, 1, 1)
624 n = d.toordinal()
625 d2 = self.theclass.fromordinal(n)
626 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000627 # Verify that moving back a day gets to the end of year-1.
628 if year > 1:
629 d = self.theclass.fromordinal(n-1)
630 d2 = self.theclass(year-1, 12, 31)
631 self.assertEqual(d, d2)
632 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000633
634 # Test every day in a leap-year and a non-leap year.
635 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
636 for year, isleap in (2000, True), (2002, False):
637 n = self.theclass(year, 1, 1).toordinal()
638 for month, maxday in zip(range(1, 13), dim):
639 if month == 2 and isleap:
640 maxday += 1
641 for day in range(1, maxday+1):
642 d = self.theclass(year, month, day)
643 self.assertEqual(d.toordinal(), n)
644 self.assertEqual(d, self.theclass.fromordinal(n))
645 n += 1
646
647 def test_extreme_ordinals(self):
648 a = self.theclass.min
649 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
650 aord = a.toordinal()
651 b = a.fromordinal(aord)
652 self.assertEqual(a, b)
653
654 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
655
656 b = a + timedelta(days=1)
657 self.assertEqual(b.toordinal(), aord + 1)
658 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
659
660 a = self.theclass.max
661 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
662 aord = a.toordinal()
663 b = a.fromordinal(aord)
664 self.assertEqual(a, b)
665
666 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
667
668 b = a - timedelta(days=1)
669 self.assertEqual(b.toordinal(), aord - 1)
670 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
671
672 def test_bad_constructor_arguments(self):
673 # bad years
674 self.theclass(MINYEAR, 1, 1) # no exception
675 self.theclass(MAXYEAR, 1, 1) # no exception
676 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
677 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
678 # bad months
679 self.theclass(2000, 1, 1) # no exception
680 self.theclass(2000, 12, 1) # no exception
681 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
682 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
683 # bad days
684 self.theclass(2000, 2, 29) # no exception
685 self.theclass(2004, 2, 29) # no exception
686 self.theclass(2400, 2, 29) # no exception
687 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
688 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
689 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
690 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
691 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
692 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
693
694 def test_hash_equality(self):
695 d = self.theclass(2000, 12, 31)
696 # same thing
697 e = self.theclass(2000, 12, 31)
698 self.assertEqual(d, e)
699 self.assertEqual(hash(d), hash(e))
700
701 dic = {d: 1}
702 dic[e] = 2
703 self.assertEqual(len(dic), 1)
704 self.assertEqual(dic[d], 2)
705 self.assertEqual(dic[e], 2)
706
707 d = self.theclass(2001, 1, 1)
708 # same thing
709 e = self.theclass(2001, 1, 1)
710 self.assertEqual(d, e)
711 self.assertEqual(hash(d), hash(e))
712
713 dic = {d: 1}
714 dic[e] = 2
715 self.assertEqual(len(dic), 1)
716 self.assertEqual(dic[d], 2)
717 self.assertEqual(dic[e], 2)
718
719 def test_computations(self):
720 a = self.theclass(2002, 1, 31)
721 b = self.theclass(1956, 1, 31)
722
723 diff = a-b
724 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
725 self.assertEqual(diff.seconds, 0)
726 self.assertEqual(diff.microseconds, 0)
727
728 day = timedelta(1)
729 week = timedelta(7)
730 a = self.theclass(2002, 3, 2)
731 self.assertEqual(a + day, self.theclass(2002, 3, 3))
732 self.assertEqual(day + a, self.theclass(2002, 3, 3))
733 self.assertEqual(a - day, self.theclass(2002, 3, 1))
734 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
735 self.assertEqual(a + week, self.theclass(2002, 3, 9))
736 self.assertEqual(a - week, self.theclass(2002, 2, 23))
737 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
738 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
739 self.assertEqual((a + week) - a, week)
740 self.assertEqual((a + day) - a, day)
741 self.assertEqual((a - week) - a, -week)
742 self.assertEqual((a - day) - a, -day)
743 self.assertEqual(a - (a + week), -week)
744 self.assertEqual(a - (a + day), -day)
745 self.assertEqual(a - (a - week), week)
746 self.assertEqual(a - (a - day), day)
747
748 # Add/sub ints, longs, floats should be illegal
749 for i in 1, 1L, 1.0:
750 self.assertRaises(TypeError, lambda: a+i)
751 self.assertRaises(TypeError, lambda: a-i)
752 self.assertRaises(TypeError, lambda: i+a)
753 self.assertRaises(TypeError, lambda: i-a)
754
755 # delta - date is senseless.
756 self.assertRaises(TypeError, lambda: day - a)
757 # mixing date and (delta or date) via * or // is senseless
758 self.assertRaises(TypeError, lambda: day * a)
759 self.assertRaises(TypeError, lambda: a * day)
760 self.assertRaises(TypeError, lambda: day // a)
761 self.assertRaises(TypeError, lambda: a // day)
762 self.assertRaises(TypeError, lambda: a * a)
763 self.assertRaises(TypeError, lambda: a // a)
764 # date + date is senseless
765 self.assertRaises(TypeError, lambda: a + a)
766
767 def test_overflow(self):
768 tiny = self.theclass.resolution
769
Alexander Belopolsky9292ee02010-05-27 20:55:27 +0000770 for delta in [tiny, timedelta(1), timedelta(2)]:
771 dt = self.theclass.min + delta
772 dt -= delta # no problem
773 self.assertRaises(OverflowError, dt.__sub__, delta)
774 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000775
Alexander Belopolsky9292ee02010-05-27 20:55:27 +0000776 dt = self.theclass.max - delta
777 dt += delta # no problem
778 self.assertRaises(OverflowError, dt.__add__, delta)
779 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000780
781 def test_fromtimestamp(self):
782 import time
783
784 # Try an arbitrary fixed value.
785 year, month, day = 1999, 9, 19
786 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
787 d = self.theclass.fromtimestamp(ts)
788 self.assertEqual(d.year, year)
789 self.assertEqual(d.month, month)
790 self.assertEqual(d.day, day)
791
Tim Peters1b6f7a92004-06-20 02:50:16 +0000792 def test_insane_fromtimestamp(self):
793 # It's possible that some platform maps time_t to double,
794 # and that this test will fail there. This test should
795 # exempt such platforms (provided they return reasonable
796 # results!).
797 for insane in -1e200, 1e200:
798 self.assertRaises(ValueError, self.theclass.fromtimestamp,
799 insane)
800
Tim Peters2a799bf2002-12-16 20:18:38 +0000801 def test_today(self):
802 import time
803
804 # We claim that today() is like fromtimestamp(time.time()), so
805 # prove it.
806 for dummy in range(3):
807 today = self.theclass.today()
808 ts = time.time()
809 todayagain = self.theclass.fromtimestamp(ts)
810 if today == todayagain:
811 break
812 # There are several legit reasons that could fail:
813 # 1. It recently became midnight, between the today() and the
814 # time() calls.
815 # 2. The platform time() has such fine resolution that we'll
816 # never get the same value twice.
817 # 3. The platform time() has poor resolution, and we just
818 # happened to call today() right before a resolution quantum
819 # boundary.
820 # 4. The system clock got fiddled between calls.
821 # In any case, wait a little while and try again.
822 time.sleep(0.1)
823
824 # It worked or it didn't. If it didn't, assume it's reason #2, and
825 # let the test pass if they're within half a second of each other.
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200826 if today != todayagain:
827 self.assertAlmostEqual(todayagain, today,
828 delta=timedelta(seconds=0.5))
Tim Peters2a799bf2002-12-16 20:18:38 +0000829
830 def test_weekday(self):
831 for i in range(7):
832 # March 4, 2002 is a Monday
833 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
834 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
835 # January 2, 1956 is a Monday
836 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
837 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
838
839 def test_isocalendar(self):
840 # Check examples from
841 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
842 for i in range(7):
843 d = self.theclass(2003, 12, 22+i)
844 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
845 d = self.theclass(2003, 12, 29) + timedelta(i)
846 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
847 d = self.theclass(2004, 1, 5+i)
848 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
849 d = self.theclass(2009, 12, 21+i)
850 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
851 d = self.theclass(2009, 12, 28) + timedelta(i)
852 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
853 d = self.theclass(2010, 1, 4+i)
854 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
855
856 def test_iso_long_years(self):
857 # Calculate long ISO years and compare to table from
858 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
859 ISO_LONG_YEARS_TABLE = """
860 4 32 60 88
861 9 37 65 93
862 15 43 71 99
863 20 48 76
864 26 54 82
865
866 105 133 161 189
867 111 139 167 195
868 116 144 172
869 122 150 178
870 128 156 184
871
872 201 229 257 285
873 207 235 263 291
874 212 240 268 296
875 218 246 274
876 224 252 280
877
878 303 331 359 387
879 308 336 364 392
880 314 342 370 398
881 320 348 376
882 325 353 381
883 """
884 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
885 iso_long_years.sort()
886 L = []
887 for i in range(400):
888 d = self.theclass(2000+i, 12, 31)
889 d1 = self.theclass(1600+i, 12, 31)
890 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
891 if d.isocalendar()[1] == 53:
892 L.append(i)
893 self.assertEqual(L, iso_long_years)
894
895 def test_isoformat(self):
896 t = self.theclass(2, 3, 2)
897 self.assertEqual(t.isoformat(), "0002-03-02")
898
899 def test_ctime(self):
900 t = self.theclass(2002, 3, 2)
901 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
902
903 def test_strftime(self):
904 t = self.theclass(2005, 3, 2)
905 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000906 self.assertEqual(t.strftime(""), "") # SF bug #761337
Georg Brandl4ddfcd32006-09-30 11:17:34 +0000907 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000908
909 self.assertRaises(TypeError, t.strftime) # needs an arg
910 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
911 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
912
Gregory P. Smith137d8242008-06-02 04:05:52 +0000913 # test that unicode input is allowed (issue 2782)
914 self.assertEqual(t.strftime(u"%m"), "03")
915
Tim Peters2a799bf2002-12-16 20:18:38 +0000916 # A naive object replaces %z and %Z w/ empty strings.
917 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
918
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000919 #make sure that invalid format specifiers are handled correctly
Kristján Valur Jónsson8adc0b52009-01-15 09:09:13 +0000920 #self.assertRaises(ValueError, t.strftime, "%e")
921 #self.assertRaises(ValueError, t.strftime, "%")
922 #self.assertRaises(ValueError, t.strftime, "%#")
923
924 #oh well, some systems just ignore those invalid ones.
Martin Panterb1d867f2016-05-26 05:28:50 +0000925 #at least, exercise them to make sure that no crashes
Kristján Valur Jónsson8adc0b52009-01-15 09:09:13 +0000926 #are generated
927 for f in ["%e", "%", "%#"]:
928 try:
929 t.strftime(f)
930 except ValueError:
931 pass
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000932
933 #check that this standard extension works
934 t.strftime("%f")
935
Gregory P. Smith137d8242008-06-02 04:05:52 +0000936
Eric Smitha9f7d622008-02-17 19:46:49 +0000937 def test_format(self):
938 dt = self.theclass(2007, 9, 10)
939 self.assertEqual(dt.__format__(''), str(dt))
940
941 # check that a derived class's __str__() gets called
942 class A(self.theclass):
943 def __str__(self):
944 return 'A'
945 a = A(2007, 9, 10)
946 self.assertEqual(a.__format__(''), 'A')
947
948 # check that a derived class's strftime gets called
949 class B(self.theclass):
950 def strftime(self, format_spec):
951 return 'B'
952 b = B(2007, 9, 10)
953 self.assertEqual(b.__format__(''), str(dt))
954
955 for fmt in ["m:%m d:%d y:%y",
956 "m:%m d:%d y:%y H:%H M:%M S:%S",
957 "%z %Z",
958 ]:
959 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
960 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
961 self.assertEqual(b.__format__(fmt), 'B')
962
Tim Peters2a799bf2002-12-16 20:18:38 +0000963 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000964 self.assertIsInstance(self.theclass.min, self.theclass)
965 self.assertIsInstance(self.theclass.max, self.theclass)
966 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000967 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000968
969 def test_extreme_timedelta(self):
970 big = self.theclass.max - self.theclass.min
971 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
972 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
973 # n == 315537897599999999 ~= 2**58.13
974 justasbig = timedelta(0, 0, n)
975 self.assertEqual(big, justasbig)
976 self.assertEqual(self.theclass.min + big, self.theclass.max)
977 self.assertEqual(self.theclass.max - big, self.theclass.min)
978
979 def test_timetuple(self):
980 for i in range(7):
981 # January 2, 1956 is a Monday (0)
982 d = self.theclass(1956, 1, 2+i)
983 t = d.timetuple()
984 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
985 # February 1, 1956 is a Wednesday (2)
986 d = self.theclass(1956, 2, 1+i)
987 t = d.timetuple()
988 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
989 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
990 # of the year.
991 d = self.theclass(1956, 3, 1+i)
992 t = d.timetuple()
993 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
994 self.assertEqual(t.tm_year, 1956)
995 self.assertEqual(t.tm_mon, 3)
996 self.assertEqual(t.tm_mday, 1+i)
997 self.assertEqual(t.tm_hour, 0)
998 self.assertEqual(t.tm_min, 0)
999 self.assertEqual(t.tm_sec, 0)
1000 self.assertEqual(t.tm_wday, (3+i)%7)
1001 self.assertEqual(t.tm_yday, 61+i)
1002 self.assertEqual(t.tm_isdst, -1)
1003
1004 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001005 args = 6, 7, 23
1006 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001007 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001008 green = pickler.dumps(orig, proto)
1009 derived = unpickler.loads(green)
1010 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001011
1012 def test_compare(self):
1013 t1 = self.theclass(2, 3, 4)
1014 t2 = self.theclass(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001015 self.assertTrue(t1 == t2)
1016 self.assertTrue(t1 <= t2)
1017 self.assertTrue(t1 >= t2)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001018 self.assertFalse(t1 != t2)
1019 self.assertFalse(t1 < t2)
1020 self.assertFalse(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001021 self.assertEqual(cmp(t1, t2), 0)
1022 self.assertEqual(cmp(t2, t1), 0)
1023
1024 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1025 t2 = self.theclass(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001026 self.assertTrue(t1 < t2)
1027 self.assertTrue(t2 > t1)
1028 self.assertTrue(t1 <= t2)
1029 self.assertTrue(t2 >= t1)
1030 self.assertTrue(t1 != t2)
1031 self.assertTrue(t2 != t1)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001032 self.assertFalse(t1 == t2)
1033 self.assertFalse(t2 == t1)
1034 self.assertFalse(t1 > t2)
1035 self.assertFalse(t2 < t1)
1036 self.assertFalse(t1 >= t2)
1037 self.assertFalse(t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001038 self.assertEqual(cmp(t1, t2), -1)
1039 self.assertEqual(cmp(t2, t1), 1)
1040
Tim Peters68124bb2003-02-08 03:46:31 +00001041 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001042 self.assertEqual(t1 == badarg, False)
1043 self.assertEqual(t1 != badarg, True)
1044 self.assertEqual(badarg == t1, False)
1045 self.assertEqual(badarg != t1, True)
1046
Tim Peters2a799bf2002-12-16 20:18:38 +00001047 self.assertRaises(TypeError, lambda: t1 < badarg)
1048 self.assertRaises(TypeError, lambda: t1 > badarg)
1049 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001050 self.assertRaises(TypeError, lambda: badarg <= t1)
1051 self.assertRaises(TypeError, lambda: badarg < t1)
1052 self.assertRaises(TypeError, lambda: badarg > t1)
1053 self.assertRaises(TypeError, lambda: badarg >= t1)
1054
Tim Peters8d81a012003-01-24 22:36:34 +00001055 def test_mixed_compare(self):
1056 our = self.theclass(2000, 4, 5)
1057 self.assertRaises(TypeError, cmp, our, 1)
1058 self.assertRaises(TypeError, cmp, 1, our)
1059
1060 class AnotherDateTimeClass(object):
1061 def __cmp__(self, other):
1062 # Return "equal" so calling this can't be confused with
1063 # compare-by-address (which never says "equal" for distinct
1064 # objects).
1065 return 0
Nick Coghlan48361f52008-08-11 15:45:58 +00001066 __hash__ = None # Silence Py3k warning
Tim Peters8d81a012003-01-24 22:36:34 +00001067
1068 # This still errors, because date and datetime comparison raise
1069 # TypeError instead of NotImplemented when they don't know what to
1070 # do, in order to stop comparison from falling back to the default
1071 # compare-by-address.
1072 their = AnotherDateTimeClass()
1073 self.assertRaises(TypeError, cmp, our, their)
1074 # Oops: The next stab raises TypeError in the C implementation,
1075 # but not in the Python implementation of datetime. The difference
1076 # is due to that the Python implementation defines __cmp__ but
1077 # the C implementation defines tp_richcompare. This is more pain
1078 # to fix than it's worth, so commenting out the test.
1079 # self.assertEqual(cmp(their, our), 0)
1080
1081 # But date and datetime comparison return NotImplemented instead if the
1082 # other object has a timetuple attr. This gives the other object a
1083 # chance to do the comparison.
1084 class Comparable(AnotherDateTimeClass):
1085 def timetuple(self):
1086 return ()
1087
1088 their = Comparable()
1089 self.assertEqual(cmp(our, their), 0)
1090 self.assertEqual(cmp(their, our), 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001091 self.assertTrue(our == their)
1092 self.assertTrue(their == our)
Tim Peters8d81a012003-01-24 22:36:34 +00001093
Tim Peters2a799bf2002-12-16 20:18:38 +00001094 def test_bool(self):
1095 # All dates are considered true.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001096 self.assertTrue(self.theclass.min)
1097 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001098
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001099 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001100 # For nasty technical reasons, we can't handle years before 1900.
1101 cls = self.theclass
1102 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1103 for y in 1, 49, 51, 99, 100, 1000, 1899:
1104 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001105
1106 def test_replace(self):
1107 cls = self.theclass
1108 args = [1, 2, 3]
1109 base = cls(*args)
1110 self.assertEqual(base, base.replace())
1111
1112 i = 0
1113 for name, newval in (("year", 2),
1114 ("month", 3),
1115 ("day", 4)):
1116 newargs = args[:]
1117 newargs[i] = newval
1118 expected = cls(*newargs)
1119 got = base.replace(**{name: newval})
1120 self.assertEqual(expected, got)
1121 i += 1
1122
1123 # Out of bounds.
1124 base = cls(2000, 2, 29)
1125 self.assertRaises(ValueError, base.replace, year=2001)
1126
Tim Petersa98924a2003-05-17 05:55:19 +00001127 def test_subclass_date(self):
1128
1129 class C(self.theclass):
1130 theAnswer = 42
1131
1132 def __new__(cls, *args, **kws):
1133 temp = kws.copy()
1134 extra = temp.pop('extra')
1135 result = self.theclass.__new__(cls, *args, **temp)
1136 result.extra = extra
1137 return result
1138
1139 def newmeth(self, start):
1140 return start + self.year + self.month
1141
1142 args = 2003, 4, 14
1143
1144 dt1 = self.theclass(*args)
1145 dt2 = C(*args, **{'extra': 7})
1146
1147 self.assertEqual(dt2.__class__, C)
1148 self.assertEqual(dt2.theAnswer, 42)
1149 self.assertEqual(dt2.extra, 7)
1150 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1151 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1152
Tim Peters604c0132004-06-07 23:04:33 +00001153 def test_pickling_subclass_date(self):
1154
1155 args = 6, 7, 23
1156 orig = SubclassDate(*args)
1157 for pickler, unpickler, proto in pickle_choices:
1158 green = pickler.dumps(orig, proto)
1159 derived = unpickler.loads(green)
1160 self.assertEqual(orig, derived)
1161
Tim Peters3f606292004-03-21 23:38:41 +00001162 def test_backdoor_resistance(self):
1163 # For fast unpickling, the constructor accepts a pickle string.
1164 # This is a low-overhead backdoor. A user can (by intent or
1165 # mistake) pass a string directly, which (if it's the right length)
1166 # will get treated like a pickle, and bypass the normal sanity
1167 # checks in the constructor. This can create insane objects.
1168 # The constructor doesn't want to burn the time to validate all
1169 # fields, but does check the month field. This stops, e.g.,
1170 # datetime.datetime('1995-03-25') from yielding an insane object.
1171 base = '1995-03-25'
1172 if not issubclass(self.theclass, datetime):
1173 base = base[:4]
1174 for month_byte in '9', chr(0), chr(13), '\xff':
1175 self.assertRaises(TypeError, self.theclass,
1176 base[:2] + month_byte + base[3:])
1177 for ord_byte in range(1, 13):
1178 # This shouldn't blow up because of the month byte alone. If
1179 # the implementation changes to do more-careful checking, it may
1180 # blow up because other fields are insane.
1181 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001182
Tim Peters2a799bf2002-12-16 20:18:38 +00001183#############################################################################
1184# datetime tests
1185
Tim Peters604c0132004-06-07 23:04:33 +00001186class SubclassDatetime(datetime):
1187 sub_var = 1
1188
Tim Peters2a799bf2002-12-16 20:18:38 +00001189class TestDateTime(TestDate):
1190
1191 theclass = datetime
1192
1193 def test_basic_attributes(self):
1194 dt = self.theclass(2002, 3, 1, 12, 0)
1195 self.assertEqual(dt.year, 2002)
1196 self.assertEqual(dt.month, 3)
1197 self.assertEqual(dt.day, 1)
1198 self.assertEqual(dt.hour, 12)
1199 self.assertEqual(dt.minute, 0)
1200 self.assertEqual(dt.second, 0)
1201 self.assertEqual(dt.microsecond, 0)
1202
1203 def test_basic_attributes_nonzero(self):
1204 # Make sure all attributes are non-zero so bugs in
1205 # bit-shifting access show up.
1206 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1207 self.assertEqual(dt.year, 2002)
1208 self.assertEqual(dt.month, 3)
1209 self.assertEqual(dt.day, 1)
1210 self.assertEqual(dt.hour, 12)
1211 self.assertEqual(dt.minute, 59)
1212 self.assertEqual(dt.second, 59)
1213 self.assertEqual(dt.microsecond, 8000)
1214
1215 def test_roundtrip(self):
1216 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1217 self.theclass.now()):
1218 # Verify dt -> string -> datetime identity.
1219 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001220 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001221 s = s[9:]
1222 dt2 = eval(s)
1223 self.assertEqual(dt, dt2)
1224
1225 # Verify identity via reconstructing from pieces.
1226 dt2 = self.theclass(dt.year, dt.month, dt.day,
1227 dt.hour, dt.minute, dt.second,
1228 dt.microsecond)
1229 self.assertEqual(dt, dt2)
1230
1231 def test_isoformat(self):
1232 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1233 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1234 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1235 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arc8645a5c2009-12-29 22:03:38 +00001236 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001237 # str is ISO format with the separator forced to a blank.
1238 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1239
1240 t = self.theclass(2, 3, 2)
1241 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1242 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1243 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1244 # str is ISO format with the separator forced to a blank.
1245 self.assertEqual(str(t), "0002-03-02 00:00:00")
1246
Eric Smitha9f7d622008-02-17 19:46:49 +00001247 def test_format(self):
1248 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1249 self.assertEqual(dt.__format__(''), str(dt))
1250
1251 # check that a derived class's __str__() gets called
1252 class A(self.theclass):
1253 def __str__(self):
1254 return 'A'
1255 a = A(2007, 9, 10, 4, 5, 1, 123)
1256 self.assertEqual(a.__format__(''), 'A')
1257
1258 # check that a derived class's strftime gets called
1259 class B(self.theclass):
1260 def strftime(self, format_spec):
1261 return 'B'
1262 b = B(2007, 9, 10, 4, 5, 1, 123)
1263 self.assertEqual(b.__format__(''), str(dt))
1264
1265 for fmt in ["m:%m d:%d y:%y",
1266 "m:%m d:%d y:%y H:%H M:%M S:%S",
1267 "%z %Z",
1268 ]:
1269 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1270 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1271 self.assertEqual(b.__format__(fmt), 'B')
1272
Tim Peters2a799bf2002-12-16 20:18:38 +00001273 def test_more_ctime(self):
1274 # Test fields that TestDate doesn't touch.
1275 import time
1276
1277 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1278 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1279 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1280 # out. The difference is that t.ctime() produces " 2" for the day,
1281 # but platform ctime() produces "02" for the day. According to
1282 # C99, t.ctime() is correct here.
1283 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1284
1285 # So test a case where that difference doesn't matter.
1286 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1287 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1288
1289 def test_tz_independent_comparing(self):
1290 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1291 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1292 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1293 self.assertEqual(dt1, dt3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001294 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001295
1296 # Make sure comparison doesn't forget microseconds, and isn't done
1297 # via comparing a float timestamp (an IEEE double doesn't have enough
1298 # precision to span microsecond resolution across years 1 thru 9999,
1299 # so comparing via timestamp necessarily calls some distinct values
1300 # equal).
1301 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1302 us = timedelta(microseconds=1)
1303 dt2 = dt1 + us
1304 self.assertEqual(dt2 - dt1, us)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001305 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001306
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001307 def test_strftime_with_bad_tzname_replace(self):
1308 # verify ok if tzinfo.tzname().replace() returns a non-string
1309 class MyTzInfo(FixedOffset):
1310 def tzname(self, dt):
1311 class MyStr(str):
1312 def replace(self, *args):
1313 return None
1314 return MyStr('name')
1315 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1316 self.assertRaises(TypeError, t.strftime, '%Z')
1317
Tim Peters2a799bf2002-12-16 20:18:38 +00001318 def test_bad_constructor_arguments(self):
1319 # bad years
1320 self.theclass(MINYEAR, 1, 1) # no exception
1321 self.theclass(MAXYEAR, 1, 1) # no exception
1322 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1323 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1324 # bad months
1325 self.theclass(2000, 1, 1) # no exception
1326 self.theclass(2000, 12, 1) # no exception
1327 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1328 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1329 # bad days
1330 self.theclass(2000, 2, 29) # no exception
1331 self.theclass(2004, 2, 29) # no exception
1332 self.theclass(2400, 2, 29) # no exception
1333 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1334 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1335 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1336 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1337 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1338 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1339 # bad hours
1340 self.theclass(2000, 1, 31, 0) # no exception
1341 self.theclass(2000, 1, 31, 23) # no exception
1342 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1343 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1344 # bad minutes
1345 self.theclass(2000, 1, 31, 23, 0) # no exception
1346 self.theclass(2000, 1, 31, 23, 59) # no exception
1347 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1348 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1349 # bad seconds
1350 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1351 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1352 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1353 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1354 # bad microseconds
1355 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1356 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1357 self.assertRaises(ValueError, self.theclass,
1358 2000, 1, 31, 23, 59, 59, -1)
1359 self.assertRaises(ValueError, self.theclass,
1360 2000, 1, 31, 23, 59, 59,
1361 1000000)
1362
1363 def test_hash_equality(self):
1364 d = self.theclass(2000, 12, 31, 23, 30, 17)
1365 e = self.theclass(2000, 12, 31, 23, 30, 17)
1366 self.assertEqual(d, e)
1367 self.assertEqual(hash(d), hash(e))
1368
1369 dic = {d: 1}
1370 dic[e] = 2
1371 self.assertEqual(len(dic), 1)
1372 self.assertEqual(dic[d], 2)
1373 self.assertEqual(dic[e], 2)
1374
1375 d = self.theclass(2001, 1, 1, 0, 5, 17)
1376 e = self.theclass(2001, 1, 1, 0, 5, 17)
1377 self.assertEqual(d, e)
1378 self.assertEqual(hash(d), hash(e))
1379
1380 dic = {d: 1}
1381 dic[e] = 2
1382 self.assertEqual(len(dic), 1)
1383 self.assertEqual(dic[d], 2)
1384 self.assertEqual(dic[e], 2)
1385
1386 def test_computations(self):
1387 a = self.theclass(2002, 1, 31)
1388 b = self.theclass(1956, 1, 31)
1389 diff = a-b
1390 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1391 self.assertEqual(diff.seconds, 0)
1392 self.assertEqual(diff.microseconds, 0)
1393 a = self.theclass(2002, 3, 2, 17, 6)
1394 millisec = timedelta(0, 0, 1000)
1395 hour = timedelta(0, 3600)
1396 day = timedelta(1)
1397 week = timedelta(7)
1398 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1399 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1400 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1401 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1402 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1403 self.assertEqual(a - hour, a + -hour)
1404 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1405 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1406 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1407 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1408 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1409 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1410 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1411 self.assertEqual((a + week) - a, week)
1412 self.assertEqual((a + day) - a, day)
1413 self.assertEqual((a + hour) - a, hour)
1414 self.assertEqual((a + millisec) - a, millisec)
1415 self.assertEqual((a - week) - a, -week)
1416 self.assertEqual((a - day) - a, -day)
1417 self.assertEqual((a - hour) - a, -hour)
1418 self.assertEqual((a - millisec) - a, -millisec)
1419 self.assertEqual(a - (a + week), -week)
1420 self.assertEqual(a - (a + day), -day)
1421 self.assertEqual(a - (a + hour), -hour)
1422 self.assertEqual(a - (a + millisec), -millisec)
1423 self.assertEqual(a - (a - week), week)
1424 self.assertEqual(a - (a - day), day)
1425 self.assertEqual(a - (a - hour), hour)
1426 self.assertEqual(a - (a - millisec), millisec)
1427 self.assertEqual(a + (week + day + hour + millisec),
1428 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1429 self.assertEqual(a + (week + day + hour + millisec),
1430 (((a + week) + day) + hour) + millisec)
1431 self.assertEqual(a - (week + day + hour + millisec),
1432 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1433 self.assertEqual(a - (week + day + hour + millisec),
1434 (((a - week) - day) - hour) - millisec)
1435 # Add/sub ints, longs, floats should be illegal
1436 for i in 1, 1L, 1.0:
1437 self.assertRaises(TypeError, lambda: a+i)
1438 self.assertRaises(TypeError, lambda: a-i)
1439 self.assertRaises(TypeError, lambda: i+a)
1440 self.assertRaises(TypeError, lambda: i-a)
1441
1442 # delta - datetime is senseless.
1443 self.assertRaises(TypeError, lambda: day - a)
1444 # mixing datetime and (delta or datetime) via * or // is senseless
1445 self.assertRaises(TypeError, lambda: day * a)
1446 self.assertRaises(TypeError, lambda: a * day)
1447 self.assertRaises(TypeError, lambda: day // a)
1448 self.assertRaises(TypeError, lambda: a // day)
1449 self.assertRaises(TypeError, lambda: a * a)
1450 self.assertRaises(TypeError, lambda: a // a)
1451 # datetime + datetime is senseless
1452 self.assertRaises(TypeError, lambda: a + a)
1453
1454 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001455 args = 6, 7, 23, 20, 59, 1, 64**2
1456 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001457 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001458 green = pickler.dumps(orig, proto)
1459 derived = unpickler.loads(green)
1460 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001461
Guido van Rossum275666f2003-02-07 21:49:01 +00001462 def test_more_pickling(self):
1463 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
Serhiy Storchaka655720e2014-12-15 14:02:43 +02001464 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1465 s = pickle.dumps(a, proto)
1466 b = pickle.loads(s)
1467 self.assertEqual(b.year, 2003)
1468 self.assertEqual(b.month, 2)
1469 self.assertEqual(b.day, 7)
Guido van Rossum275666f2003-02-07 21:49:01 +00001470
Tim Peters604c0132004-06-07 23:04:33 +00001471 def test_pickling_subclass_datetime(self):
1472 args = 6, 7, 23, 20, 59, 1, 64**2
1473 orig = SubclassDatetime(*args)
1474 for pickler, unpickler, proto in pickle_choices:
1475 green = pickler.dumps(orig, proto)
1476 derived = unpickler.loads(green)
1477 self.assertEqual(orig, derived)
1478
Tim Peters2a799bf2002-12-16 20:18:38 +00001479 def test_more_compare(self):
1480 # The test_compare() inherited from TestDate covers the error cases.
1481 # We just want to test lexicographic ordering on the members datetime
1482 # has that date lacks.
1483 args = [2000, 11, 29, 20, 58, 16, 999998]
1484 t1 = self.theclass(*args)
1485 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001486 self.assertTrue(t1 == t2)
1487 self.assertTrue(t1 <= t2)
1488 self.assertTrue(t1 >= t2)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001489 self.assertFalse(t1 != t2)
1490 self.assertFalse(t1 < t2)
1491 self.assertFalse(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001492 self.assertEqual(cmp(t1, t2), 0)
1493 self.assertEqual(cmp(t2, t1), 0)
1494
1495 for i in range(len(args)):
1496 newargs = args[:]
1497 newargs[i] = args[i] + 1
1498 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001499 self.assertTrue(t1 < t2)
1500 self.assertTrue(t2 > t1)
1501 self.assertTrue(t1 <= t2)
1502 self.assertTrue(t2 >= t1)
1503 self.assertTrue(t1 != t2)
1504 self.assertTrue(t2 != t1)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001505 self.assertFalse(t1 == t2)
1506 self.assertFalse(t2 == t1)
1507 self.assertFalse(t1 > t2)
1508 self.assertFalse(t2 < t1)
1509 self.assertFalse(t1 >= t2)
1510 self.assertFalse(t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001511 self.assertEqual(cmp(t1, t2), -1)
1512 self.assertEqual(cmp(t2, t1), 1)
1513
1514
1515 # A helper for timestamp constructor tests.
1516 def verify_field_equality(self, expected, got):
1517 self.assertEqual(expected.tm_year, got.year)
1518 self.assertEqual(expected.tm_mon, got.month)
1519 self.assertEqual(expected.tm_mday, got.day)
1520 self.assertEqual(expected.tm_hour, got.hour)
1521 self.assertEqual(expected.tm_min, got.minute)
1522 self.assertEqual(expected.tm_sec, got.second)
1523
1524 def test_fromtimestamp(self):
1525 import time
1526
1527 ts = time.time()
1528 expected = time.localtime(ts)
1529 got = self.theclass.fromtimestamp(ts)
1530 self.verify_field_equality(expected, got)
1531
1532 def test_utcfromtimestamp(self):
1533 import time
1534
1535 ts = time.time()
1536 expected = time.gmtime(ts)
1537 got = self.theclass.utcfromtimestamp(ts)
1538 self.verify_field_equality(expected, got)
1539
Georg Brandl6d78a582006-04-28 19:09:24 +00001540 def test_microsecond_rounding(self):
1541 # Test whether fromtimestamp "rounds up" floats that are less
1542 # than one microsecond smaller than an integer.
Ezio Melotti2623a372010-11-21 13:34:58 +00001543 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1544 self.theclass.fromtimestamp(1))
Georg Brandl6d78a582006-04-28 19:09:24 +00001545
Tim Peters1b6f7a92004-06-20 02:50:16 +00001546 def test_insane_fromtimestamp(self):
1547 # It's possible that some platform maps time_t to double,
1548 # and that this test will fail there. This test should
1549 # exempt such platforms (provided they return reasonable
1550 # results!).
1551 for insane in -1e200, 1e200:
1552 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1553 insane)
1554
1555 def test_insane_utcfromtimestamp(self):
1556 # It's possible that some platform maps time_t to double,
1557 # and that this test will fail there. This test should
1558 # exempt such platforms (provided they return reasonable
1559 # results!).
1560 for insane in -1e200, 1e200:
1561 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1562 insane)
Alexander Belopolsky58451d22010-05-26 20:45:37 +00001563 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossum2054ee92007-03-06 15:50:01 +00001564 def test_negative_float_fromtimestamp(self):
1565 # The result is tz-dependent; at least test that this doesn't
1566 # fail (like it did before bug 1646728 was fixed).
1567 self.theclass.fromtimestamp(-1.05)
1568
Alexander Belopolsky58451d22010-05-26 20:45:37 +00001569 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossum2054ee92007-03-06 15:50:01 +00001570 def test_negative_float_utcfromtimestamp(self):
1571 d = self.theclass.utcfromtimestamp(-1.05)
Ezio Melotti2623a372010-11-21 13:34:58 +00001572 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossum2054ee92007-03-06 15:50:01 +00001573
Tim Peters2a799bf2002-12-16 20:18:38 +00001574 def test_utcnow(self):
1575 import time
1576
1577 # Call it a success if utcnow() and utcfromtimestamp() are within
1578 # a second of each other.
1579 tolerance = timedelta(seconds=1)
1580 for dummy in range(3):
1581 from_now = self.theclass.utcnow()
1582 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1583 if abs(from_timestamp - from_now) <= tolerance:
1584 break
1585 # Else try again a few times.
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001586 self.assertLessEqual(abs(from_timestamp - from_now), tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001587
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001588 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001589 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001590
Skip Montanarofc070d22008-03-15 16:04:45 +00001591 string = '2004-12-01 13:02:47.197'
1592 format = '%Y-%m-%d %H:%M:%S.%f'
1593 result, frac = _strptime._strptime(string, format)
1594 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001595 got = self.theclass.strptime(string, format)
1596 self.assertEqual(expected, got)
1597
Tim Peters2a799bf2002-12-16 20:18:38 +00001598 def test_more_timetuple(self):
1599 # This tests fields beyond those tested by the TestDate.test_timetuple.
1600 t = self.theclass(2004, 12, 31, 6, 22, 33)
1601 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1602 self.assertEqual(t.timetuple(),
1603 (t.year, t.month, t.day,
1604 t.hour, t.minute, t.second,
1605 t.weekday(),
1606 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1607 -1))
1608 tt = t.timetuple()
1609 self.assertEqual(tt.tm_year, t.year)
1610 self.assertEqual(tt.tm_mon, t.month)
1611 self.assertEqual(tt.tm_mday, t.day)
1612 self.assertEqual(tt.tm_hour, t.hour)
1613 self.assertEqual(tt.tm_min, t.minute)
1614 self.assertEqual(tt.tm_sec, t.second)
1615 self.assertEqual(tt.tm_wday, t.weekday())
1616 self.assertEqual(tt.tm_yday, t.toordinal() -
1617 date(t.year, 1, 1).toordinal() + 1)
1618 self.assertEqual(tt.tm_isdst, -1)
1619
1620 def test_more_strftime(self):
1621 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001622 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1623 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1624 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001625
1626 def test_extract(self):
1627 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1628 self.assertEqual(dt.date(), date(2002, 3, 4))
1629 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1630
1631 def test_combine(self):
1632 d = date(2002, 3, 4)
1633 t = time(18, 45, 3, 1234)
1634 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1635 combine = self.theclass.combine
1636 dt = combine(d, t)
1637 self.assertEqual(dt, expected)
1638
1639 dt = combine(time=t, date=d)
1640 self.assertEqual(dt, expected)
1641
1642 self.assertEqual(d, dt.date())
1643 self.assertEqual(t, dt.time())
1644 self.assertEqual(dt, combine(dt.date(), dt.time()))
1645
1646 self.assertRaises(TypeError, combine) # need an arg
1647 self.assertRaises(TypeError, combine, d) # need two args
1648 self.assertRaises(TypeError, combine, t, d) # args reversed
1649 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1650 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1651
Tim Peters12bf3392002-12-24 05:41:27 +00001652 def test_replace(self):
1653 cls = self.theclass
1654 args = [1, 2, 3, 4, 5, 6, 7]
1655 base = cls(*args)
1656 self.assertEqual(base, base.replace())
1657
1658 i = 0
1659 for name, newval in (("year", 2),
1660 ("month", 3),
1661 ("day", 4),
1662 ("hour", 5),
1663 ("minute", 6),
1664 ("second", 7),
1665 ("microsecond", 8)):
1666 newargs = args[:]
1667 newargs[i] = newval
1668 expected = cls(*newargs)
1669 got = base.replace(**{name: newval})
1670 self.assertEqual(expected, got)
1671 i += 1
1672
1673 # Out of bounds.
1674 base = cls(2000, 2, 29)
1675 self.assertRaises(ValueError, base.replace, year=2001)
1676
Tim Peters80475bb2002-12-25 07:40:55 +00001677 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001678 # Pretty boring! The TZ test is more interesting here. astimezone()
1679 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001680 dt = self.theclass.now()
1681 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001682 self.assertRaises(TypeError, dt.astimezone) # not enough args
1683 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1684 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001685 self.assertRaises(ValueError, dt.astimezone, f) # naive
1686 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001687
Tim Peters52dcce22003-01-23 16:36:11 +00001688 class Bogus(tzinfo):
1689 def utcoffset(self, dt): return None
1690 def dst(self, dt): return timedelta(0)
1691 bog = Bogus()
1692 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1693
1694 class AlsoBogus(tzinfo):
1695 def utcoffset(self, dt): return timedelta(0)
1696 def dst(self, dt): return None
1697 alsobog = AlsoBogus()
1698 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001699
Tim Petersa98924a2003-05-17 05:55:19 +00001700 def test_subclass_datetime(self):
1701
1702 class C(self.theclass):
1703 theAnswer = 42
1704
1705 def __new__(cls, *args, **kws):
1706 temp = kws.copy()
1707 extra = temp.pop('extra')
1708 result = self.theclass.__new__(cls, *args, **temp)
1709 result.extra = extra
1710 return result
1711
1712 def newmeth(self, start):
1713 return start + self.year + self.month + self.second
1714
1715 args = 2003, 4, 14, 12, 13, 41
1716
1717 dt1 = self.theclass(*args)
1718 dt2 = C(*args, **{'extra': 7})
1719
1720 self.assertEqual(dt2.__class__, C)
1721 self.assertEqual(dt2.theAnswer, 42)
1722 self.assertEqual(dt2.extra, 7)
1723 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1724 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1725 dt1.second - 7)
1726
Tim Peters604c0132004-06-07 23:04:33 +00001727class SubclassTime(time):
1728 sub_var = 1
1729
Collin Winterc2898c52007-04-25 17:29:52 +00001730class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001731
1732 theclass = time
1733
1734 def test_basic_attributes(self):
1735 t = self.theclass(12, 0)
1736 self.assertEqual(t.hour, 12)
1737 self.assertEqual(t.minute, 0)
1738 self.assertEqual(t.second, 0)
1739 self.assertEqual(t.microsecond, 0)
1740
1741 def test_basic_attributes_nonzero(self):
1742 # Make sure all attributes are non-zero so bugs in
1743 # bit-shifting access show up.
1744 t = self.theclass(12, 59, 59, 8000)
1745 self.assertEqual(t.hour, 12)
1746 self.assertEqual(t.minute, 59)
1747 self.assertEqual(t.second, 59)
1748 self.assertEqual(t.microsecond, 8000)
1749
1750 def test_roundtrip(self):
1751 t = self.theclass(1, 2, 3, 4)
1752
1753 # Verify t -> string -> time identity.
1754 s = repr(t)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001755 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001756 s = s[9:]
1757 t2 = eval(s)
1758 self.assertEqual(t, t2)
1759
1760 # Verify identity via reconstructing from pieces.
1761 t2 = self.theclass(t.hour, t.minute, t.second,
1762 t.microsecond)
1763 self.assertEqual(t, t2)
1764
1765 def test_comparing(self):
1766 args = [1, 2, 3, 4]
1767 t1 = self.theclass(*args)
1768 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001769 self.assertTrue(t1 == t2)
1770 self.assertTrue(t1 <= t2)
1771 self.assertTrue(t1 >= t2)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001772 self.assertFalse(t1 != t2)
1773 self.assertFalse(t1 < t2)
1774 self.assertFalse(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001775 self.assertEqual(cmp(t1, t2), 0)
1776 self.assertEqual(cmp(t2, t1), 0)
1777
1778 for i in range(len(args)):
1779 newargs = args[:]
1780 newargs[i] = args[i] + 1
1781 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001782 self.assertTrue(t1 < t2)
1783 self.assertTrue(t2 > t1)
1784 self.assertTrue(t1 <= t2)
1785 self.assertTrue(t2 >= t1)
1786 self.assertTrue(t1 != t2)
1787 self.assertTrue(t2 != t1)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001788 self.assertFalse(t1 == t2)
1789 self.assertFalse(t2 == t1)
1790 self.assertFalse(t1 > t2)
1791 self.assertFalse(t2 < t1)
1792 self.assertFalse(t1 >= t2)
1793 self.assertFalse(t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001794 self.assertEqual(cmp(t1, t2), -1)
1795 self.assertEqual(cmp(t2, t1), 1)
1796
Tim Peters68124bb2003-02-08 03:46:31 +00001797 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001798 self.assertEqual(t1 == badarg, False)
1799 self.assertEqual(t1 != badarg, True)
1800 self.assertEqual(badarg == t1, False)
1801 self.assertEqual(badarg != t1, True)
1802
Tim Peters2a799bf2002-12-16 20:18:38 +00001803 self.assertRaises(TypeError, lambda: t1 <= badarg)
1804 self.assertRaises(TypeError, lambda: t1 < badarg)
1805 self.assertRaises(TypeError, lambda: t1 > badarg)
1806 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001807 self.assertRaises(TypeError, lambda: badarg <= t1)
1808 self.assertRaises(TypeError, lambda: badarg < t1)
1809 self.assertRaises(TypeError, lambda: badarg > t1)
1810 self.assertRaises(TypeError, lambda: badarg >= t1)
1811
1812 def test_bad_constructor_arguments(self):
1813 # bad hours
1814 self.theclass(0, 0) # no exception
1815 self.theclass(23, 0) # no exception
1816 self.assertRaises(ValueError, self.theclass, -1, 0)
1817 self.assertRaises(ValueError, self.theclass, 24, 0)
1818 # bad minutes
1819 self.theclass(23, 0) # no exception
1820 self.theclass(23, 59) # no exception
1821 self.assertRaises(ValueError, self.theclass, 23, -1)
1822 self.assertRaises(ValueError, self.theclass, 23, 60)
1823 # bad seconds
1824 self.theclass(23, 59, 0) # no exception
1825 self.theclass(23, 59, 59) # no exception
1826 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1827 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1828 # bad microseconds
1829 self.theclass(23, 59, 59, 0) # no exception
1830 self.theclass(23, 59, 59, 999999) # no exception
1831 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1832 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1833
1834 def test_hash_equality(self):
1835 d = self.theclass(23, 30, 17)
1836 e = self.theclass(23, 30, 17)
1837 self.assertEqual(d, e)
1838 self.assertEqual(hash(d), hash(e))
1839
1840 dic = {d: 1}
1841 dic[e] = 2
1842 self.assertEqual(len(dic), 1)
1843 self.assertEqual(dic[d], 2)
1844 self.assertEqual(dic[e], 2)
1845
1846 d = self.theclass(0, 5, 17)
1847 e = self.theclass(0, 5, 17)
1848 self.assertEqual(d, e)
1849 self.assertEqual(hash(d), hash(e))
1850
1851 dic = {d: 1}
1852 dic[e] = 2
1853 self.assertEqual(len(dic), 1)
1854 self.assertEqual(dic[d], 2)
1855 self.assertEqual(dic[e], 2)
1856
1857 def test_isoformat(self):
1858 t = self.theclass(4, 5, 1, 123)
1859 self.assertEqual(t.isoformat(), "04:05:01.000123")
1860 self.assertEqual(t.isoformat(), str(t))
1861
1862 t = self.theclass()
1863 self.assertEqual(t.isoformat(), "00:00:00")
1864 self.assertEqual(t.isoformat(), str(t))
1865
1866 t = self.theclass(microsecond=1)
1867 self.assertEqual(t.isoformat(), "00:00:00.000001")
1868 self.assertEqual(t.isoformat(), str(t))
1869
1870 t = self.theclass(microsecond=10)
1871 self.assertEqual(t.isoformat(), "00:00:00.000010")
1872 self.assertEqual(t.isoformat(), str(t))
1873
1874 t = self.theclass(microsecond=100)
1875 self.assertEqual(t.isoformat(), "00:00:00.000100")
1876 self.assertEqual(t.isoformat(), str(t))
1877
1878 t = self.theclass(microsecond=1000)
1879 self.assertEqual(t.isoformat(), "00:00:00.001000")
1880 self.assertEqual(t.isoformat(), str(t))
1881
1882 t = self.theclass(microsecond=10000)
1883 self.assertEqual(t.isoformat(), "00:00:00.010000")
1884 self.assertEqual(t.isoformat(), str(t))
1885
1886 t = self.theclass(microsecond=100000)
1887 self.assertEqual(t.isoformat(), "00:00:00.100000")
1888 self.assertEqual(t.isoformat(), str(t))
1889
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001890 def test_1653736(self):
1891 # verify it doesn't accept extra keyword arguments
1892 t = self.theclass(second=1)
1893 self.assertRaises(TypeError, t.isoformat, foo=3)
1894
Tim Peters2a799bf2002-12-16 20:18:38 +00001895 def test_strftime(self):
1896 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001897 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001898 # A naive object replaces %z and %Z with empty strings.
1899 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1900
Eric Smitha9f7d622008-02-17 19:46:49 +00001901 def test_format(self):
1902 t = self.theclass(1, 2, 3, 4)
1903 self.assertEqual(t.__format__(''), str(t))
1904
1905 # check that a derived class's __str__() gets called
1906 class A(self.theclass):
1907 def __str__(self):
1908 return 'A'
1909 a = A(1, 2, 3, 4)
1910 self.assertEqual(a.__format__(''), 'A')
1911
1912 # check that a derived class's strftime gets called
1913 class B(self.theclass):
1914 def strftime(self, format_spec):
1915 return 'B'
1916 b = B(1, 2, 3, 4)
1917 self.assertEqual(b.__format__(''), str(t))
1918
1919 for fmt in ['%H %M %S',
1920 ]:
1921 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1922 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1923 self.assertEqual(b.__format__(fmt), 'B')
1924
Tim Peters2a799bf2002-12-16 20:18:38 +00001925 def test_str(self):
1926 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1927 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1928 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1929 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1930 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1931
1932 def test_repr(self):
1933 name = 'datetime.' + self.theclass.__name__
1934 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1935 "%s(1, 2, 3, 4)" % name)
1936 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1937 "%s(10, 2, 3, 4000)" % name)
1938 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1939 "%s(0, 2, 3, 400000)" % name)
1940 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1941 "%s(12, 2, 3)" % name)
1942 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1943 "%s(23, 15)" % name)
1944
1945 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +00001946 self.assertIsInstance(self.theclass.min, self.theclass)
1947 self.assertIsInstance(self.theclass.max, self.theclass)
1948 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001949 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001950
1951 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001952 args = 20, 59, 16, 64**2
1953 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001954 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001955 green = pickler.dumps(orig, proto)
1956 derived = unpickler.loads(green)
1957 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001958
Tim Peters604c0132004-06-07 23:04:33 +00001959 def test_pickling_subclass_time(self):
1960 args = 20, 59, 16, 64**2
1961 orig = SubclassTime(*args)
1962 for pickler, unpickler, proto in pickle_choices:
1963 green = pickler.dumps(orig, proto)
1964 derived = unpickler.loads(green)
1965 self.assertEqual(orig, derived)
1966
Tim Peters2a799bf2002-12-16 20:18:38 +00001967 def test_bool(self):
1968 cls = self.theclass
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001969 self.assertTrue(cls(1))
1970 self.assertTrue(cls(0, 1))
1971 self.assertTrue(cls(0, 0, 1))
1972 self.assertTrue(cls(0, 0, 0, 1))
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001973 self.assertFalse(cls(0))
1974 self.assertFalse(cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001975
Tim Peters12bf3392002-12-24 05:41:27 +00001976 def test_replace(self):
1977 cls = self.theclass
1978 args = [1, 2, 3, 4]
1979 base = cls(*args)
1980 self.assertEqual(base, base.replace())
1981
1982 i = 0
1983 for name, newval in (("hour", 5),
1984 ("minute", 6),
1985 ("second", 7),
1986 ("microsecond", 8)):
1987 newargs = args[:]
1988 newargs[i] = newval
1989 expected = cls(*newargs)
1990 got = base.replace(**{name: newval})
1991 self.assertEqual(expected, got)
1992 i += 1
1993
1994 # Out of bounds.
1995 base = cls(1)
1996 self.assertRaises(ValueError, base.replace, hour=24)
1997 self.assertRaises(ValueError, base.replace, minute=-1)
1998 self.assertRaises(ValueError, base.replace, second=100)
1999 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2000
Tim Petersa98924a2003-05-17 05:55:19 +00002001 def test_subclass_time(self):
2002
2003 class C(self.theclass):
2004 theAnswer = 42
2005
2006 def __new__(cls, *args, **kws):
2007 temp = kws.copy()
2008 extra = temp.pop('extra')
2009 result = self.theclass.__new__(cls, *args, **temp)
2010 result.extra = extra
2011 return result
2012
2013 def newmeth(self, start):
2014 return start + self.hour + self.second
2015
2016 args = 4, 5, 6
2017
2018 dt1 = self.theclass(*args)
2019 dt2 = C(*args, **{'extra': 7})
2020
2021 self.assertEqual(dt2.__class__, C)
2022 self.assertEqual(dt2.theAnswer, 42)
2023 self.assertEqual(dt2.extra, 7)
2024 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2025 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2026
Armin Rigof4afb212005-11-07 07:15:48 +00002027 def test_backdoor_resistance(self):
2028 # see TestDate.test_backdoor_resistance().
2029 base = '2:59.0'
2030 for hour_byte in ' ', '9', chr(24), '\xff':
2031 self.assertRaises(TypeError, self.theclass,
2032 hour_byte + base[1:])
2033
Tim Peters855fe882002-12-22 03:43:39 +00002034# A mixin for classes with a tzinfo= argument. Subclasses must define
Martin Panterb1d867f2016-05-26 05:28:50 +00002035# theclass as a class attribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002036# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00002037class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002038
Tim Petersbad8ff02002-12-30 20:52:32 +00002039 def test_argument_passing(self):
2040 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002041 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002042 class introspective(tzinfo):
2043 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002044 def utcoffset(self, dt):
2045 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002046 dst = utcoffset
2047
2048 obj = cls(1, 2, 3, tzinfo=introspective())
2049
Tim Peters0bf60bd2003-01-08 20:40:01 +00002050 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002051 self.assertEqual(obj.tzname(), expected)
2052
Tim Peters0bf60bd2003-01-08 20:40:01 +00002053 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002054 self.assertEqual(obj.utcoffset(), expected)
2055 self.assertEqual(obj.dst(), expected)
2056
Tim Peters855fe882002-12-22 03:43:39 +00002057 def test_bad_tzinfo_classes(self):
2058 cls = self.theclass
2059 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002060
Tim Peters855fe882002-12-22 03:43:39 +00002061 class NiceTry(object):
2062 def __init__(self): pass
2063 def utcoffset(self, dt): pass
2064 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2065
2066 class BetterTry(tzinfo):
2067 def __init__(self): pass
2068 def utcoffset(self, dt): pass
2069 b = BetterTry()
2070 t = cls(1, 1, 1, tzinfo=b)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002071 self.assertIs(t.tzinfo, b)
Tim Peters855fe882002-12-22 03:43:39 +00002072
2073 def test_utc_offset_out_of_bounds(self):
2074 class Edgy(tzinfo):
2075 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002076 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002077 def utcoffset(self, dt):
2078 return self.offset
2079
2080 cls = self.theclass
2081 for offset, legit in ((-1440, False),
2082 (-1439, True),
2083 (1439, True),
2084 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002085 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002086 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002087 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002088 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002089 else:
2090 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002091 if legit:
2092 aofs = abs(offset)
2093 h, m = divmod(aofs, 60)
2094 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002095 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002096 t = t.timetz()
2097 self.assertEqual(str(t), "01:02:03" + tag)
2098 else:
2099 self.assertRaises(ValueError, str, t)
2100
2101 def test_tzinfo_classes(self):
2102 cls = self.theclass
2103 class C1(tzinfo):
2104 def utcoffset(self, dt): return None
2105 def dst(self, dt): return None
2106 def tzname(self, dt): return None
2107 for t in (cls(1, 1, 1),
2108 cls(1, 1, 1, tzinfo=None),
2109 cls(1, 1, 1, tzinfo=C1())):
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002110 self.assertIsNone(t.utcoffset())
2111 self.assertIsNone(t.dst())
2112 self.assertIsNone(t.tzname())
Tim Peters855fe882002-12-22 03:43:39 +00002113
Tim Peters855fe882002-12-22 03:43:39 +00002114 class C3(tzinfo):
2115 def utcoffset(self, dt): return timedelta(minutes=-1439)
2116 def dst(self, dt): return timedelta(minutes=1439)
2117 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002118 t = cls(1, 1, 1, tzinfo=C3())
2119 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2120 self.assertEqual(t.dst(), timedelta(minutes=1439))
2121 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002122
2123 # Wrong types.
2124 class C4(tzinfo):
2125 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002126 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002127 def tzname(self, dt): return 0
2128 t = cls(1, 1, 1, tzinfo=C4())
2129 self.assertRaises(TypeError, t.utcoffset)
2130 self.assertRaises(TypeError, t.dst)
2131 self.assertRaises(TypeError, t.tzname)
2132
2133 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002134 class C6(tzinfo):
2135 def utcoffset(self, dt): return timedelta(hours=-24)
2136 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002137 t = cls(1, 1, 1, tzinfo=C6())
2138 self.assertRaises(ValueError, t.utcoffset)
2139 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002140
2141 # Not a whole number of minutes.
2142 class C7(tzinfo):
2143 def utcoffset(self, dt): return timedelta(seconds=61)
2144 def dst(self, dt): return timedelta(microseconds=-81)
2145 t = cls(1, 1, 1, tzinfo=C7())
2146 self.assertRaises(ValueError, t.utcoffset)
2147 self.assertRaises(ValueError, t.dst)
2148
Tim Peters4c0db782002-12-26 05:01:19 +00002149 def test_aware_compare(self):
2150 cls = self.theclass
2151
Tim Peters60c76e42002-12-27 00:41:11 +00002152 # Ensure that utcoffset() gets ignored if the comparands have
2153 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002154 class OperandDependentOffset(tzinfo):
2155 def utcoffset(self, t):
2156 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002157 # d0 and d1 equal after adjustment
2158 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002159 else:
Tim Peters397301e2003-01-02 21:28:08 +00002160 # d2 off in the weeds
2161 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002162
2163 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2164 d0 = base.replace(minute=3)
2165 d1 = base.replace(minute=9)
2166 d2 = base.replace(minute=11)
2167 for x in d0, d1, d2:
2168 for y in d0, d1, d2:
2169 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002170 expected = cmp(x.minute, y.minute)
2171 self.assertEqual(got, expected)
2172
2173 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002174 # Note that a time can't actually have an operand-depedent offset,
2175 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2176 # so skip this test for time.
2177 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002178 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2179 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2180 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2181 for x in d0, d1, d2:
2182 for y in d0, d1, d2:
2183 got = cmp(x, y)
2184 if (x is d0 or x is d1) and (y is d0 or y is d1):
2185 expected = 0
2186 elif x is y is d2:
2187 expected = 0
2188 elif x is d2:
2189 expected = -1
2190 else:
2191 assert y is d2
2192 expected = 1
2193 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002194
Tim Peters855fe882002-12-22 03:43:39 +00002195
Tim Peters0bf60bd2003-01-08 20:40:01 +00002196# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002197class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002198 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002199
2200 def test_empty(self):
2201 t = self.theclass()
2202 self.assertEqual(t.hour, 0)
2203 self.assertEqual(t.minute, 0)
2204 self.assertEqual(t.second, 0)
2205 self.assertEqual(t.microsecond, 0)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002206 self.assertIsNone(t.tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +00002207
Tim Peters2a799bf2002-12-16 20:18:38 +00002208 def test_zones(self):
2209 est = FixedOffset(-300, "EST", 1)
2210 utc = FixedOffset(0, "UTC", -2)
2211 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002212 t1 = time( 7, 47, tzinfo=est)
2213 t2 = time(12, 47, tzinfo=utc)
2214 t3 = time(13, 47, tzinfo=met)
2215 t4 = time(microsecond=40)
2216 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002217
2218 self.assertEqual(t1.tzinfo, est)
2219 self.assertEqual(t2.tzinfo, utc)
2220 self.assertEqual(t3.tzinfo, met)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002221 self.assertIsNone(t4.tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +00002222 self.assertEqual(t5.tzinfo, utc)
2223
Tim Peters855fe882002-12-22 03:43:39 +00002224 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2225 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2226 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002227 self.assertIsNone(t4.utcoffset())
Tim Peters2a799bf2002-12-16 20:18:38 +00002228 self.assertRaises(TypeError, t1.utcoffset, "no args")
2229
2230 self.assertEqual(t1.tzname(), "EST")
2231 self.assertEqual(t2.tzname(), "UTC")
2232 self.assertEqual(t3.tzname(), "MET")
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002233 self.assertIsNone(t4.tzname())
Tim Peters2a799bf2002-12-16 20:18:38 +00002234 self.assertRaises(TypeError, t1.tzname, "no args")
2235
Tim Peters855fe882002-12-22 03:43:39 +00002236 self.assertEqual(t1.dst(), timedelta(minutes=1))
2237 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2238 self.assertEqual(t3.dst(), timedelta(minutes=3))
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002239 self.assertIsNone(t4.dst())
Tim Peters2a799bf2002-12-16 20:18:38 +00002240 self.assertRaises(TypeError, t1.dst, "no args")
2241
2242 self.assertEqual(hash(t1), hash(t2))
2243 self.assertEqual(hash(t1), hash(t3))
2244 self.assertEqual(hash(t2), hash(t3))
2245
2246 self.assertEqual(t1, t2)
2247 self.assertEqual(t1, t3)
2248 self.assertEqual(t2, t3)
2249 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2250 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2251 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2252
2253 self.assertEqual(str(t1), "07:47:00-05:00")
2254 self.assertEqual(str(t2), "12:47:00+00:00")
2255 self.assertEqual(str(t3), "13:47:00+01:00")
2256 self.assertEqual(str(t4), "00:00:00.000040")
2257 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2258
2259 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2260 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2261 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2262 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2263 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2264
Tim Peters0bf60bd2003-01-08 20:40:01 +00002265 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002266 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2267 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2268 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2269 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2270 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2271
2272 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2273 "07:47:00 %Z=EST %z=-0500")
2274 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2275 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2276
2277 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002278 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002279 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2280 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2281
Tim Petersb92bb712002-12-21 17:44:07 +00002282 # Check that an invalid tzname result raises an exception.
2283 class Badtzname(tzinfo):
2284 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002285 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002286 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2287 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002288
2289 def test_hash_edge_cases(self):
2290 # Offsets that overflow a basic time.
2291 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2292 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2293 self.assertEqual(hash(t1), hash(t2))
2294
2295 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2296 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2297 self.assertEqual(hash(t1), hash(t2))
2298
Tim Peters2a799bf2002-12-16 20:18:38 +00002299 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002300 # Try one without a tzinfo.
2301 args = 20, 59, 16, 64**2
2302 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002303 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002304 green = pickler.dumps(orig, proto)
2305 derived = unpickler.loads(green)
2306 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002307
2308 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002309 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002310 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002311 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002312 green = pickler.dumps(orig, proto)
2313 derived = unpickler.loads(green)
2314 self.assertEqual(orig, derived)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002315 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002316 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2317 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002318
2319 def test_more_bool(self):
2320 # Test cases with non-None tzinfo.
2321 cls = self.theclass
2322
2323 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002324 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002325
2326 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002327 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002328
2329 t = cls(5, tzinfo=FixedOffset(300, ""))
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002330 self.assertFalse(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002331
2332 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002333 self.assertFalse(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002334
2335 # Mostly ensuring this doesn't overflow internally.
2336 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002337 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002338
2339 # But this should yield a value error -- the utcoffset is bogus.
2340 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2341 self.assertRaises(ValueError, lambda: bool(t))
2342
2343 # Likewise.
2344 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2345 self.assertRaises(ValueError, lambda: bool(t))
2346
Tim Peters12bf3392002-12-24 05:41:27 +00002347 def test_replace(self):
2348 cls = self.theclass
2349 z100 = FixedOffset(100, "+100")
2350 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2351 args = [1, 2, 3, 4, z100]
2352 base = cls(*args)
2353 self.assertEqual(base, base.replace())
2354
2355 i = 0
2356 for name, newval in (("hour", 5),
2357 ("minute", 6),
2358 ("second", 7),
2359 ("microsecond", 8),
2360 ("tzinfo", zm200)):
2361 newargs = args[:]
2362 newargs[i] = newval
2363 expected = cls(*newargs)
2364 got = base.replace(**{name: newval})
2365 self.assertEqual(expected, got)
2366 i += 1
2367
2368 # Ensure we can get rid of a tzinfo.
2369 self.assertEqual(base.tzname(), "+100")
2370 base2 = base.replace(tzinfo=None)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002371 self.assertIsNone(base2.tzinfo)
2372 self.assertIsNone(base2.tzname())
Tim Peters12bf3392002-12-24 05:41:27 +00002373
2374 # Ensure we can add one.
2375 base3 = base2.replace(tzinfo=z100)
2376 self.assertEqual(base, base3)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002377 self.assertIs(base.tzinfo, base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002378
2379 # Out of bounds.
2380 base = cls(1)
2381 self.assertRaises(ValueError, base.replace, hour=24)
2382 self.assertRaises(ValueError, base.replace, minute=-1)
2383 self.assertRaises(ValueError, base.replace, second=100)
2384 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2385
Tim Peters60c76e42002-12-27 00:41:11 +00002386 def test_mixed_compare(self):
2387 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002388 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002389 self.assertEqual(t1, t2)
2390 t2 = t2.replace(tzinfo=None)
2391 self.assertEqual(t1, t2)
2392 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2393 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002394 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2395 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002396
Tim Peters0bf60bd2003-01-08 20:40:01 +00002397 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002398 class Varies(tzinfo):
2399 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002400 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002401 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002402 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002403 return self.offset
2404
2405 v = Varies()
2406 t1 = t2.replace(tzinfo=v)
2407 t2 = t2.replace(tzinfo=v)
2408 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2409 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2410 self.assertEqual(t1, t2)
2411
2412 # But if they're not identical, it isn't ignored.
2413 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002414 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002415
Tim Petersa98924a2003-05-17 05:55:19 +00002416 def test_subclass_timetz(self):
2417
2418 class C(self.theclass):
2419 theAnswer = 42
2420
2421 def __new__(cls, *args, **kws):
2422 temp = kws.copy()
2423 extra = temp.pop('extra')
2424 result = self.theclass.__new__(cls, *args, **temp)
2425 result.extra = extra
2426 return result
2427
2428 def newmeth(self, start):
2429 return start + self.hour + self.second
2430
2431 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2432
2433 dt1 = self.theclass(*args)
2434 dt2 = C(*args, **{'extra': 7})
2435
2436 self.assertEqual(dt2.__class__, C)
2437 self.assertEqual(dt2.theAnswer, 42)
2438 self.assertEqual(dt2.extra, 7)
2439 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2440 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2441
Tim Peters4c0db782002-12-26 05:01:19 +00002442
Tim Peters0bf60bd2003-01-08 20:40:01 +00002443# Testing datetime objects with a non-None tzinfo.
2444
Collin Winterc2898c52007-04-25 17:29:52 +00002445class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002446 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002447
2448 def test_trivial(self):
2449 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2450 self.assertEqual(dt.year, 1)
2451 self.assertEqual(dt.month, 2)
2452 self.assertEqual(dt.day, 3)
2453 self.assertEqual(dt.hour, 4)
2454 self.assertEqual(dt.minute, 5)
2455 self.assertEqual(dt.second, 6)
2456 self.assertEqual(dt.microsecond, 7)
2457 self.assertEqual(dt.tzinfo, None)
2458
2459 def test_even_more_compare(self):
2460 # The test_compare() and test_more_compare() inherited from TestDate
2461 # and TestDateTime covered non-tzinfo cases.
2462
2463 # Smallest possible after UTC adjustment.
2464 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2465 # Largest possible after UTC adjustment.
2466 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2467 tzinfo=FixedOffset(-1439, ""))
2468
2469 # Make sure those compare correctly, and w/o overflow.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002470 self.assertTrue(t1 < t2)
2471 self.assertTrue(t1 != t2)
2472 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002473
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002474 self.assertTrue(t1 == t1)
2475 self.assertTrue(t2 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002476
2477 # Equal afer adjustment.
2478 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2479 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2480 self.assertEqual(t1, t2)
2481
2482 # Change t1 not to subtract a minute, and t1 should be larger.
2483 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002484 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002485
2486 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2487 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002488 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002489
2490 # Back to the original t1, but make seconds resolve it.
2491 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2492 second=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002493 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002494
2495 # Likewise, but make microseconds resolve it.
2496 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2497 microsecond=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002498 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002499
2500 # Make t2 naive and it should fail.
2501 t2 = self.theclass.min
2502 self.assertRaises(TypeError, lambda: t1 == t2)
2503 self.assertEqual(t2, t2)
2504
2505 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2506 class Naive(tzinfo):
2507 def utcoffset(self, dt): return None
2508 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2509 self.assertRaises(TypeError, lambda: t1 == t2)
2510 self.assertEqual(t2, t2)
2511
2512 # OTOH, it's OK to compare two of these mixing the two ways of being
2513 # naive.
2514 t1 = self.theclass(5, 6, 7)
2515 self.assertEqual(t1, t2)
2516
2517 # Try a bogus uctoffset.
2518 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002519 def utcoffset(self, dt):
2520 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002521 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2522 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002523 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002524
Tim Peters2a799bf2002-12-16 20:18:38 +00002525 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002526 # Try one without a tzinfo.
2527 args = 6, 7, 23, 20, 59, 1, 64**2
2528 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002529 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002530 green = pickler.dumps(orig, proto)
2531 derived = unpickler.loads(green)
2532 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002533
2534 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002535 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002536 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002537 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002538 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002539 green = pickler.dumps(orig, proto)
2540 derived = unpickler.loads(green)
2541 self.assertEqual(orig, derived)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002542 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002543 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2544 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002545
2546 def test_extreme_hashes(self):
2547 # If an attempt is made to hash these via subtracting the offset
2548 # then hashing a datetime object, OverflowError results. The
2549 # Python implementation used to blow up here.
2550 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2551 hash(t)
2552 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2553 tzinfo=FixedOffset(-1439, ""))
2554 hash(t)
2555
2556 # OTOH, an OOB offset should blow up.
2557 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2558 self.assertRaises(ValueError, hash, t)
2559
2560 def test_zones(self):
2561 est = FixedOffset(-300, "EST")
2562 utc = FixedOffset(0, "UTC")
2563 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002564 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2565 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2566 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002567 self.assertEqual(t1.tzinfo, est)
2568 self.assertEqual(t2.tzinfo, utc)
2569 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002570 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2571 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2572 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002573 self.assertEqual(t1.tzname(), "EST")
2574 self.assertEqual(t2.tzname(), "UTC")
2575 self.assertEqual(t3.tzname(), "MET")
2576 self.assertEqual(hash(t1), hash(t2))
2577 self.assertEqual(hash(t1), hash(t3))
2578 self.assertEqual(hash(t2), hash(t3))
2579 self.assertEqual(t1, t2)
2580 self.assertEqual(t1, t3)
2581 self.assertEqual(t2, t3)
2582 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2583 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2584 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002585 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002586 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2587 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2588 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2589
2590 def test_combine(self):
2591 met = FixedOffset(60, "MET")
2592 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002593 tz = time(18, 45, 3, 1234, tzinfo=met)
2594 dt = datetime.combine(d, tz)
2595 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002596 tzinfo=met))
2597
2598 def test_extract(self):
2599 met = FixedOffset(60, "MET")
2600 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2601 self.assertEqual(dt.date(), date(2002, 3, 4))
2602 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002603 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002604
2605 def test_tz_aware_arithmetic(self):
2606 import random
2607
2608 now = self.theclass.now()
2609 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002610 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002611 nowaware = self.theclass.combine(now.date(), timeaware)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002612 self.assertIs(nowaware.tzinfo, tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002613 self.assertEqual(nowaware.timetz(), timeaware)
2614
2615 # Can't mix aware and non-aware.
2616 self.assertRaises(TypeError, lambda: now - nowaware)
2617 self.assertRaises(TypeError, lambda: nowaware - now)
2618
Tim Peters0bf60bd2003-01-08 20:40:01 +00002619 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002620 self.assertRaises(TypeError, lambda: now + nowaware)
2621 self.assertRaises(TypeError, lambda: nowaware + now)
2622 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2623
2624 # Subtracting should yield 0.
2625 self.assertEqual(now - now, timedelta(0))
2626 self.assertEqual(nowaware - nowaware, timedelta(0))
2627
2628 # Adding a delta should preserve tzinfo.
2629 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2630 nowawareplus = nowaware + delta
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002631 self.assertIs(nowaware.tzinfo, tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002632 nowawareplus2 = delta + nowaware
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002633 self.assertIs(nowawareplus2.tzinfo, tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002634 self.assertEqual(nowawareplus, nowawareplus2)
2635
2636 # that - delta should be what we started with, and that - what we
2637 # started with should be delta.
2638 diff = nowawareplus - delta
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002639 self.assertIs(diff.tzinfo, tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002640 self.assertEqual(nowaware, diff)
2641 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2642 self.assertEqual(nowawareplus - nowaware, delta)
2643
2644 # Make up a random timezone.
2645 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002646 # Attach it to nowawareplus.
2647 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002648 self.assertIs(nowawareplus.tzinfo, tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002649 # Make sure the difference takes the timezone adjustments into account.
2650 got = nowaware - nowawareplus
2651 # Expected: (nowaware base - nowaware offset) -
2652 # (nowawareplus base - nowawareplus offset) =
2653 # (nowaware base - nowawareplus base) +
2654 # (nowawareplus offset - nowaware offset) =
2655 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002656 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002657 self.assertEqual(got, expected)
2658
2659 # Try max possible difference.
2660 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2661 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2662 tzinfo=FixedOffset(-1439, "max"))
2663 maxdiff = max - min
2664 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2665 timedelta(minutes=2*1439))
2666
2667 def test_tzinfo_now(self):
2668 meth = self.theclass.now
2669 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2670 base = meth()
2671 # Try with and without naming the keyword.
2672 off42 = FixedOffset(42, "42")
2673 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002674 again = meth(tz=off42)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002675 self.assertIs(another.tzinfo, again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002676 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002677 # Bad argument with and w/o naming the keyword.
2678 self.assertRaises(TypeError, meth, 16)
2679 self.assertRaises(TypeError, meth, tzinfo=16)
2680 # Bad keyword name.
2681 self.assertRaises(TypeError, meth, tinfo=off42)
2682 # Too many args.
2683 self.assertRaises(TypeError, meth, off42, off42)
2684
Tim Peters10cadce2003-01-23 19:58:02 +00002685 # We don't know which time zone we're in, and don't have a tzinfo
2686 # class to represent it, so seeing whether a tz argument actually
2687 # does a conversion is tricky.
2688 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2689 utc = FixedOffset(0, "utc", 0)
2690 for dummy in range(3):
2691 now = datetime.now(weirdtz)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002692 self.assertIs(now.tzinfo, weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002693 utcnow = datetime.utcnow().replace(tzinfo=utc)
2694 now2 = utcnow.astimezone(weirdtz)
2695 if abs(now - now2) < timedelta(seconds=30):
2696 break
2697 # Else the code is broken, or more than 30 seconds passed between
2698 # calls; assuming the latter, just try again.
2699 else:
2700 # Three strikes and we're out.
2701 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2702
Tim Peters2a799bf2002-12-16 20:18:38 +00002703 def test_tzinfo_fromtimestamp(self):
2704 import time
2705 meth = self.theclass.fromtimestamp
2706 ts = time.time()
2707 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2708 base = meth(ts)
2709 # Try with and without naming the keyword.
2710 off42 = FixedOffset(42, "42")
2711 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002712 again = meth(ts, tz=off42)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002713 self.assertIs(another.tzinfo, again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002714 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002715 # Bad argument with and w/o naming the keyword.
2716 self.assertRaises(TypeError, meth, ts, 16)
2717 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2718 # Bad keyword name.
2719 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2720 # Too many args.
2721 self.assertRaises(TypeError, meth, ts, off42, off42)
2722 # Too few args.
2723 self.assertRaises(TypeError, meth)
2724
Tim Peters2a44a8d2003-01-23 20:53:10 +00002725 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002726 timestamp = 1000000000
2727 utcdatetime = datetime.utcfromtimestamp(timestamp)
2728 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2729 # But on some flavor of Mac, it's nowhere near that. So we can't have
2730 # any idea here what time that actually is, we can only test that
2731 # relative changes match.
2732 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2733 tz = FixedOffset(utcoffset, "tz", 0)
2734 expected = utcdatetime + utcoffset
2735 got = datetime.fromtimestamp(timestamp, tz)
2736 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002737
Tim Peters2a799bf2002-12-16 20:18:38 +00002738 def test_tzinfo_utcnow(self):
2739 meth = self.theclass.utcnow
2740 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2741 base = meth()
2742 # Try with and without naming the keyword; for whatever reason,
2743 # utcnow() doesn't accept a tzinfo argument.
2744 off42 = FixedOffset(42, "42")
2745 self.assertRaises(TypeError, meth, off42)
2746 self.assertRaises(TypeError, meth, tzinfo=off42)
2747
2748 def test_tzinfo_utcfromtimestamp(self):
2749 import time
2750 meth = self.theclass.utcfromtimestamp
2751 ts = time.time()
2752 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2753 base = meth(ts)
2754 # Try with and without naming the keyword; for whatever reason,
2755 # utcfromtimestamp() doesn't accept a tzinfo argument.
2756 off42 = FixedOffset(42, "42")
2757 self.assertRaises(TypeError, meth, ts, off42)
2758 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2759
2760 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002761 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002762 # DST flag.
2763 class DST(tzinfo):
2764 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002765 if isinstance(dstvalue, int):
2766 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002767 self.dstvalue = dstvalue
2768 def dst(self, dt):
2769 return self.dstvalue
2770
2771 cls = self.theclass
2772 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2773 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2774 t = d.timetuple()
2775 self.assertEqual(1, t.tm_year)
2776 self.assertEqual(1, t.tm_mon)
2777 self.assertEqual(1, t.tm_mday)
2778 self.assertEqual(10, t.tm_hour)
2779 self.assertEqual(20, t.tm_min)
2780 self.assertEqual(30, t.tm_sec)
2781 self.assertEqual(0, t.tm_wday)
2782 self.assertEqual(1, t.tm_yday)
2783 self.assertEqual(flag, t.tm_isdst)
2784
2785 # dst() returns wrong type.
2786 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2787
2788 # dst() at the edge.
2789 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2790 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2791
2792 # dst() out of range.
2793 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2794 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2795
2796 def test_utctimetuple(self):
2797 class DST(tzinfo):
2798 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002799 if isinstance(dstvalue, int):
2800 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002801 self.dstvalue = dstvalue
2802 def dst(self, dt):
2803 return self.dstvalue
2804
2805 cls = self.theclass
2806 # This can't work: DST didn't implement utcoffset.
2807 self.assertRaises(NotImplementedError,
2808 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2809
2810 class UOFS(DST):
2811 def __init__(self, uofs, dofs=None):
2812 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002813 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002814 def utcoffset(self, dt):
2815 return self.uofs
2816
2817 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2818 # in effect for a UTC time.
2819 for dstvalue in -33, 33, 0, None:
2820 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2821 t = d.utctimetuple()
2822 self.assertEqual(d.year, t.tm_year)
2823 self.assertEqual(d.month, t.tm_mon)
2824 self.assertEqual(d.day, t.tm_mday)
2825 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2826 self.assertEqual(13, t.tm_min)
2827 self.assertEqual(d.second, t.tm_sec)
2828 self.assertEqual(d.weekday(), t.tm_wday)
2829 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2830 t.tm_yday)
2831 self.assertEqual(0, t.tm_isdst)
2832
2833 # At the edges, UTC adjustment can normalize into years out-of-range
2834 # for a datetime object. Ensure that a correct timetuple is
2835 # created anyway.
2836 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2837 # That goes back 1 minute less than a full day.
2838 t = tiny.utctimetuple()
2839 self.assertEqual(t.tm_year, MINYEAR-1)
2840 self.assertEqual(t.tm_mon, 12)
2841 self.assertEqual(t.tm_mday, 31)
2842 self.assertEqual(t.tm_hour, 0)
2843 self.assertEqual(t.tm_min, 1)
2844 self.assertEqual(t.tm_sec, 37)
2845 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2846 self.assertEqual(t.tm_isdst, 0)
2847
2848 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2849 # That goes forward 1 minute less than a full day.
2850 t = huge.utctimetuple()
2851 self.assertEqual(t.tm_year, MAXYEAR+1)
2852 self.assertEqual(t.tm_mon, 1)
2853 self.assertEqual(t.tm_mday, 1)
2854 self.assertEqual(t.tm_hour, 23)
2855 self.assertEqual(t.tm_min, 58)
2856 self.assertEqual(t.tm_sec, 37)
2857 self.assertEqual(t.tm_yday, 1)
2858 self.assertEqual(t.tm_isdst, 0)
2859
2860 def test_tzinfo_isoformat(self):
2861 zero = FixedOffset(0, "+00:00")
2862 plus = FixedOffset(220, "+03:40")
2863 minus = FixedOffset(-231, "-03:51")
2864 unknown = FixedOffset(None, "")
2865
2866 cls = self.theclass
2867 datestr = '0001-02-03'
2868 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002869 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002870 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2871 timestr = '04:05:59' + (us and '.987001' or '')
2872 ofsstr = ofs is not None and d.tzname() or ''
2873 tailstr = timestr + ofsstr
2874 iso = d.isoformat()
2875 self.assertEqual(iso, datestr + 'T' + tailstr)
2876 self.assertEqual(iso, d.isoformat('T'))
2877 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2878 self.assertEqual(str(d), datestr + ' ' + tailstr)
2879
Tim Peters12bf3392002-12-24 05:41:27 +00002880 def test_replace(self):
2881 cls = self.theclass
2882 z100 = FixedOffset(100, "+100")
2883 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2884 args = [1, 2, 3, 4, 5, 6, 7, z100]
2885 base = cls(*args)
2886 self.assertEqual(base, base.replace())
2887
2888 i = 0
2889 for name, newval in (("year", 2),
2890 ("month", 3),
2891 ("day", 4),
2892 ("hour", 5),
2893 ("minute", 6),
2894 ("second", 7),
2895 ("microsecond", 8),
2896 ("tzinfo", zm200)):
2897 newargs = args[:]
2898 newargs[i] = newval
2899 expected = cls(*newargs)
2900 got = base.replace(**{name: newval})
2901 self.assertEqual(expected, got)
2902 i += 1
2903
2904 # Ensure we can get rid of a tzinfo.
2905 self.assertEqual(base.tzname(), "+100")
2906 base2 = base.replace(tzinfo=None)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002907 self.assertIsNone(base2.tzinfo)
2908 self.assertIsNone(base2.tzname())
Tim Peters12bf3392002-12-24 05:41:27 +00002909
2910 # Ensure we can add one.
2911 base3 = base2.replace(tzinfo=z100)
2912 self.assertEqual(base, base3)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002913 self.assertIs(base.tzinfo, base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002914
2915 # Out of bounds.
2916 base = cls(2000, 2, 29)
2917 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002918
Tim Peters80475bb2002-12-25 07:40:55 +00002919 def test_more_astimezone(self):
2920 # The inherited test_astimezone covered some trivial and error cases.
2921 fnone = FixedOffset(None, "None")
2922 f44m = FixedOffset(44, "44")
2923 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2924
Tim Peters10cadce2003-01-23 19:58:02 +00002925 dt = self.theclass.now(tz=f44m)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002926 self.assertIs(dt.tzinfo, f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002927 # Replacing with degenerate tzinfo raises an exception.
2928 self.assertRaises(ValueError, dt.astimezone, fnone)
2929 # Ditto with None tz.
2930 self.assertRaises(TypeError, dt.astimezone, None)
2931 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002932 x = dt.astimezone(dt.tzinfo)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002933 self.assertIs(x.tzinfo, f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002934 self.assertEqual(x.date(), dt.date())
2935 self.assertEqual(x.time(), dt.time())
2936
2937 # Replacing with different tzinfo does adjust.
2938 got = dt.astimezone(fm5h)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002939 self.assertIs(got.tzinfo, fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002940 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2941 expected = dt - dt.utcoffset() # in effect, convert to UTC
2942 expected += fm5h.utcoffset(dt) # and from there to local time
2943 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2944 self.assertEqual(got.date(), expected.date())
2945 self.assertEqual(got.time(), expected.time())
2946 self.assertEqual(got.timetz(), expected.timetz())
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002947 self.assertIs(got.tzinfo, expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002948 self.assertEqual(got, expected)
2949
Tim Peters4c0db782002-12-26 05:01:19 +00002950 def test_aware_subtract(self):
2951 cls = self.theclass
2952
Tim Peters60c76e42002-12-27 00:41:11 +00002953 # Ensure that utcoffset() is ignored when the operands have the
2954 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002955 class OperandDependentOffset(tzinfo):
2956 def utcoffset(self, t):
2957 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002958 # d0 and d1 equal after adjustment
2959 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002960 else:
Tim Peters397301e2003-01-02 21:28:08 +00002961 # d2 off in the weeds
2962 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002963
2964 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2965 d0 = base.replace(minute=3)
2966 d1 = base.replace(minute=9)
2967 d2 = base.replace(minute=11)
2968 for x in d0, d1, d2:
2969 for y in d0, d1, d2:
2970 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002971 expected = timedelta(minutes=x.minute - y.minute)
2972 self.assertEqual(got, expected)
2973
2974 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2975 # ignored.
2976 base = cls(8, 9, 10, 11, 12, 13, 14)
2977 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2978 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2979 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2980 for x in d0, d1, d2:
2981 for y in d0, d1, d2:
2982 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002983 if (x is d0 or x is d1) and (y is d0 or y is d1):
2984 expected = timedelta(0)
2985 elif x is y is d2:
2986 expected = timedelta(0)
2987 elif x is d2:
2988 expected = timedelta(minutes=(11-59)-0)
2989 else:
2990 assert y is d2
2991 expected = timedelta(minutes=0-(11-59))
2992 self.assertEqual(got, expected)
2993
Tim Peters60c76e42002-12-27 00:41:11 +00002994 def test_mixed_compare(self):
2995 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002996 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002997 self.assertEqual(t1, t2)
2998 t2 = t2.replace(tzinfo=None)
2999 self.assertEqual(t1, t2)
3000 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3001 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003002 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3003 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003004
Tim Peters0bf60bd2003-01-08 20:40:01 +00003005 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003006 class Varies(tzinfo):
3007 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003008 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003009 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003010 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003011 return self.offset
3012
3013 v = Varies()
3014 t1 = t2.replace(tzinfo=v)
3015 t2 = t2.replace(tzinfo=v)
3016 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3017 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3018 self.assertEqual(t1, t2)
3019
3020 # But if they're not identical, it isn't ignored.
3021 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003022 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003023
Tim Petersa98924a2003-05-17 05:55:19 +00003024 def test_subclass_datetimetz(self):
3025
3026 class C(self.theclass):
3027 theAnswer = 42
3028
3029 def __new__(cls, *args, **kws):
3030 temp = kws.copy()
3031 extra = temp.pop('extra')
3032 result = self.theclass.__new__(cls, *args, **temp)
3033 result.extra = extra
3034 return result
3035
3036 def newmeth(self, start):
3037 return start + self.hour + self.year
3038
3039 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3040
3041 dt1 = self.theclass(*args)
3042 dt2 = C(*args, **{'extra': 7})
3043
3044 self.assertEqual(dt2.__class__, C)
3045 self.assertEqual(dt2.theAnswer, 42)
3046 self.assertEqual(dt2.extra, 7)
3047 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3048 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3049
Tim Peters621818b2002-12-29 23:44:49 +00003050# Pain to set up DST-aware tzinfo classes.
3051
3052def first_sunday_on_or_after(dt):
3053 days_to_go = 6 - dt.weekday()
3054 if days_to_go:
3055 dt += timedelta(days_to_go)
3056 return dt
3057
3058ZERO = timedelta(0)
3059HOUR = timedelta(hours=1)
3060DAY = timedelta(days=1)
3061# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3062DSTSTART = datetime(1, 4, 1, 2)
3063# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003064# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3065# being standard time on that day, there is no spelling in local time of
3066# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3067DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003068
3069class USTimeZone(tzinfo):
3070
3071 def __init__(self, hours, reprname, stdname, dstname):
3072 self.stdoffset = timedelta(hours=hours)
3073 self.reprname = reprname
3074 self.stdname = stdname
3075 self.dstname = dstname
3076
3077 def __repr__(self):
3078 return self.reprname
3079
3080 def tzname(self, dt):
3081 if self.dst(dt):
3082 return self.dstname
3083 else:
3084 return self.stdname
3085
3086 def utcoffset(self, dt):
3087 return self.stdoffset + self.dst(dt)
3088
3089 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003090 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003091 # An exception instead may be sensible here, in one or more of
3092 # the cases.
3093 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003094 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003095
3096 # Find first Sunday in April.
3097 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3098 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3099
3100 # Find last Sunday in October.
3101 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3102 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3103
Tim Peters621818b2002-12-29 23:44:49 +00003104 # Can't compare naive to aware objects, so strip the timezone from
3105 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003106 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003107 return HOUR
3108 else:
3109 return ZERO
3110
Tim Peters521fc152002-12-31 17:36:56 +00003111Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3112Central = USTimeZone(-6, "Central", "CST", "CDT")
3113Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3114Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003115utc_real = FixedOffset(0, "UTC", 0)
3116# For better test coverage, we want another flavor of UTC that's west of
3117# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003118utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003119
3120class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003121 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003122 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003123 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003124
Tim Peters0bf60bd2003-01-08 20:40:01 +00003125 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003126
Tim Peters521fc152002-12-31 17:36:56 +00003127 # Check a time that's inside DST.
3128 def checkinside(self, dt, tz, utc, dston, dstoff):
3129 self.assertEqual(dt.dst(), HOUR)
3130
3131 # Conversion to our own timezone is always an identity.
3132 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003133
3134 asutc = dt.astimezone(utc)
3135 there_and_back = asutc.astimezone(tz)
3136
3137 # Conversion to UTC and back isn't always an identity here,
3138 # because there are redundant spellings (in local time) of
3139 # UTC time when DST begins: the clock jumps from 1:59:59
3140 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3141 # make sense then. The classes above treat 2:MM:SS as
3142 # daylight time then (it's "after 2am"), really an alias
3143 # for 1:MM:SS standard time. The latter form is what
3144 # conversion back from UTC produces.
3145 if dt.date() == dston.date() and dt.hour == 2:
3146 # We're in the redundant hour, and coming back from
3147 # UTC gives the 1:MM:SS standard-time spelling.
3148 self.assertEqual(there_and_back + HOUR, dt)
3149 # Although during was considered to be in daylight
3150 # time, there_and_back is not.
3151 self.assertEqual(there_and_back.dst(), ZERO)
3152 # They're the same times in UTC.
3153 self.assertEqual(there_and_back.astimezone(utc),
3154 dt.astimezone(utc))
3155 else:
3156 # We're not in the redundant hour.
3157 self.assertEqual(dt, there_and_back)
3158
Tim Peters327098a2003-01-20 22:54:38 +00003159 # Because we have a redundant spelling when DST begins, there is
Ezio Melottic2077b02011-03-16 12:34:31 +02003160 # (unfortunately) an hour when DST ends that can't be spelled at all in
Tim Peters327098a2003-01-20 22:54:38 +00003161 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3162 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3163 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3164 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3165 # expressed in local time. Nevertheless, we want conversion back
3166 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003167 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003168 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003169 if dt.date() == dstoff.date() and dt.hour == 0:
3170 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003171 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003172 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3173 nexthour_utc += HOUR
3174 nexthour_tz = nexthour_utc.astimezone(tz)
3175 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003176 else:
Tim Peters327098a2003-01-20 22:54:38 +00003177 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003178
3179 # Check a time that's outside DST.
3180 def checkoutside(self, dt, tz, utc):
3181 self.assertEqual(dt.dst(), ZERO)
3182
3183 # Conversion to our own timezone is always an identity.
3184 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003185
3186 # Converting to UTC and back is an identity too.
3187 asutc = dt.astimezone(utc)
3188 there_and_back = asutc.astimezone(tz)
3189 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003190
Tim Peters1024bf82002-12-30 17:09:40 +00003191 def convert_between_tz_and_utc(self, tz, utc):
3192 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003193 # Because 1:MM on the day DST ends is taken as being standard time,
3194 # there is no spelling in tz for the last hour of daylight time.
3195 # For purposes of the test, the last hour of DST is 0:MM, which is
3196 # taken as being daylight time (and 1:MM is taken as being standard
3197 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003198 dstoff = self.dstoff.replace(tzinfo=tz)
3199 for delta in (timedelta(weeks=13),
3200 DAY,
3201 HOUR,
3202 timedelta(minutes=1),
3203 timedelta(microseconds=1)):
3204
Tim Peters521fc152002-12-31 17:36:56 +00003205 self.checkinside(dston, tz, utc, dston, dstoff)
3206 for during in dston + delta, dstoff - delta:
3207 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003208
Tim Peters521fc152002-12-31 17:36:56 +00003209 self.checkoutside(dstoff, tz, utc)
3210 for outside in dston - delta, dstoff + delta:
3211 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003212
Tim Peters621818b2002-12-29 23:44:49 +00003213 def test_easy(self):
3214 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003215 self.convert_between_tz_and_utc(Eastern, utc_real)
3216 self.convert_between_tz_and_utc(Pacific, utc_real)
3217 self.convert_between_tz_and_utc(Eastern, utc_fake)
3218 self.convert_between_tz_and_utc(Pacific, utc_fake)
3219 # The next is really dancing near the edge. It works because
3220 # Pacific and Eastern are far enough apart that their "problem
3221 # hours" don't overlap.
3222 self.convert_between_tz_and_utc(Eastern, Pacific)
3223 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003224 # OTOH, these fail! Don't enable them. The difficulty is that
3225 # the edge case tests assume that every hour is representable in
3226 # the "utc" class. This is always true for a fixed-offset tzinfo
3227 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3228 # For these adjacent DST-aware time zones, the range of time offsets
3229 # tested ends up creating hours in the one that aren't representable
3230 # in the other. For the same reason, we would see failures in the
3231 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3232 # offset deltas in convert_between_tz_and_utc().
3233 #
3234 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3235 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003236
Tim Petersf3615152003-01-01 21:51:37 +00003237 def test_tricky(self):
3238 # 22:00 on day before daylight starts.
3239 fourback = self.dston - timedelta(hours=4)
3240 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003241 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003242 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3243 # 2", we should get the 3 spelling.
3244 # If we plug 22:00 the day before into Eastern, it "looks like std
3245 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3246 # to 22:00 lands on 2:00, which makes no sense in local time (the
3247 # local clock jumps from 1 to 3). The point here is to make sure we
3248 # get the 3 spelling.
3249 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003250 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003251 self.assertEqual(expected, got)
3252
3253 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3254 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003255 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003256 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3257 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3258 # spelling.
3259 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003260 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003261 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003262
Tim Petersadf64202003-01-04 06:03:15 +00003263 # Now on the day DST ends, we want "repeat an hour" behavior.
3264 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3265 # EST 23:MM 0:MM 1:MM 2:MM
3266 # EDT 0:MM 1:MM 2:MM 3:MM
3267 # wall 0:MM 1:MM 1:MM 2:MM against these
3268 for utc in utc_real, utc_fake:
3269 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003270 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003271 # Convert that to UTC.
3272 first_std_hour -= tz.utcoffset(None)
3273 # Adjust for possibly fake UTC.
3274 asutc = first_std_hour + utc.utcoffset(None)
3275 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3276 # tz=Eastern.
3277 asutcbase = asutc.replace(tzinfo=utc)
3278 for tzhour in (0, 1, 1, 2):
3279 expectedbase = self.dstoff.replace(hour=tzhour)
3280 for minute in 0, 30, 59:
3281 expected = expectedbase.replace(minute=minute)
3282 asutc = asutcbase.replace(minute=minute)
3283 astz = asutc.astimezone(tz)
3284 self.assertEqual(astz.replace(tzinfo=None), expected)
3285 asutcbase += HOUR
3286
3287
Tim Peters710fb152003-01-02 19:35:54 +00003288 def test_bogus_dst(self):
3289 class ok(tzinfo):
3290 def utcoffset(self, dt): return HOUR
3291 def dst(self, dt): return HOUR
3292
3293 now = self.theclass.now().replace(tzinfo=utc_real)
3294 # Doesn't blow up.
3295 now.astimezone(ok())
3296
3297 # Does blow up.
3298 class notok(ok):
3299 def dst(self, dt): return None
3300 self.assertRaises(ValueError, now.astimezone, notok())
3301
Tim Peters52dcce22003-01-23 16:36:11 +00003302 def test_fromutc(self):
3303 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3304 now = datetime.utcnow().replace(tzinfo=utc_real)
3305 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3306 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3307 enow = Eastern.fromutc(now) # doesn't blow up
3308 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3309 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3310 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3311
3312 # Always converts UTC to standard time.
3313 class FauxUSTimeZone(USTimeZone):
3314 def fromutc(self, dt):
3315 return dt + self.stdoffset
3316 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3317
3318 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3319 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3320 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3321
3322 # Check around DST start.
3323 start = self.dston.replace(hour=4, tzinfo=Eastern)
3324 fstart = start.replace(tzinfo=FEastern)
3325 for wall in 23, 0, 1, 3, 4, 5:
3326 expected = start.replace(hour=wall)
3327 if wall == 23:
3328 expected -= timedelta(days=1)
3329 got = Eastern.fromutc(start)
3330 self.assertEqual(expected, got)
3331
3332 expected = fstart + FEastern.stdoffset
3333 got = FEastern.fromutc(fstart)
3334 self.assertEqual(expected, got)
3335
3336 # Ensure astimezone() calls fromutc() too.
3337 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3338 self.assertEqual(expected, got)
3339
3340 start += HOUR
3341 fstart += HOUR
3342
3343 # Check around DST end.
3344 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3345 fstart = start.replace(tzinfo=FEastern)
3346 for wall in 0, 1, 1, 2, 3, 4:
3347 expected = start.replace(hour=wall)
3348 got = Eastern.fromutc(start)
3349 self.assertEqual(expected, got)
3350
3351 expected = fstart + FEastern.stdoffset
3352 got = FEastern.fromutc(fstart)
3353 self.assertEqual(expected, got)
3354
3355 # Ensure astimezone() calls fromutc() too.
3356 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3357 self.assertEqual(expected, got)
3358
3359 start += HOUR
3360 fstart += HOUR
3361
Tim Peters710fb152003-01-02 19:35:54 +00003362
Tim Peters528ca532004-09-16 01:30:50 +00003363#############################################################################
3364# oddballs
3365
3366class Oddballs(unittest.TestCase):
3367
3368 def test_bug_1028306(self):
3369 # Trying to compare a date to a datetime should act like a mixed-
3370 # type comparison, despite that datetime is a subclass of date.
3371 as_date = date.today()
3372 as_datetime = datetime.combine(as_date, time())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003373 self.assertTrue(as_date != as_datetime)
3374 self.assertTrue(as_datetime != as_date)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02003375 self.assertFalse(as_date == as_datetime)
3376 self.assertFalse(as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003377 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3378 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3379 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3380 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3381 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3382 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3383 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3384 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3385
Martin Panter65076572016-09-07 12:03:06 +00003386 # Nevertheless, comparison should work with the base-class (date)
Tim Peters528ca532004-09-16 01:30:50 +00003387 # projection if use of a date method is forced.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003388 self.assertTrue(as_date.__eq__(as_datetime))
Tim Peters528ca532004-09-16 01:30:50 +00003389 different_day = (as_date.day + 1) % 20 + 1
Serhiy Storchaka1438b982013-11-17 13:03:21 +02003390 self.assertFalse(as_date.__eq__(as_datetime.replace(day=different_day)))
Tim Peters528ca532004-09-16 01:30:50 +00003391
3392 # And date should compare with other subclasses of date. If a
3393 # subclass wants to stop this, it's up to the subclass to do so.
3394 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3395 self.assertEqual(as_date, date_sc)
3396 self.assertEqual(date_sc, as_date)
3397
3398 # Ditto for datetimes.
3399 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3400 as_date.day, 0, 0, 0)
3401 self.assertEqual(as_datetime, datetime_sc)
3402 self.assertEqual(datetime_sc, as_datetime)
3403
Tim Peters2a799bf2002-12-16 20:18:38 +00003404def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003405 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003406
3407if __name__ == "__main__":
3408 test_main()