blob: 7caa40818ffb40d0dec847dbf07ea3910711011b [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
Tim Peters2a799bf2002-12-16 20:18:38 +0000495#############################################################################
496# date tests
497
498class TestDateOnly(unittest.TestCase):
499 # Tests here won't pass if also run on datetime objects, so don't
500 # subclass this to test datetimes too.
501
502 def test_delta_non_days_ignored(self):
503 dt = date(2000, 1, 2)
504 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
505 microseconds=5)
506 days = timedelta(delta.days)
507 self.assertEqual(days, timedelta(1))
508
509 dt2 = dt + delta
510 self.assertEqual(dt2, dt + days)
511
512 dt2 = delta + dt
513 self.assertEqual(dt2, dt + days)
514
515 dt2 = dt - delta
516 self.assertEqual(dt2, dt - days)
517
518 delta = -delta
519 days = timedelta(delta.days)
520 self.assertEqual(days, timedelta(-2))
521
522 dt2 = dt + delta
523 self.assertEqual(dt2, dt + days)
524
525 dt2 = delta + dt
526 self.assertEqual(dt2, dt + days)
527
528 dt2 = dt - delta
529 self.assertEqual(dt2, dt - days)
530
Tim Peters604c0132004-06-07 23:04:33 +0000531class SubclassDate(date):
532 sub_var = 1
533
Collin Winterc2898c52007-04-25 17:29:52 +0000534class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000535 # Tests here should pass for both dates and datetimes, except for a
536 # few tests that TestDateTime overrides.
537
538 theclass = date
539
540 def test_basic_attributes(self):
541 dt = self.theclass(2002, 3, 1)
542 self.assertEqual(dt.year, 2002)
543 self.assertEqual(dt.month, 3)
544 self.assertEqual(dt.day, 1)
545
546 def test_roundtrip(self):
547 for dt in (self.theclass(1, 2, 3),
548 self.theclass.today()):
549 # Verify dt -> string -> date identity.
550 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000551 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000552 s = s[9:]
553 dt2 = eval(s)
554 self.assertEqual(dt, dt2)
555
556 # Verify identity via reconstructing from pieces.
557 dt2 = self.theclass(dt.year, dt.month, dt.day)
558 self.assertEqual(dt, dt2)
559
560 def test_ordinal_conversions(self):
561 # Check some fixed values.
562 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
563 (1, 12, 31, 365),
564 (2, 1, 1, 366),
565 # first example from "Calendrical Calculations"
566 (1945, 11, 12, 710347)]:
567 d = self.theclass(y, m, d)
568 self.assertEqual(n, d.toordinal())
569 fromord = self.theclass.fromordinal(n)
570 self.assertEqual(d, fromord)
571 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000572 # if we're checking something fancier than a date, verify
573 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000574 self.assertEqual(fromord.hour, 0)
575 self.assertEqual(fromord.minute, 0)
576 self.assertEqual(fromord.second, 0)
577 self.assertEqual(fromord.microsecond, 0)
578
Tim Peters0bf60bd2003-01-08 20:40:01 +0000579 # Check first and last days of year spottily across the whole
580 # range of years supported.
581 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000582 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
583 d = self.theclass(year, 1, 1)
584 n = d.toordinal()
585 d2 = self.theclass.fromordinal(n)
586 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000587 # Verify that moving back a day gets to the end of year-1.
588 if year > 1:
589 d = self.theclass.fromordinal(n-1)
590 d2 = self.theclass(year-1, 12, 31)
591 self.assertEqual(d, d2)
592 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000593
594 # Test every day in a leap-year and a non-leap year.
595 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
596 for year, isleap in (2000, True), (2002, False):
597 n = self.theclass(year, 1, 1).toordinal()
598 for month, maxday in zip(range(1, 13), dim):
599 if month == 2 and isleap:
600 maxday += 1
601 for day in range(1, maxday+1):
602 d = self.theclass(year, month, day)
603 self.assertEqual(d.toordinal(), n)
604 self.assertEqual(d, self.theclass.fromordinal(n))
605 n += 1
606
607 def test_extreme_ordinals(self):
608 a = self.theclass.min
609 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
610 aord = a.toordinal()
611 b = a.fromordinal(aord)
612 self.assertEqual(a, b)
613
614 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
615
616 b = a + timedelta(days=1)
617 self.assertEqual(b.toordinal(), aord + 1)
618 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
619
620 a = self.theclass.max
621 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
622 aord = a.toordinal()
623 b = a.fromordinal(aord)
624 self.assertEqual(a, b)
625
626 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
627
628 b = a - timedelta(days=1)
629 self.assertEqual(b.toordinal(), aord - 1)
630 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
631
632 def test_bad_constructor_arguments(self):
633 # bad years
634 self.theclass(MINYEAR, 1, 1) # no exception
635 self.theclass(MAXYEAR, 1, 1) # no exception
636 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
637 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
638 # bad months
639 self.theclass(2000, 1, 1) # no exception
640 self.theclass(2000, 12, 1) # no exception
641 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
642 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
643 # bad days
644 self.theclass(2000, 2, 29) # no exception
645 self.theclass(2004, 2, 29) # no exception
646 self.theclass(2400, 2, 29) # no exception
647 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
648 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
649 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
650 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
651 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
652 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
653
654 def test_hash_equality(self):
655 d = self.theclass(2000, 12, 31)
656 # same thing
657 e = self.theclass(2000, 12, 31)
658 self.assertEqual(d, e)
659 self.assertEqual(hash(d), hash(e))
660
661 dic = {d: 1}
662 dic[e] = 2
663 self.assertEqual(len(dic), 1)
664 self.assertEqual(dic[d], 2)
665 self.assertEqual(dic[e], 2)
666
667 d = self.theclass(2001, 1, 1)
668 # same thing
669 e = self.theclass(2001, 1, 1)
670 self.assertEqual(d, e)
671 self.assertEqual(hash(d), hash(e))
672
673 dic = {d: 1}
674 dic[e] = 2
675 self.assertEqual(len(dic), 1)
676 self.assertEqual(dic[d], 2)
677 self.assertEqual(dic[e], 2)
678
679 def test_computations(self):
680 a = self.theclass(2002, 1, 31)
681 b = self.theclass(1956, 1, 31)
682
683 diff = a-b
684 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
685 self.assertEqual(diff.seconds, 0)
686 self.assertEqual(diff.microseconds, 0)
687
688 day = timedelta(1)
689 week = timedelta(7)
690 a = self.theclass(2002, 3, 2)
691 self.assertEqual(a + day, self.theclass(2002, 3, 3))
692 self.assertEqual(day + a, self.theclass(2002, 3, 3))
693 self.assertEqual(a - day, self.theclass(2002, 3, 1))
694 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
695 self.assertEqual(a + week, self.theclass(2002, 3, 9))
696 self.assertEqual(a - week, self.theclass(2002, 2, 23))
697 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
698 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
699 self.assertEqual((a + week) - a, week)
700 self.assertEqual((a + day) - a, day)
701 self.assertEqual((a - week) - a, -week)
702 self.assertEqual((a - day) - a, -day)
703 self.assertEqual(a - (a + week), -week)
704 self.assertEqual(a - (a + day), -day)
705 self.assertEqual(a - (a - week), week)
706 self.assertEqual(a - (a - day), day)
707
708 # Add/sub ints, longs, floats should be illegal
709 for i in 1, 1L, 1.0:
710 self.assertRaises(TypeError, lambda: a+i)
711 self.assertRaises(TypeError, lambda: a-i)
712 self.assertRaises(TypeError, lambda: i+a)
713 self.assertRaises(TypeError, lambda: i-a)
714
715 # delta - date is senseless.
716 self.assertRaises(TypeError, lambda: day - a)
717 # mixing date and (delta or date) via * or // is senseless
718 self.assertRaises(TypeError, lambda: day * a)
719 self.assertRaises(TypeError, lambda: a * day)
720 self.assertRaises(TypeError, lambda: day // a)
721 self.assertRaises(TypeError, lambda: a // day)
722 self.assertRaises(TypeError, lambda: a * a)
723 self.assertRaises(TypeError, lambda: a // a)
724 # date + date is senseless
725 self.assertRaises(TypeError, lambda: a + a)
726
727 def test_overflow(self):
728 tiny = self.theclass.resolution
729
Alexander Belopolsky9292ee02010-05-27 20:55:27 +0000730 for delta in [tiny, timedelta(1), timedelta(2)]:
731 dt = self.theclass.min + delta
732 dt -= delta # no problem
733 self.assertRaises(OverflowError, dt.__sub__, delta)
734 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000735
Alexander Belopolsky9292ee02010-05-27 20:55:27 +0000736 dt = self.theclass.max - delta
737 dt += delta # no problem
738 self.assertRaises(OverflowError, dt.__add__, delta)
739 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000740
741 def test_fromtimestamp(self):
742 import time
743
744 # Try an arbitrary fixed value.
745 year, month, day = 1999, 9, 19
746 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
747 d = self.theclass.fromtimestamp(ts)
748 self.assertEqual(d.year, year)
749 self.assertEqual(d.month, month)
750 self.assertEqual(d.day, day)
751
Tim Peters1b6f7a92004-06-20 02:50:16 +0000752 def test_insane_fromtimestamp(self):
753 # It's possible that some platform maps time_t to double,
754 # and that this test will fail there. This test should
755 # exempt such platforms (provided they return reasonable
756 # results!).
757 for insane in -1e200, 1e200:
758 self.assertRaises(ValueError, self.theclass.fromtimestamp,
759 insane)
760
Tim Peters2a799bf2002-12-16 20:18:38 +0000761 def test_today(self):
762 import time
763
764 # We claim that today() is like fromtimestamp(time.time()), so
765 # prove it.
766 for dummy in range(3):
767 today = self.theclass.today()
768 ts = time.time()
769 todayagain = self.theclass.fromtimestamp(ts)
770 if today == todayagain:
771 break
772 # There are several legit reasons that could fail:
773 # 1. It recently became midnight, between the today() and the
774 # time() calls.
775 # 2. The platform time() has such fine resolution that we'll
776 # never get the same value twice.
777 # 3. The platform time() has poor resolution, and we just
778 # happened to call today() right before a resolution quantum
779 # boundary.
780 # 4. The system clock got fiddled between calls.
781 # In any case, wait a little while and try again.
782 time.sleep(0.1)
783
784 # It worked or it didn't. If it didn't, assume it's reason #2, and
785 # let the test pass if they're within half a second of each other.
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200786 if today != todayagain:
787 self.assertAlmostEqual(todayagain, today,
788 delta=timedelta(seconds=0.5))
Tim Peters2a799bf2002-12-16 20:18:38 +0000789
790 def test_weekday(self):
791 for i in range(7):
792 # March 4, 2002 is a Monday
793 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
794 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
795 # January 2, 1956 is a Monday
796 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
797 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
798
799 def test_isocalendar(self):
800 # Check examples from
801 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
802 for i in range(7):
803 d = self.theclass(2003, 12, 22+i)
804 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
805 d = self.theclass(2003, 12, 29) + timedelta(i)
806 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
807 d = self.theclass(2004, 1, 5+i)
808 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
809 d = self.theclass(2009, 12, 21+i)
810 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
811 d = self.theclass(2009, 12, 28) + timedelta(i)
812 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
813 d = self.theclass(2010, 1, 4+i)
814 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
815
816 def test_iso_long_years(self):
817 # Calculate long ISO years and compare to table from
818 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
819 ISO_LONG_YEARS_TABLE = """
820 4 32 60 88
821 9 37 65 93
822 15 43 71 99
823 20 48 76
824 26 54 82
825
826 105 133 161 189
827 111 139 167 195
828 116 144 172
829 122 150 178
830 128 156 184
831
832 201 229 257 285
833 207 235 263 291
834 212 240 268 296
835 218 246 274
836 224 252 280
837
838 303 331 359 387
839 308 336 364 392
840 314 342 370 398
841 320 348 376
842 325 353 381
843 """
844 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
845 iso_long_years.sort()
846 L = []
847 for i in range(400):
848 d = self.theclass(2000+i, 12, 31)
849 d1 = self.theclass(1600+i, 12, 31)
850 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
851 if d.isocalendar()[1] == 53:
852 L.append(i)
853 self.assertEqual(L, iso_long_years)
854
855 def test_isoformat(self):
856 t = self.theclass(2, 3, 2)
857 self.assertEqual(t.isoformat(), "0002-03-02")
858
859 def test_ctime(self):
860 t = self.theclass(2002, 3, 2)
861 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
862
863 def test_strftime(self):
864 t = self.theclass(2005, 3, 2)
865 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000866 self.assertEqual(t.strftime(""), "") # SF bug #761337
Georg Brandl4ddfcd32006-09-30 11:17:34 +0000867 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000868
869 self.assertRaises(TypeError, t.strftime) # needs an arg
870 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
871 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
872
Gregory P. Smith137d8242008-06-02 04:05:52 +0000873 # test that unicode input is allowed (issue 2782)
874 self.assertEqual(t.strftime(u"%m"), "03")
875
Tim Peters2a799bf2002-12-16 20:18:38 +0000876 # A naive object replaces %z and %Z w/ empty strings.
877 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
878
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000879 #make sure that invalid format specifiers are handled correctly
Kristján Valur Jónsson8adc0b52009-01-15 09:09:13 +0000880 #self.assertRaises(ValueError, t.strftime, "%e")
881 #self.assertRaises(ValueError, t.strftime, "%")
882 #self.assertRaises(ValueError, t.strftime, "%#")
883
884 #oh well, some systems just ignore those invalid ones.
885 #at least, excercise them to make sure that no crashes
886 #are generated
887 for f in ["%e", "%", "%#"]:
888 try:
889 t.strftime(f)
890 except ValueError:
891 pass
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000892
893 #check that this standard extension works
894 t.strftime("%f")
895
Gregory P. Smith137d8242008-06-02 04:05:52 +0000896
Eric Smitha9f7d622008-02-17 19:46:49 +0000897 def test_format(self):
898 dt = self.theclass(2007, 9, 10)
899 self.assertEqual(dt.__format__(''), str(dt))
900
901 # check that a derived class's __str__() gets called
902 class A(self.theclass):
903 def __str__(self):
904 return 'A'
905 a = A(2007, 9, 10)
906 self.assertEqual(a.__format__(''), 'A')
907
908 # check that a derived class's strftime gets called
909 class B(self.theclass):
910 def strftime(self, format_spec):
911 return 'B'
912 b = B(2007, 9, 10)
913 self.assertEqual(b.__format__(''), str(dt))
914
915 for fmt in ["m:%m d:%d y:%y",
916 "m:%m d:%d y:%y H:%H M:%M S:%S",
917 "%z %Z",
918 ]:
919 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
920 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
921 self.assertEqual(b.__format__(fmt), 'B')
922
Tim Peters2a799bf2002-12-16 20:18:38 +0000923 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000924 self.assertIsInstance(self.theclass.min, self.theclass)
925 self.assertIsInstance(self.theclass.max, self.theclass)
926 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000927 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000928
929 def test_extreme_timedelta(self):
930 big = self.theclass.max - self.theclass.min
931 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
932 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
933 # n == 315537897599999999 ~= 2**58.13
934 justasbig = timedelta(0, 0, n)
935 self.assertEqual(big, justasbig)
936 self.assertEqual(self.theclass.min + big, self.theclass.max)
937 self.assertEqual(self.theclass.max - big, self.theclass.min)
938
939 def test_timetuple(self):
940 for i in range(7):
941 # January 2, 1956 is a Monday (0)
942 d = self.theclass(1956, 1, 2+i)
943 t = d.timetuple()
944 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
945 # February 1, 1956 is a Wednesday (2)
946 d = self.theclass(1956, 2, 1+i)
947 t = d.timetuple()
948 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
949 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
950 # of the year.
951 d = self.theclass(1956, 3, 1+i)
952 t = d.timetuple()
953 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
954 self.assertEqual(t.tm_year, 1956)
955 self.assertEqual(t.tm_mon, 3)
956 self.assertEqual(t.tm_mday, 1+i)
957 self.assertEqual(t.tm_hour, 0)
958 self.assertEqual(t.tm_min, 0)
959 self.assertEqual(t.tm_sec, 0)
960 self.assertEqual(t.tm_wday, (3+i)%7)
961 self.assertEqual(t.tm_yday, 61+i)
962 self.assertEqual(t.tm_isdst, -1)
963
964 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000965 args = 6, 7, 23
966 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000967 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000968 green = pickler.dumps(orig, proto)
969 derived = unpickler.loads(green)
970 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000971
972 def test_compare(self):
973 t1 = self.theclass(2, 3, 4)
974 t2 = self.theclass(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000975 self.assertTrue(t1 == t2)
976 self.assertTrue(t1 <= t2)
977 self.assertTrue(t1 >= t2)
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200978 self.assertFalse(t1 != t2)
979 self.assertFalse(t1 < t2)
980 self.assertFalse(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000981 self.assertEqual(cmp(t1, t2), 0)
982 self.assertEqual(cmp(t2, t1), 0)
983
984 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
985 t2 = self.theclass(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000986 self.assertTrue(t1 < t2)
987 self.assertTrue(t2 > t1)
988 self.assertTrue(t1 <= t2)
989 self.assertTrue(t2 >= t1)
990 self.assertTrue(t1 != t2)
991 self.assertTrue(t2 != t1)
Serhiy Storchaka1438b982013-11-17 13:03:21 +0200992 self.assertFalse(t1 == t2)
993 self.assertFalse(t2 == t1)
994 self.assertFalse(t1 > t2)
995 self.assertFalse(t2 < t1)
996 self.assertFalse(t1 >= t2)
997 self.assertFalse(t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000998 self.assertEqual(cmp(t1, t2), -1)
999 self.assertEqual(cmp(t2, t1), 1)
1000
Tim Peters68124bb2003-02-08 03:46:31 +00001001 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001002 self.assertEqual(t1 == badarg, False)
1003 self.assertEqual(t1 != badarg, True)
1004 self.assertEqual(badarg == t1, False)
1005 self.assertEqual(badarg != t1, True)
1006
Tim Peters2a799bf2002-12-16 20:18:38 +00001007 self.assertRaises(TypeError, lambda: t1 < badarg)
1008 self.assertRaises(TypeError, lambda: t1 > badarg)
1009 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001010 self.assertRaises(TypeError, lambda: badarg <= t1)
1011 self.assertRaises(TypeError, lambda: badarg < t1)
1012 self.assertRaises(TypeError, lambda: badarg > t1)
1013 self.assertRaises(TypeError, lambda: badarg >= t1)
1014
Tim Peters8d81a012003-01-24 22:36:34 +00001015 def test_mixed_compare(self):
1016 our = self.theclass(2000, 4, 5)
1017 self.assertRaises(TypeError, cmp, our, 1)
1018 self.assertRaises(TypeError, cmp, 1, our)
1019
1020 class AnotherDateTimeClass(object):
1021 def __cmp__(self, other):
1022 # Return "equal" so calling this can't be confused with
1023 # compare-by-address (which never says "equal" for distinct
1024 # objects).
1025 return 0
Nick Coghlan48361f52008-08-11 15:45:58 +00001026 __hash__ = None # Silence Py3k warning
Tim Peters8d81a012003-01-24 22:36:34 +00001027
1028 # This still errors, because date and datetime comparison raise
1029 # TypeError instead of NotImplemented when they don't know what to
1030 # do, in order to stop comparison from falling back to the default
1031 # compare-by-address.
1032 their = AnotherDateTimeClass()
1033 self.assertRaises(TypeError, cmp, our, their)
1034 # Oops: The next stab raises TypeError in the C implementation,
1035 # but not in the Python implementation of datetime. The difference
1036 # is due to that the Python implementation defines __cmp__ but
1037 # the C implementation defines tp_richcompare. This is more pain
1038 # to fix than it's worth, so commenting out the test.
1039 # self.assertEqual(cmp(their, our), 0)
1040
1041 # But date and datetime comparison return NotImplemented instead if the
1042 # other object has a timetuple attr. This gives the other object a
1043 # chance to do the comparison.
1044 class Comparable(AnotherDateTimeClass):
1045 def timetuple(self):
1046 return ()
1047
1048 their = Comparable()
1049 self.assertEqual(cmp(our, their), 0)
1050 self.assertEqual(cmp(their, our), 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001051 self.assertTrue(our == their)
1052 self.assertTrue(their == our)
Tim Peters8d81a012003-01-24 22:36:34 +00001053
Tim Peters2a799bf2002-12-16 20:18:38 +00001054 def test_bool(self):
1055 # All dates are considered true.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001056 self.assertTrue(self.theclass.min)
1057 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001058
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001059 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001060 # For nasty technical reasons, we can't handle years before 1900.
1061 cls = self.theclass
1062 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1063 for y in 1, 49, 51, 99, 100, 1000, 1899:
1064 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001065
1066 def test_replace(self):
1067 cls = self.theclass
1068 args = [1, 2, 3]
1069 base = cls(*args)
1070 self.assertEqual(base, base.replace())
1071
1072 i = 0
1073 for name, newval in (("year", 2),
1074 ("month", 3),
1075 ("day", 4)):
1076 newargs = args[:]
1077 newargs[i] = newval
1078 expected = cls(*newargs)
1079 got = base.replace(**{name: newval})
1080 self.assertEqual(expected, got)
1081 i += 1
1082
1083 # Out of bounds.
1084 base = cls(2000, 2, 29)
1085 self.assertRaises(ValueError, base.replace, year=2001)
1086
Tim Petersa98924a2003-05-17 05:55:19 +00001087 def test_subclass_date(self):
1088
1089 class C(self.theclass):
1090 theAnswer = 42
1091
1092 def __new__(cls, *args, **kws):
1093 temp = kws.copy()
1094 extra = temp.pop('extra')
1095 result = self.theclass.__new__(cls, *args, **temp)
1096 result.extra = extra
1097 return result
1098
1099 def newmeth(self, start):
1100 return start + self.year + self.month
1101
1102 args = 2003, 4, 14
1103
1104 dt1 = self.theclass(*args)
1105 dt2 = C(*args, **{'extra': 7})
1106
1107 self.assertEqual(dt2.__class__, C)
1108 self.assertEqual(dt2.theAnswer, 42)
1109 self.assertEqual(dt2.extra, 7)
1110 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1111 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1112
Tim Peters604c0132004-06-07 23:04:33 +00001113 def test_pickling_subclass_date(self):
1114
1115 args = 6, 7, 23
1116 orig = SubclassDate(*args)
1117 for pickler, unpickler, proto in pickle_choices:
1118 green = pickler.dumps(orig, proto)
1119 derived = unpickler.loads(green)
1120 self.assertEqual(orig, derived)
1121
Tim Peters3f606292004-03-21 23:38:41 +00001122 def test_backdoor_resistance(self):
1123 # For fast unpickling, the constructor accepts a pickle string.
1124 # This is a low-overhead backdoor. A user can (by intent or
1125 # mistake) pass a string directly, which (if it's the right length)
1126 # will get treated like a pickle, and bypass the normal sanity
1127 # checks in the constructor. This can create insane objects.
1128 # The constructor doesn't want to burn the time to validate all
1129 # fields, but does check the month field. This stops, e.g.,
1130 # datetime.datetime('1995-03-25') from yielding an insane object.
1131 base = '1995-03-25'
1132 if not issubclass(self.theclass, datetime):
1133 base = base[:4]
1134 for month_byte in '9', chr(0), chr(13), '\xff':
1135 self.assertRaises(TypeError, self.theclass,
1136 base[:2] + month_byte + base[3:])
1137 for ord_byte in range(1, 13):
1138 # This shouldn't blow up because of the month byte alone. If
1139 # the implementation changes to do more-careful checking, it may
1140 # blow up because other fields are insane.
1141 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001142
Tim Peters2a799bf2002-12-16 20:18:38 +00001143#############################################################################
1144# datetime tests
1145
Tim Peters604c0132004-06-07 23:04:33 +00001146class SubclassDatetime(datetime):
1147 sub_var = 1
1148
Tim Peters2a799bf2002-12-16 20:18:38 +00001149class TestDateTime(TestDate):
1150
1151 theclass = datetime
1152
1153 def test_basic_attributes(self):
1154 dt = self.theclass(2002, 3, 1, 12, 0)
1155 self.assertEqual(dt.year, 2002)
1156 self.assertEqual(dt.month, 3)
1157 self.assertEqual(dt.day, 1)
1158 self.assertEqual(dt.hour, 12)
1159 self.assertEqual(dt.minute, 0)
1160 self.assertEqual(dt.second, 0)
1161 self.assertEqual(dt.microsecond, 0)
1162
1163 def test_basic_attributes_nonzero(self):
1164 # Make sure all attributes are non-zero so bugs in
1165 # bit-shifting access show up.
1166 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1167 self.assertEqual(dt.year, 2002)
1168 self.assertEqual(dt.month, 3)
1169 self.assertEqual(dt.day, 1)
1170 self.assertEqual(dt.hour, 12)
1171 self.assertEqual(dt.minute, 59)
1172 self.assertEqual(dt.second, 59)
1173 self.assertEqual(dt.microsecond, 8000)
1174
1175 def test_roundtrip(self):
1176 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1177 self.theclass.now()):
1178 # Verify dt -> string -> datetime identity.
1179 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001180 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001181 s = s[9:]
1182 dt2 = eval(s)
1183 self.assertEqual(dt, dt2)
1184
1185 # Verify identity via reconstructing from pieces.
1186 dt2 = self.theclass(dt.year, dt.month, dt.day,
1187 dt.hour, dt.minute, dt.second,
1188 dt.microsecond)
1189 self.assertEqual(dt, dt2)
1190
1191 def test_isoformat(self):
1192 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1193 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1194 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1195 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arc8645a5c2009-12-29 22:03:38 +00001196 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001197 # str is ISO format with the separator forced to a blank.
1198 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1199
1200 t = self.theclass(2, 3, 2)
1201 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1202 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1203 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1204 # str is ISO format with the separator forced to a blank.
1205 self.assertEqual(str(t), "0002-03-02 00:00:00")
1206
Eric Smitha9f7d622008-02-17 19:46:49 +00001207 def test_format(self):
1208 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1209 self.assertEqual(dt.__format__(''), str(dt))
1210
1211 # check that a derived class's __str__() gets called
1212 class A(self.theclass):
1213 def __str__(self):
1214 return 'A'
1215 a = A(2007, 9, 10, 4, 5, 1, 123)
1216 self.assertEqual(a.__format__(''), 'A')
1217
1218 # check that a derived class's strftime gets called
1219 class B(self.theclass):
1220 def strftime(self, format_spec):
1221 return 'B'
1222 b = B(2007, 9, 10, 4, 5, 1, 123)
1223 self.assertEqual(b.__format__(''), str(dt))
1224
1225 for fmt in ["m:%m d:%d y:%y",
1226 "m:%m d:%d y:%y H:%H M:%M S:%S",
1227 "%z %Z",
1228 ]:
1229 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1230 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1231 self.assertEqual(b.__format__(fmt), 'B')
1232
Tim Peters2a799bf2002-12-16 20:18:38 +00001233 def test_more_ctime(self):
1234 # Test fields that TestDate doesn't touch.
1235 import time
1236
1237 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1238 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1239 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1240 # out. The difference is that t.ctime() produces " 2" for the day,
1241 # but platform ctime() produces "02" for the day. According to
1242 # C99, t.ctime() is correct here.
1243 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1244
1245 # So test a case where that difference doesn't matter.
1246 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1247 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1248
1249 def test_tz_independent_comparing(self):
1250 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1251 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1252 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1253 self.assertEqual(dt1, dt3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001254 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001255
1256 # Make sure comparison doesn't forget microseconds, and isn't done
1257 # via comparing a float timestamp (an IEEE double doesn't have enough
1258 # precision to span microsecond resolution across years 1 thru 9999,
1259 # so comparing via timestamp necessarily calls some distinct values
1260 # equal).
1261 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1262 us = timedelta(microseconds=1)
1263 dt2 = dt1 + us
1264 self.assertEqual(dt2 - dt1, us)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001265 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001266
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001267 def test_strftime_with_bad_tzname_replace(self):
1268 # verify ok if tzinfo.tzname().replace() returns a non-string
1269 class MyTzInfo(FixedOffset):
1270 def tzname(self, dt):
1271 class MyStr(str):
1272 def replace(self, *args):
1273 return None
1274 return MyStr('name')
1275 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1276 self.assertRaises(TypeError, t.strftime, '%Z')
1277
Tim Peters2a799bf2002-12-16 20:18:38 +00001278 def test_bad_constructor_arguments(self):
1279 # bad years
1280 self.theclass(MINYEAR, 1, 1) # no exception
1281 self.theclass(MAXYEAR, 1, 1) # no exception
1282 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1283 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1284 # bad months
1285 self.theclass(2000, 1, 1) # no exception
1286 self.theclass(2000, 12, 1) # no exception
1287 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1288 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1289 # bad days
1290 self.theclass(2000, 2, 29) # no exception
1291 self.theclass(2004, 2, 29) # no exception
1292 self.theclass(2400, 2, 29) # no exception
1293 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1294 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1295 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1296 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1297 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1298 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1299 # bad hours
1300 self.theclass(2000, 1, 31, 0) # no exception
1301 self.theclass(2000, 1, 31, 23) # no exception
1302 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1303 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1304 # bad minutes
1305 self.theclass(2000, 1, 31, 23, 0) # no exception
1306 self.theclass(2000, 1, 31, 23, 59) # no exception
1307 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1308 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1309 # bad seconds
1310 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1311 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1312 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1313 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1314 # bad microseconds
1315 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1316 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1317 self.assertRaises(ValueError, self.theclass,
1318 2000, 1, 31, 23, 59, 59, -1)
1319 self.assertRaises(ValueError, self.theclass,
1320 2000, 1, 31, 23, 59, 59,
1321 1000000)
1322
1323 def test_hash_equality(self):
1324 d = self.theclass(2000, 12, 31, 23, 30, 17)
1325 e = self.theclass(2000, 12, 31, 23, 30, 17)
1326 self.assertEqual(d, e)
1327 self.assertEqual(hash(d), hash(e))
1328
1329 dic = {d: 1}
1330 dic[e] = 2
1331 self.assertEqual(len(dic), 1)
1332 self.assertEqual(dic[d], 2)
1333 self.assertEqual(dic[e], 2)
1334
1335 d = self.theclass(2001, 1, 1, 0, 5, 17)
1336 e = self.theclass(2001, 1, 1, 0, 5, 17)
1337 self.assertEqual(d, e)
1338 self.assertEqual(hash(d), hash(e))
1339
1340 dic = {d: 1}
1341 dic[e] = 2
1342 self.assertEqual(len(dic), 1)
1343 self.assertEqual(dic[d], 2)
1344 self.assertEqual(dic[e], 2)
1345
1346 def test_computations(self):
1347 a = self.theclass(2002, 1, 31)
1348 b = self.theclass(1956, 1, 31)
1349 diff = a-b
1350 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1351 self.assertEqual(diff.seconds, 0)
1352 self.assertEqual(diff.microseconds, 0)
1353 a = self.theclass(2002, 3, 2, 17, 6)
1354 millisec = timedelta(0, 0, 1000)
1355 hour = timedelta(0, 3600)
1356 day = timedelta(1)
1357 week = timedelta(7)
1358 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1359 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1360 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1361 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1362 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1363 self.assertEqual(a - hour, a + -hour)
1364 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1365 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1366 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1367 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1368 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1369 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1370 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1371 self.assertEqual((a + week) - a, week)
1372 self.assertEqual((a + day) - a, day)
1373 self.assertEqual((a + hour) - a, hour)
1374 self.assertEqual((a + millisec) - a, millisec)
1375 self.assertEqual((a - week) - a, -week)
1376 self.assertEqual((a - day) - a, -day)
1377 self.assertEqual((a - hour) - a, -hour)
1378 self.assertEqual((a - millisec) - a, -millisec)
1379 self.assertEqual(a - (a + week), -week)
1380 self.assertEqual(a - (a + day), -day)
1381 self.assertEqual(a - (a + hour), -hour)
1382 self.assertEqual(a - (a + millisec), -millisec)
1383 self.assertEqual(a - (a - week), week)
1384 self.assertEqual(a - (a - day), day)
1385 self.assertEqual(a - (a - hour), hour)
1386 self.assertEqual(a - (a - millisec), millisec)
1387 self.assertEqual(a + (week + day + hour + millisec),
1388 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1389 self.assertEqual(a + (week + day + hour + millisec),
1390 (((a + week) + day) + hour) + millisec)
1391 self.assertEqual(a - (week + day + hour + millisec),
1392 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1393 self.assertEqual(a - (week + day + hour + millisec),
1394 (((a - week) - day) - hour) - millisec)
1395 # Add/sub ints, longs, floats should be illegal
1396 for i in 1, 1L, 1.0:
1397 self.assertRaises(TypeError, lambda: a+i)
1398 self.assertRaises(TypeError, lambda: a-i)
1399 self.assertRaises(TypeError, lambda: i+a)
1400 self.assertRaises(TypeError, lambda: i-a)
1401
1402 # delta - datetime is senseless.
1403 self.assertRaises(TypeError, lambda: day - a)
1404 # mixing datetime and (delta or datetime) via * or // is senseless
1405 self.assertRaises(TypeError, lambda: day * a)
1406 self.assertRaises(TypeError, lambda: a * day)
1407 self.assertRaises(TypeError, lambda: day // a)
1408 self.assertRaises(TypeError, lambda: a // day)
1409 self.assertRaises(TypeError, lambda: a * a)
1410 self.assertRaises(TypeError, lambda: a // a)
1411 # datetime + datetime is senseless
1412 self.assertRaises(TypeError, lambda: a + a)
1413
1414 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001415 args = 6, 7, 23, 20, 59, 1, 64**2
1416 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001417 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001418 green = pickler.dumps(orig, proto)
1419 derived = unpickler.loads(green)
1420 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001421
Guido van Rossum275666f2003-02-07 21:49:01 +00001422 def test_more_pickling(self):
1423 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1424 s = pickle.dumps(a)
1425 b = pickle.loads(s)
1426 self.assertEqual(b.year, 2003)
1427 self.assertEqual(b.month, 2)
1428 self.assertEqual(b.day, 7)
1429
Tim Peters604c0132004-06-07 23:04:33 +00001430 def test_pickling_subclass_datetime(self):
1431 args = 6, 7, 23, 20, 59, 1, 64**2
1432 orig = SubclassDatetime(*args)
1433 for pickler, unpickler, proto in pickle_choices:
1434 green = pickler.dumps(orig, proto)
1435 derived = unpickler.loads(green)
1436 self.assertEqual(orig, derived)
1437
Tim Peters2a799bf2002-12-16 20:18:38 +00001438 def test_more_compare(self):
1439 # The test_compare() inherited from TestDate covers the error cases.
1440 # We just want to test lexicographic ordering on the members datetime
1441 # has that date lacks.
1442 args = [2000, 11, 29, 20, 58, 16, 999998]
1443 t1 = self.theclass(*args)
1444 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001445 self.assertTrue(t1 == t2)
1446 self.assertTrue(t1 <= t2)
1447 self.assertTrue(t1 >= t2)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001448 self.assertFalse(t1 != t2)
1449 self.assertFalse(t1 < t2)
1450 self.assertFalse(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001451 self.assertEqual(cmp(t1, t2), 0)
1452 self.assertEqual(cmp(t2, t1), 0)
1453
1454 for i in range(len(args)):
1455 newargs = args[:]
1456 newargs[i] = args[i] + 1
1457 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001458 self.assertTrue(t1 < t2)
1459 self.assertTrue(t2 > t1)
1460 self.assertTrue(t1 <= t2)
1461 self.assertTrue(t2 >= t1)
1462 self.assertTrue(t1 != t2)
1463 self.assertTrue(t2 != t1)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001464 self.assertFalse(t1 == t2)
1465 self.assertFalse(t2 == t1)
1466 self.assertFalse(t1 > t2)
1467 self.assertFalse(t2 < t1)
1468 self.assertFalse(t1 >= t2)
1469 self.assertFalse(t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001470 self.assertEqual(cmp(t1, t2), -1)
1471 self.assertEqual(cmp(t2, t1), 1)
1472
1473
1474 # A helper for timestamp constructor tests.
1475 def verify_field_equality(self, expected, got):
1476 self.assertEqual(expected.tm_year, got.year)
1477 self.assertEqual(expected.tm_mon, got.month)
1478 self.assertEqual(expected.tm_mday, got.day)
1479 self.assertEqual(expected.tm_hour, got.hour)
1480 self.assertEqual(expected.tm_min, got.minute)
1481 self.assertEqual(expected.tm_sec, got.second)
1482
1483 def test_fromtimestamp(self):
1484 import time
1485
1486 ts = time.time()
1487 expected = time.localtime(ts)
1488 got = self.theclass.fromtimestamp(ts)
1489 self.verify_field_equality(expected, got)
1490
1491 def test_utcfromtimestamp(self):
1492 import time
1493
1494 ts = time.time()
1495 expected = time.gmtime(ts)
1496 got = self.theclass.utcfromtimestamp(ts)
1497 self.verify_field_equality(expected, got)
1498
Georg Brandl6d78a582006-04-28 19:09:24 +00001499 def test_microsecond_rounding(self):
1500 # Test whether fromtimestamp "rounds up" floats that are less
1501 # than one microsecond smaller than an integer.
Ezio Melotti2623a372010-11-21 13:34:58 +00001502 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1503 self.theclass.fromtimestamp(1))
Georg Brandl6d78a582006-04-28 19:09:24 +00001504
Tim Peters1b6f7a92004-06-20 02:50:16 +00001505 def test_insane_fromtimestamp(self):
1506 # It's possible that some platform maps time_t to double,
1507 # and that this test will fail there. This test should
1508 # exempt such platforms (provided they return reasonable
1509 # results!).
1510 for insane in -1e200, 1e200:
1511 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1512 insane)
1513
1514 def test_insane_utcfromtimestamp(self):
1515 # It's possible that some platform maps time_t to double,
1516 # and that this test will fail there. This test should
1517 # exempt such platforms (provided they return reasonable
1518 # results!).
1519 for insane in -1e200, 1e200:
1520 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1521 insane)
Alexander Belopolsky58451d22010-05-26 20:45:37 +00001522 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossum2054ee92007-03-06 15:50:01 +00001523 def test_negative_float_fromtimestamp(self):
1524 # The result is tz-dependent; at least test that this doesn't
1525 # fail (like it did before bug 1646728 was fixed).
1526 self.theclass.fromtimestamp(-1.05)
1527
Alexander Belopolsky58451d22010-05-26 20:45:37 +00001528 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossum2054ee92007-03-06 15:50:01 +00001529 def test_negative_float_utcfromtimestamp(self):
1530 d = self.theclass.utcfromtimestamp(-1.05)
Ezio Melotti2623a372010-11-21 13:34:58 +00001531 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossum2054ee92007-03-06 15:50:01 +00001532
Tim Peters2a799bf2002-12-16 20:18:38 +00001533 def test_utcnow(self):
1534 import time
1535
1536 # Call it a success if utcnow() and utcfromtimestamp() are within
1537 # a second of each other.
1538 tolerance = timedelta(seconds=1)
1539 for dummy in range(3):
1540 from_now = self.theclass.utcnow()
1541 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1542 if abs(from_timestamp - from_now) <= tolerance:
1543 break
1544 # Else try again a few times.
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001545 self.assertLessEqual(abs(from_timestamp - from_now), tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001546
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001547 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001548 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001549
Skip Montanarofc070d22008-03-15 16:04:45 +00001550 string = '2004-12-01 13:02:47.197'
1551 format = '%Y-%m-%d %H:%M:%S.%f'
1552 result, frac = _strptime._strptime(string, format)
1553 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001554 got = self.theclass.strptime(string, format)
1555 self.assertEqual(expected, got)
1556
Tim Peters2a799bf2002-12-16 20:18:38 +00001557 def test_more_timetuple(self):
1558 # This tests fields beyond those tested by the TestDate.test_timetuple.
1559 t = self.theclass(2004, 12, 31, 6, 22, 33)
1560 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1561 self.assertEqual(t.timetuple(),
1562 (t.year, t.month, t.day,
1563 t.hour, t.minute, t.second,
1564 t.weekday(),
1565 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1566 -1))
1567 tt = t.timetuple()
1568 self.assertEqual(tt.tm_year, t.year)
1569 self.assertEqual(tt.tm_mon, t.month)
1570 self.assertEqual(tt.tm_mday, t.day)
1571 self.assertEqual(tt.tm_hour, t.hour)
1572 self.assertEqual(tt.tm_min, t.minute)
1573 self.assertEqual(tt.tm_sec, t.second)
1574 self.assertEqual(tt.tm_wday, t.weekday())
1575 self.assertEqual(tt.tm_yday, t.toordinal() -
1576 date(t.year, 1, 1).toordinal() + 1)
1577 self.assertEqual(tt.tm_isdst, -1)
1578
1579 def test_more_strftime(self):
1580 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001581 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1582 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1583 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001584
1585 def test_extract(self):
1586 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1587 self.assertEqual(dt.date(), date(2002, 3, 4))
1588 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1589
1590 def test_combine(self):
1591 d = date(2002, 3, 4)
1592 t = time(18, 45, 3, 1234)
1593 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1594 combine = self.theclass.combine
1595 dt = combine(d, t)
1596 self.assertEqual(dt, expected)
1597
1598 dt = combine(time=t, date=d)
1599 self.assertEqual(dt, expected)
1600
1601 self.assertEqual(d, dt.date())
1602 self.assertEqual(t, dt.time())
1603 self.assertEqual(dt, combine(dt.date(), dt.time()))
1604
1605 self.assertRaises(TypeError, combine) # need an arg
1606 self.assertRaises(TypeError, combine, d) # need two args
1607 self.assertRaises(TypeError, combine, t, d) # args reversed
1608 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1609 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1610
Tim Peters12bf3392002-12-24 05:41:27 +00001611 def test_replace(self):
1612 cls = self.theclass
1613 args = [1, 2, 3, 4, 5, 6, 7]
1614 base = cls(*args)
1615 self.assertEqual(base, base.replace())
1616
1617 i = 0
1618 for name, newval in (("year", 2),
1619 ("month", 3),
1620 ("day", 4),
1621 ("hour", 5),
1622 ("minute", 6),
1623 ("second", 7),
1624 ("microsecond", 8)):
1625 newargs = args[:]
1626 newargs[i] = newval
1627 expected = cls(*newargs)
1628 got = base.replace(**{name: newval})
1629 self.assertEqual(expected, got)
1630 i += 1
1631
1632 # Out of bounds.
1633 base = cls(2000, 2, 29)
1634 self.assertRaises(ValueError, base.replace, year=2001)
1635
Tim Peters80475bb2002-12-25 07:40:55 +00001636 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001637 # Pretty boring! The TZ test is more interesting here. astimezone()
1638 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001639 dt = self.theclass.now()
1640 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001641 self.assertRaises(TypeError, dt.astimezone) # not enough args
1642 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1643 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001644 self.assertRaises(ValueError, dt.astimezone, f) # naive
1645 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001646
Tim Peters52dcce22003-01-23 16:36:11 +00001647 class Bogus(tzinfo):
1648 def utcoffset(self, dt): return None
1649 def dst(self, dt): return timedelta(0)
1650 bog = Bogus()
1651 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1652
1653 class AlsoBogus(tzinfo):
1654 def utcoffset(self, dt): return timedelta(0)
1655 def dst(self, dt): return None
1656 alsobog = AlsoBogus()
1657 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001658
Tim Petersa98924a2003-05-17 05:55:19 +00001659 def test_subclass_datetime(self):
1660
1661 class C(self.theclass):
1662 theAnswer = 42
1663
1664 def __new__(cls, *args, **kws):
1665 temp = kws.copy()
1666 extra = temp.pop('extra')
1667 result = self.theclass.__new__(cls, *args, **temp)
1668 result.extra = extra
1669 return result
1670
1671 def newmeth(self, start):
1672 return start + self.year + self.month + self.second
1673
1674 args = 2003, 4, 14, 12, 13, 41
1675
1676 dt1 = self.theclass(*args)
1677 dt2 = C(*args, **{'extra': 7})
1678
1679 self.assertEqual(dt2.__class__, C)
1680 self.assertEqual(dt2.theAnswer, 42)
1681 self.assertEqual(dt2.extra, 7)
1682 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1683 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1684 dt1.second - 7)
1685
Tim Peters604c0132004-06-07 23:04:33 +00001686class SubclassTime(time):
1687 sub_var = 1
1688
Collin Winterc2898c52007-04-25 17:29:52 +00001689class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001690
1691 theclass = time
1692
1693 def test_basic_attributes(self):
1694 t = self.theclass(12, 0)
1695 self.assertEqual(t.hour, 12)
1696 self.assertEqual(t.minute, 0)
1697 self.assertEqual(t.second, 0)
1698 self.assertEqual(t.microsecond, 0)
1699
1700 def test_basic_attributes_nonzero(self):
1701 # Make sure all attributes are non-zero so bugs in
1702 # bit-shifting access show up.
1703 t = self.theclass(12, 59, 59, 8000)
1704 self.assertEqual(t.hour, 12)
1705 self.assertEqual(t.minute, 59)
1706 self.assertEqual(t.second, 59)
1707 self.assertEqual(t.microsecond, 8000)
1708
1709 def test_roundtrip(self):
1710 t = self.theclass(1, 2, 3, 4)
1711
1712 # Verify t -> string -> time identity.
1713 s = repr(t)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001714 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001715 s = s[9:]
1716 t2 = eval(s)
1717 self.assertEqual(t, t2)
1718
1719 # Verify identity via reconstructing from pieces.
1720 t2 = self.theclass(t.hour, t.minute, t.second,
1721 t.microsecond)
1722 self.assertEqual(t, t2)
1723
1724 def test_comparing(self):
1725 args = [1, 2, 3, 4]
1726 t1 = self.theclass(*args)
1727 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001728 self.assertTrue(t1 == t2)
1729 self.assertTrue(t1 <= t2)
1730 self.assertTrue(t1 >= t2)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001731 self.assertFalse(t1 != t2)
1732 self.assertFalse(t1 < t2)
1733 self.assertFalse(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001734 self.assertEqual(cmp(t1, t2), 0)
1735 self.assertEqual(cmp(t2, t1), 0)
1736
1737 for i in range(len(args)):
1738 newargs = args[:]
1739 newargs[i] = args[i] + 1
1740 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001741 self.assertTrue(t1 < t2)
1742 self.assertTrue(t2 > t1)
1743 self.assertTrue(t1 <= t2)
1744 self.assertTrue(t2 >= t1)
1745 self.assertTrue(t1 != t2)
1746 self.assertTrue(t2 != t1)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001747 self.assertFalse(t1 == t2)
1748 self.assertFalse(t2 == t1)
1749 self.assertFalse(t1 > t2)
1750 self.assertFalse(t2 < t1)
1751 self.assertFalse(t1 >= t2)
1752 self.assertFalse(t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001753 self.assertEqual(cmp(t1, t2), -1)
1754 self.assertEqual(cmp(t2, t1), 1)
1755
Tim Peters68124bb2003-02-08 03:46:31 +00001756 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001757 self.assertEqual(t1 == badarg, False)
1758 self.assertEqual(t1 != badarg, True)
1759 self.assertEqual(badarg == t1, False)
1760 self.assertEqual(badarg != t1, True)
1761
Tim Peters2a799bf2002-12-16 20:18:38 +00001762 self.assertRaises(TypeError, lambda: t1 <= badarg)
1763 self.assertRaises(TypeError, lambda: t1 < badarg)
1764 self.assertRaises(TypeError, lambda: t1 > badarg)
1765 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001766 self.assertRaises(TypeError, lambda: badarg <= t1)
1767 self.assertRaises(TypeError, lambda: badarg < t1)
1768 self.assertRaises(TypeError, lambda: badarg > t1)
1769 self.assertRaises(TypeError, lambda: badarg >= t1)
1770
1771 def test_bad_constructor_arguments(self):
1772 # bad hours
1773 self.theclass(0, 0) # no exception
1774 self.theclass(23, 0) # no exception
1775 self.assertRaises(ValueError, self.theclass, -1, 0)
1776 self.assertRaises(ValueError, self.theclass, 24, 0)
1777 # bad minutes
1778 self.theclass(23, 0) # no exception
1779 self.theclass(23, 59) # no exception
1780 self.assertRaises(ValueError, self.theclass, 23, -1)
1781 self.assertRaises(ValueError, self.theclass, 23, 60)
1782 # bad seconds
1783 self.theclass(23, 59, 0) # no exception
1784 self.theclass(23, 59, 59) # no exception
1785 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1786 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1787 # bad microseconds
1788 self.theclass(23, 59, 59, 0) # no exception
1789 self.theclass(23, 59, 59, 999999) # no exception
1790 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1791 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1792
1793 def test_hash_equality(self):
1794 d = self.theclass(23, 30, 17)
1795 e = self.theclass(23, 30, 17)
1796 self.assertEqual(d, e)
1797 self.assertEqual(hash(d), hash(e))
1798
1799 dic = {d: 1}
1800 dic[e] = 2
1801 self.assertEqual(len(dic), 1)
1802 self.assertEqual(dic[d], 2)
1803 self.assertEqual(dic[e], 2)
1804
1805 d = self.theclass(0, 5, 17)
1806 e = self.theclass(0, 5, 17)
1807 self.assertEqual(d, e)
1808 self.assertEqual(hash(d), hash(e))
1809
1810 dic = {d: 1}
1811 dic[e] = 2
1812 self.assertEqual(len(dic), 1)
1813 self.assertEqual(dic[d], 2)
1814 self.assertEqual(dic[e], 2)
1815
1816 def test_isoformat(self):
1817 t = self.theclass(4, 5, 1, 123)
1818 self.assertEqual(t.isoformat(), "04:05:01.000123")
1819 self.assertEqual(t.isoformat(), str(t))
1820
1821 t = self.theclass()
1822 self.assertEqual(t.isoformat(), "00:00:00")
1823 self.assertEqual(t.isoformat(), str(t))
1824
1825 t = self.theclass(microsecond=1)
1826 self.assertEqual(t.isoformat(), "00:00:00.000001")
1827 self.assertEqual(t.isoformat(), str(t))
1828
1829 t = self.theclass(microsecond=10)
1830 self.assertEqual(t.isoformat(), "00:00:00.000010")
1831 self.assertEqual(t.isoformat(), str(t))
1832
1833 t = self.theclass(microsecond=100)
1834 self.assertEqual(t.isoformat(), "00:00:00.000100")
1835 self.assertEqual(t.isoformat(), str(t))
1836
1837 t = self.theclass(microsecond=1000)
1838 self.assertEqual(t.isoformat(), "00:00:00.001000")
1839 self.assertEqual(t.isoformat(), str(t))
1840
1841 t = self.theclass(microsecond=10000)
1842 self.assertEqual(t.isoformat(), "00:00:00.010000")
1843 self.assertEqual(t.isoformat(), str(t))
1844
1845 t = self.theclass(microsecond=100000)
1846 self.assertEqual(t.isoformat(), "00:00:00.100000")
1847 self.assertEqual(t.isoformat(), str(t))
1848
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001849 def test_1653736(self):
1850 # verify it doesn't accept extra keyword arguments
1851 t = self.theclass(second=1)
1852 self.assertRaises(TypeError, t.isoformat, foo=3)
1853
Tim Peters2a799bf2002-12-16 20:18:38 +00001854 def test_strftime(self):
1855 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001856 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001857 # A naive object replaces %z and %Z with empty strings.
1858 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1859
Eric Smitha9f7d622008-02-17 19:46:49 +00001860 def test_format(self):
1861 t = self.theclass(1, 2, 3, 4)
1862 self.assertEqual(t.__format__(''), str(t))
1863
1864 # check that a derived class's __str__() gets called
1865 class A(self.theclass):
1866 def __str__(self):
1867 return 'A'
1868 a = A(1, 2, 3, 4)
1869 self.assertEqual(a.__format__(''), 'A')
1870
1871 # check that a derived class's strftime gets called
1872 class B(self.theclass):
1873 def strftime(self, format_spec):
1874 return 'B'
1875 b = B(1, 2, 3, 4)
1876 self.assertEqual(b.__format__(''), str(t))
1877
1878 for fmt in ['%H %M %S',
1879 ]:
1880 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1881 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1882 self.assertEqual(b.__format__(fmt), 'B')
1883
Tim Peters2a799bf2002-12-16 20:18:38 +00001884 def test_str(self):
1885 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1886 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1887 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1888 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1889 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1890
1891 def test_repr(self):
1892 name = 'datetime.' + self.theclass.__name__
1893 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1894 "%s(1, 2, 3, 4)" % name)
1895 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1896 "%s(10, 2, 3, 4000)" % name)
1897 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1898 "%s(0, 2, 3, 400000)" % name)
1899 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1900 "%s(12, 2, 3)" % name)
1901 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1902 "%s(23, 15)" % name)
1903
1904 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +00001905 self.assertIsInstance(self.theclass.min, self.theclass)
1906 self.assertIsInstance(self.theclass.max, self.theclass)
1907 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001908 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001909
1910 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001911 args = 20, 59, 16, 64**2
1912 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001913 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001914 green = pickler.dumps(orig, proto)
1915 derived = unpickler.loads(green)
1916 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001917
Tim Peters604c0132004-06-07 23:04:33 +00001918 def test_pickling_subclass_time(self):
1919 args = 20, 59, 16, 64**2
1920 orig = SubclassTime(*args)
1921 for pickler, unpickler, proto in pickle_choices:
1922 green = pickler.dumps(orig, proto)
1923 derived = unpickler.loads(green)
1924 self.assertEqual(orig, derived)
1925
Tim Peters2a799bf2002-12-16 20:18:38 +00001926 def test_bool(self):
1927 cls = self.theclass
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001928 self.assertTrue(cls(1))
1929 self.assertTrue(cls(0, 1))
1930 self.assertTrue(cls(0, 0, 1))
1931 self.assertTrue(cls(0, 0, 0, 1))
Serhiy Storchaka1438b982013-11-17 13:03:21 +02001932 self.assertFalse(cls(0))
1933 self.assertFalse(cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001934
Tim Peters12bf3392002-12-24 05:41:27 +00001935 def test_replace(self):
1936 cls = self.theclass
1937 args = [1, 2, 3, 4]
1938 base = cls(*args)
1939 self.assertEqual(base, base.replace())
1940
1941 i = 0
1942 for name, newval in (("hour", 5),
1943 ("minute", 6),
1944 ("second", 7),
1945 ("microsecond", 8)):
1946 newargs = args[:]
1947 newargs[i] = newval
1948 expected = cls(*newargs)
1949 got = base.replace(**{name: newval})
1950 self.assertEqual(expected, got)
1951 i += 1
1952
1953 # Out of bounds.
1954 base = cls(1)
1955 self.assertRaises(ValueError, base.replace, hour=24)
1956 self.assertRaises(ValueError, base.replace, minute=-1)
1957 self.assertRaises(ValueError, base.replace, second=100)
1958 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1959
Tim Petersa98924a2003-05-17 05:55:19 +00001960 def test_subclass_time(self):
1961
1962 class C(self.theclass):
1963 theAnswer = 42
1964
1965 def __new__(cls, *args, **kws):
1966 temp = kws.copy()
1967 extra = temp.pop('extra')
1968 result = self.theclass.__new__(cls, *args, **temp)
1969 result.extra = extra
1970 return result
1971
1972 def newmeth(self, start):
1973 return start + self.hour + self.second
1974
1975 args = 4, 5, 6
1976
1977 dt1 = self.theclass(*args)
1978 dt2 = C(*args, **{'extra': 7})
1979
1980 self.assertEqual(dt2.__class__, C)
1981 self.assertEqual(dt2.theAnswer, 42)
1982 self.assertEqual(dt2.extra, 7)
1983 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1984 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1985
Armin Rigof4afb212005-11-07 07:15:48 +00001986 def test_backdoor_resistance(self):
1987 # see TestDate.test_backdoor_resistance().
1988 base = '2:59.0'
1989 for hour_byte in ' ', '9', chr(24), '\xff':
1990 self.assertRaises(TypeError, self.theclass,
1991 hour_byte + base[1:])
1992
Tim Peters855fe882002-12-22 03:43:39 +00001993# A mixin for classes with a tzinfo= argument. Subclasses must define
1994# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001995# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001996class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001997
Tim Petersbad8ff02002-12-30 20:52:32 +00001998 def test_argument_passing(self):
1999 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002000 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002001 class introspective(tzinfo):
2002 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002003 def utcoffset(self, dt):
2004 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002005 dst = utcoffset
2006
2007 obj = cls(1, 2, 3, tzinfo=introspective())
2008
Tim Peters0bf60bd2003-01-08 20:40:01 +00002009 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002010 self.assertEqual(obj.tzname(), expected)
2011
Tim Peters0bf60bd2003-01-08 20:40:01 +00002012 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002013 self.assertEqual(obj.utcoffset(), expected)
2014 self.assertEqual(obj.dst(), expected)
2015
Tim Peters855fe882002-12-22 03:43:39 +00002016 def test_bad_tzinfo_classes(self):
2017 cls = self.theclass
2018 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002019
Tim Peters855fe882002-12-22 03:43:39 +00002020 class NiceTry(object):
2021 def __init__(self): pass
2022 def utcoffset(self, dt): pass
2023 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2024
2025 class BetterTry(tzinfo):
2026 def __init__(self): pass
2027 def utcoffset(self, dt): pass
2028 b = BetterTry()
2029 t = cls(1, 1, 1, tzinfo=b)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002030 self.assertIs(t.tzinfo, b)
Tim Peters855fe882002-12-22 03:43:39 +00002031
2032 def test_utc_offset_out_of_bounds(self):
2033 class Edgy(tzinfo):
2034 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002035 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002036 def utcoffset(self, dt):
2037 return self.offset
2038
2039 cls = self.theclass
2040 for offset, legit in ((-1440, False),
2041 (-1439, True),
2042 (1439, True),
2043 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002044 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002045 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002046 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002047 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002048 else:
2049 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002050 if legit:
2051 aofs = abs(offset)
2052 h, m = divmod(aofs, 60)
2053 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002054 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002055 t = t.timetz()
2056 self.assertEqual(str(t), "01:02:03" + tag)
2057 else:
2058 self.assertRaises(ValueError, str, t)
2059
2060 def test_tzinfo_classes(self):
2061 cls = self.theclass
2062 class C1(tzinfo):
2063 def utcoffset(self, dt): return None
2064 def dst(self, dt): return None
2065 def tzname(self, dt): return None
2066 for t in (cls(1, 1, 1),
2067 cls(1, 1, 1, tzinfo=None),
2068 cls(1, 1, 1, tzinfo=C1())):
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002069 self.assertIsNone(t.utcoffset())
2070 self.assertIsNone(t.dst())
2071 self.assertIsNone(t.tzname())
Tim Peters855fe882002-12-22 03:43:39 +00002072
Tim Peters855fe882002-12-22 03:43:39 +00002073 class C3(tzinfo):
2074 def utcoffset(self, dt): return timedelta(minutes=-1439)
2075 def dst(self, dt): return timedelta(minutes=1439)
2076 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002077 t = cls(1, 1, 1, tzinfo=C3())
2078 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2079 self.assertEqual(t.dst(), timedelta(minutes=1439))
2080 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002081
2082 # Wrong types.
2083 class C4(tzinfo):
2084 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002085 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002086 def tzname(self, dt): return 0
2087 t = cls(1, 1, 1, tzinfo=C4())
2088 self.assertRaises(TypeError, t.utcoffset)
2089 self.assertRaises(TypeError, t.dst)
2090 self.assertRaises(TypeError, t.tzname)
2091
2092 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002093 class C6(tzinfo):
2094 def utcoffset(self, dt): return timedelta(hours=-24)
2095 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002096 t = cls(1, 1, 1, tzinfo=C6())
2097 self.assertRaises(ValueError, t.utcoffset)
2098 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002099
2100 # Not a whole number of minutes.
2101 class C7(tzinfo):
2102 def utcoffset(self, dt): return timedelta(seconds=61)
2103 def dst(self, dt): return timedelta(microseconds=-81)
2104 t = cls(1, 1, 1, tzinfo=C7())
2105 self.assertRaises(ValueError, t.utcoffset)
2106 self.assertRaises(ValueError, t.dst)
2107
Tim Peters4c0db782002-12-26 05:01:19 +00002108 def test_aware_compare(self):
2109 cls = self.theclass
2110
Tim Peters60c76e42002-12-27 00:41:11 +00002111 # Ensure that utcoffset() gets ignored if the comparands have
2112 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002113 class OperandDependentOffset(tzinfo):
2114 def utcoffset(self, t):
2115 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002116 # d0 and d1 equal after adjustment
2117 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002118 else:
Tim Peters397301e2003-01-02 21:28:08 +00002119 # d2 off in the weeds
2120 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002121
2122 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2123 d0 = base.replace(minute=3)
2124 d1 = base.replace(minute=9)
2125 d2 = base.replace(minute=11)
2126 for x in d0, d1, d2:
2127 for y in d0, d1, d2:
2128 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002129 expected = cmp(x.minute, y.minute)
2130 self.assertEqual(got, expected)
2131
2132 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002133 # Note that a time can't actually have an operand-depedent offset,
2134 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2135 # so skip this test for time.
2136 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002137 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2138 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2139 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2140 for x in d0, d1, d2:
2141 for y in d0, d1, d2:
2142 got = cmp(x, y)
2143 if (x is d0 or x is d1) and (y is d0 or y is d1):
2144 expected = 0
2145 elif x is y is d2:
2146 expected = 0
2147 elif x is d2:
2148 expected = -1
2149 else:
2150 assert y is d2
2151 expected = 1
2152 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002153
Tim Peters855fe882002-12-22 03:43:39 +00002154
Tim Peters0bf60bd2003-01-08 20:40:01 +00002155# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002156class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002157 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002158
2159 def test_empty(self):
2160 t = self.theclass()
2161 self.assertEqual(t.hour, 0)
2162 self.assertEqual(t.minute, 0)
2163 self.assertEqual(t.second, 0)
2164 self.assertEqual(t.microsecond, 0)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002165 self.assertIsNone(t.tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +00002166
Tim Peters2a799bf2002-12-16 20:18:38 +00002167 def test_zones(self):
2168 est = FixedOffset(-300, "EST", 1)
2169 utc = FixedOffset(0, "UTC", -2)
2170 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002171 t1 = time( 7, 47, tzinfo=est)
2172 t2 = time(12, 47, tzinfo=utc)
2173 t3 = time(13, 47, tzinfo=met)
2174 t4 = time(microsecond=40)
2175 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002176
2177 self.assertEqual(t1.tzinfo, est)
2178 self.assertEqual(t2.tzinfo, utc)
2179 self.assertEqual(t3.tzinfo, met)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002180 self.assertIsNone(t4.tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +00002181 self.assertEqual(t5.tzinfo, utc)
2182
Tim Peters855fe882002-12-22 03:43:39 +00002183 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2184 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2185 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002186 self.assertIsNone(t4.utcoffset())
Tim Peters2a799bf2002-12-16 20:18:38 +00002187 self.assertRaises(TypeError, t1.utcoffset, "no args")
2188
2189 self.assertEqual(t1.tzname(), "EST")
2190 self.assertEqual(t2.tzname(), "UTC")
2191 self.assertEqual(t3.tzname(), "MET")
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002192 self.assertIsNone(t4.tzname())
Tim Peters2a799bf2002-12-16 20:18:38 +00002193 self.assertRaises(TypeError, t1.tzname, "no args")
2194
Tim Peters855fe882002-12-22 03:43:39 +00002195 self.assertEqual(t1.dst(), timedelta(minutes=1))
2196 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2197 self.assertEqual(t3.dst(), timedelta(minutes=3))
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002198 self.assertIsNone(t4.dst())
Tim Peters2a799bf2002-12-16 20:18:38 +00002199 self.assertRaises(TypeError, t1.dst, "no args")
2200
2201 self.assertEqual(hash(t1), hash(t2))
2202 self.assertEqual(hash(t1), hash(t3))
2203 self.assertEqual(hash(t2), hash(t3))
2204
2205 self.assertEqual(t1, t2)
2206 self.assertEqual(t1, t3)
2207 self.assertEqual(t2, t3)
2208 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2209 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2210 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2211
2212 self.assertEqual(str(t1), "07:47:00-05:00")
2213 self.assertEqual(str(t2), "12:47:00+00:00")
2214 self.assertEqual(str(t3), "13:47:00+01:00")
2215 self.assertEqual(str(t4), "00:00:00.000040")
2216 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2217
2218 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2219 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2220 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2221 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2222 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2223
Tim Peters0bf60bd2003-01-08 20:40:01 +00002224 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002225 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2226 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2227 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2228 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2229 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2230
2231 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2232 "07:47:00 %Z=EST %z=-0500")
2233 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2234 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2235
2236 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002237 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002238 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2239 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2240
Tim Petersb92bb712002-12-21 17:44:07 +00002241 # Check that an invalid tzname result raises an exception.
2242 class Badtzname(tzinfo):
2243 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002244 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002245 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2246 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002247
2248 def test_hash_edge_cases(self):
2249 # Offsets that overflow a basic time.
2250 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2251 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2252 self.assertEqual(hash(t1), hash(t2))
2253
2254 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2255 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2256 self.assertEqual(hash(t1), hash(t2))
2257
Tim Peters2a799bf2002-12-16 20:18:38 +00002258 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002259 # Try one without a tzinfo.
2260 args = 20, 59, 16, 64**2
2261 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002262 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002263 green = pickler.dumps(orig, proto)
2264 derived = unpickler.loads(green)
2265 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002266
2267 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002268 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002269 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002270 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002271 green = pickler.dumps(orig, proto)
2272 derived = unpickler.loads(green)
2273 self.assertEqual(orig, derived)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002274 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002275 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2276 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002277
2278 def test_more_bool(self):
2279 # Test cases with non-None tzinfo.
2280 cls = self.theclass
2281
2282 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002283 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002284
2285 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002286 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002287
2288 t = cls(5, tzinfo=FixedOffset(300, ""))
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002289 self.assertFalse(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002290
2291 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002292 self.assertFalse(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002293
2294 # Mostly ensuring this doesn't overflow internally.
2295 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002296 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002297
2298 # But this should yield a value error -- the utcoffset is bogus.
2299 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2300 self.assertRaises(ValueError, lambda: bool(t))
2301
2302 # Likewise.
2303 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2304 self.assertRaises(ValueError, lambda: bool(t))
2305
Tim Peters12bf3392002-12-24 05:41:27 +00002306 def test_replace(self):
2307 cls = self.theclass
2308 z100 = FixedOffset(100, "+100")
2309 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2310 args = [1, 2, 3, 4, z100]
2311 base = cls(*args)
2312 self.assertEqual(base, base.replace())
2313
2314 i = 0
2315 for name, newval in (("hour", 5),
2316 ("minute", 6),
2317 ("second", 7),
2318 ("microsecond", 8),
2319 ("tzinfo", zm200)):
2320 newargs = args[:]
2321 newargs[i] = newval
2322 expected = cls(*newargs)
2323 got = base.replace(**{name: newval})
2324 self.assertEqual(expected, got)
2325 i += 1
2326
2327 # Ensure we can get rid of a tzinfo.
2328 self.assertEqual(base.tzname(), "+100")
2329 base2 = base.replace(tzinfo=None)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002330 self.assertIsNone(base2.tzinfo)
2331 self.assertIsNone(base2.tzname())
Tim Peters12bf3392002-12-24 05:41:27 +00002332
2333 # Ensure we can add one.
2334 base3 = base2.replace(tzinfo=z100)
2335 self.assertEqual(base, base3)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002336 self.assertIs(base.tzinfo, base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002337
2338 # Out of bounds.
2339 base = cls(1)
2340 self.assertRaises(ValueError, base.replace, hour=24)
2341 self.assertRaises(ValueError, base.replace, minute=-1)
2342 self.assertRaises(ValueError, base.replace, second=100)
2343 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2344
Tim Peters60c76e42002-12-27 00:41:11 +00002345 def test_mixed_compare(self):
2346 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002347 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002348 self.assertEqual(t1, t2)
2349 t2 = t2.replace(tzinfo=None)
2350 self.assertEqual(t1, t2)
2351 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2352 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002353 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2354 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002355
Tim Peters0bf60bd2003-01-08 20:40:01 +00002356 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002357 class Varies(tzinfo):
2358 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002359 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002360 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002361 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002362 return self.offset
2363
2364 v = Varies()
2365 t1 = t2.replace(tzinfo=v)
2366 t2 = t2.replace(tzinfo=v)
2367 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2368 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2369 self.assertEqual(t1, t2)
2370
2371 # But if they're not identical, it isn't ignored.
2372 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002373 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002374
Tim Petersa98924a2003-05-17 05:55:19 +00002375 def test_subclass_timetz(self):
2376
2377 class C(self.theclass):
2378 theAnswer = 42
2379
2380 def __new__(cls, *args, **kws):
2381 temp = kws.copy()
2382 extra = temp.pop('extra')
2383 result = self.theclass.__new__(cls, *args, **temp)
2384 result.extra = extra
2385 return result
2386
2387 def newmeth(self, start):
2388 return start + self.hour + self.second
2389
2390 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2391
2392 dt1 = self.theclass(*args)
2393 dt2 = C(*args, **{'extra': 7})
2394
2395 self.assertEqual(dt2.__class__, C)
2396 self.assertEqual(dt2.theAnswer, 42)
2397 self.assertEqual(dt2.extra, 7)
2398 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2399 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2400
Tim Peters4c0db782002-12-26 05:01:19 +00002401
Tim Peters0bf60bd2003-01-08 20:40:01 +00002402# Testing datetime objects with a non-None tzinfo.
2403
Collin Winterc2898c52007-04-25 17:29:52 +00002404class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002405 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002406
2407 def test_trivial(self):
2408 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2409 self.assertEqual(dt.year, 1)
2410 self.assertEqual(dt.month, 2)
2411 self.assertEqual(dt.day, 3)
2412 self.assertEqual(dt.hour, 4)
2413 self.assertEqual(dt.minute, 5)
2414 self.assertEqual(dt.second, 6)
2415 self.assertEqual(dt.microsecond, 7)
2416 self.assertEqual(dt.tzinfo, None)
2417
2418 def test_even_more_compare(self):
2419 # The test_compare() and test_more_compare() inherited from TestDate
2420 # and TestDateTime covered non-tzinfo cases.
2421
2422 # Smallest possible after UTC adjustment.
2423 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2424 # Largest possible after UTC adjustment.
2425 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2426 tzinfo=FixedOffset(-1439, ""))
2427
2428 # Make sure those compare correctly, and w/o overflow.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002429 self.assertTrue(t1 < t2)
2430 self.assertTrue(t1 != t2)
2431 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002432
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002433 self.assertTrue(t1 == t1)
2434 self.assertTrue(t2 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002435
2436 # Equal afer adjustment.
2437 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2438 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2439 self.assertEqual(t1, t2)
2440
2441 # Change t1 not to subtract a minute, and t1 should be larger.
2442 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002443 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002444
2445 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2446 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002447 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002448
2449 # Back to the original t1, but make seconds resolve it.
2450 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2451 second=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002452 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002453
2454 # Likewise, but make microseconds resolve it.
2455 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2456 microsecond=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002457 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002458
2459 # Make t2 naive and it should fail.
2460 t2 = self.theclass.min
2461 self.assertRaises(TypeError, lambda: t1 == t2)
2462 self.assertEqual(t2, t2)
2463
2464 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2465 class Naive(tzinfo):
2466 def utcoffset(self, dt): return None
2467 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2468 self.assertRaises(TypeError, lambda: t1 == t2)
2469 self.assertEqual(t2, t2)
2470
2471 # OTOH, it's OK to compare two of these mixing the two ways of being
2472 # naive.
2473 t1 = self.theclass(5, 6, 7)
2474 self.assertEqual(t1, t2)
2475
2476 # Try a bogus uctoffset.
2477 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002478 def utcoffset(self, dt):
2479 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002480 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2481 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002482 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002483
Tim Peters2a799bf2002-12-16 20:18:38 +00002484 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002485 # Try one without a tzinfo.
2486 args = 6, 7, 23, 20, 59, 1, 64**2
2487 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002488 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002489 green = pickler.dumps(orig, proto)
2490 derived = unpickler.loads(green)
2491 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002492
2493 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002494 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002495 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002496 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002497 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002498 green = pickler.dumps(orig, proto)
2499 derived = unpickler.loads(green)
2500 self.assertEqual(orig, derived)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002501 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002502 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2503 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002504
2505 def test_extreme_hashes(self):
2506 # If an attempt is made to hash these via subtracting the offset
2507 # then hashing a datetime object, OverflowError results. The
2508 # Python implementation used to blow up here.
2509 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2510 hash(t)
2511 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2512 tzinfo=FixedOffset(-1439, ""))
2513 hash(t)
2514
2515 # OTOH, an OOB offset should blow up.
2516 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2517 self.assertRaises(ValueError, hash, t)
2518
2519 def test_zones(self):
2520 est = FixedOffset(-300, "EST")
2521 utc = FixedOffset(0, "UTC")
2522 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002523 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2524 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2525 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002526 self.assertEqual(t1.tzinfo, est)
2527 self.assertEqual(t2.tzinfo, utc)
2528 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002529 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2530 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2531 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002532 self.assertEqual(t1.tzname(), "EST")
2533 self.assertEqual(t2.tzname(), "UTC")
2534 self.assertEqual(t3.tzname(), "MET")
2535 self.assertEqual(hash(t1), hash(t2))
2536 self.assertEqual(hash(t1), hash(t3))
2537 self.assertEqual(hash(t2), hash(t3))
2538 self.assertEqual(t1, t2)
2539 self.assertEqual(t1, t3)
2540 self.assertEqual(t2, t3)
2541 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2542 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2543 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002544 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002545 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2546 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2547 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2548
2549 def test_combine(self):
2550 met = FixedOffset(60, "MET")
2551 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002552 tz = time(18, 45, 3, 1234, tzinfo=met)
2553 dt = datetime.combine(d, tz)
2554 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002555 tzinfo=met))
2556
2557 def test_extract(self):
2558 met = FixedOffset(60, "MET")
2559 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2560 self.assertEqual(dt.date(), date(2002, 3, 4))
2561 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002562 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002563
2564 def test_tz_aware_arithmetic(self):
2565 import random
2566
2567 now = self.theclass.now()
2568 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002569 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002570 nowaware = self.theclass.combine(now.date(), timeaware)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002571 self.assertIs(nowaware.tzinfo, tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002572 self.assertEqual(nowaware.timetz(), timeaware)
2573
2574 # Can't mix aware and non-aware.
2575 self.assertRaises(TypeError, lambda: now - nowaware)
2576 self.assertRaises(TypeError, lambda: nowaware - now)
2577
Tim Peters0bf60bd2003-01-08 20:40:01 +00002578 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002579 self.assertRaises(TypeError, lambda: now + nowaware)
2580 self.assertRaises(TypeError, lambda: nowaware + now)
2581 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2582
2583 # Subtracting should yield 0.
2584 self.assertEqual(now - now, timedelta(0))
2585 self.assertEqual(nowaware - nowaware, timedelta(0))
2586
2587 # Adding a delta should preserve tzinfo.
2588 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2589 nowawareplus = nowaware + delta
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002590 self.assertIs(nowaware.tzinfo, tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002591 nowawareplus2 = delta + nowaware
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002592 self.assertIs(nowawareplus2.tzinfo, tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002593 self.assertEqual(nowawareplus, nowawareplus2)
2594
2595 # that - delta should be what we started with, and that - what we
2596 # started with should be delta.
2597 diff = nowawareplus - delta
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002598 self.assertIs(diff.tzinfo, tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002599 self.assertEqual(nowaware, diff)
2600 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2601 self.assertEqual(nowawareplus - nowaware, delta)
2602
2603 # Make up a random timezone.
2604 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002605 # Attach it to nowawareplus.
2606 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002607 self.assertIs(nowawareplus.tzinfo, tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002608 # Make sure the difference takes the timezone adjustments into account.
2609 got = nowaware - nowawareplus
2610 # Expected: (nowaware base - nowaware offset) -
2611 # (nowawareplus base - nowawareplus offset) =
2612 # (nowaware base - nowawareplus base) +
2613 # (nowawareplus offset - nowaware offset) =
2614 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002615 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002616 self.assertEqual(got, expected)
2617
2618 # Try max possible difference.
2619 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2620 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2621 tzinfo=FixedOffset(-1439, "max"))
2622 maxdiff = max - min
2623 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2624 timedelta(minutes=2*1439))
2625
2626 def test_tzinfo_now(self):
2627 meth = self.theclass.now
2628 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2629 base = meth()
2630 # Try with and without naming the keyword.
2631 off42 = FixedOffset(42, "42")
2632 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002633 again = meth(tz=off42)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002634 self.assertIs(another.tzinfo, again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002635 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002636 # Bad argument with and w/o naming the keyword.
2637 self.assertRaises(TypeError, meth, 16)
2638 self.assertRaises(TypeError, meth, tzinfo=16)
2639 # Bad keyword name.
2640 self.assertRaises(TypeError, meth, tinfo=off42)
2641 # Too many args.
2642 self.assertRaises(TypeError, meth, off42, off42)
2643
Tim Peters10cadce2003-01-23 19:58:02 +00002644 # We don't know which time zone we're in, and don't have a tzinfo
2645 # class to represent it, so seeing whether a tz argument actually
2646 # does a conversion is tricky.
2647 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2648 utc = FixedOffset(0, "utc", 0)
2649 for dummy in range(3):
2650 now = datetime.now(weirdtz)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002651 self.assertIs(now.tzinfo, weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002652 utcnow = datetime.utcnow().replace(tzinfo=utc)
2653 now2 = utcnow.astimezone(weirdtz)
2654 if abs(now - now2) < timedelta(seconds=30):
2655 break
2656 # Else the code is broken, or more than 30 seconds passed between
2657 # calls; assuming the latter, just try again.
2658 else:
2659 # Three strikes and we're out.
2660 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2661
Tim Peters2a799bf2002-12-16 20:18:38 +00002662 def test_tzinfo_fromtimestamp(self):
2663 import time
2664 meth = self.theclass.fromtimestamp
2665 ts = time.time()
2666 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2667 base = meth(ts)
2668 # Try with and without naming the keyword.
2669 off42 = FixedOffset(42, "42")
2670 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002671 again = meth(ts, tz=off42)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002672 self.assertIs(another.tzinfo, again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002673 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002674 # Bad argument with and w/o naming the keyword.
2675 self.assertRaises(TypeError, meth, ts, 16)
2676 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2677 # Bad keyword name.
2678 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2679 # Too many args.
2680 self.assertRaises(TypeError, meth, ts, off42, off42)
2681 # Too few args.
2682 self.assertRaises(TypeError, meth)
2683
Tim Peters2a44a8d2003-01-23 20:53:10 +00002684 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002685 timestamp = 1000000000
2686 utcdatetime = datetime.utcfromtimestamp(timestamp)
2687 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2688 # But on some flavor of Mac, it's nowhere near that. So we can't have
2689 # any idea here what time that actually is, we can only test that
2690 # relative changes match.
2691 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2692 tz = FixedOffset(utcoffset, "tz", 0)
2693 expected = utcdatetime + utcoffset
2694 got = datetime.fromtimestamp(timestamp, tz)
2695 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002696
Tim Peters2a799bf2002-12-16 20:18:38 +00002697 def test_tzinfo_utcnow(self):
2698 meth = self.theclass.utcnow
2699 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2700 base = meth()
2701 # Try with and without naming the keyword; for whatever reason,
2702 # utcnow() doesn't accept a tzinfo argument.
2703 off42 = FixedOffset(42, "42")
2704 self.assertRaises(TypeError, meth, off42)
2705 self.assertRaises(TypeError, meth, tzinfo=off42)
2706
2707 def test_tzinfo_utcfromtimestamp(self):
2708 import time
2709 meth = self.theclass.utcfromtimestamp
2710 ts = time.time()
2711 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2712 base = meth(ts)
2713 # Try with and without naming the keyword; for whatever reason,
2714 # utcfromtimestamp() doesn't accept a tzinfo argument.
2715 off42 = FixedOffset(42, "42")
2716 self.assertRaises(TypeError, meth, ts, off42)
2717 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2718
2719 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002720 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002721 # DST flag.
2722 class DST(tzinfo):
2723 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002724 if isinstance(dstvalue, int):
2725 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002726 self.dstvalue = dstvalue
2727 def dst(self, dt):
2728 return self.dstvalue
2729
2730 cls = self.theclass
2731 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2732 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2733 t = d.timetuple()
2734 self.assertEqual(1, t.tm_year)
2735 self.assertEqual(1, t.tm_mon)
2736 self.assertEqual(1, t.tm_mday)
2737 self.assertEqual(10, t.tm_hour)
2738 self.assertEqual(20, t.tm_min)
2739 self.assertEqual(30, t.tm_sec)
2740 self.assertEqual(0, t.tm_wday)
2741 self.assertEqual(1, t.tm_yday)
2742 self.assertEqual(flag, t.tm_isdst)
2743
2744 # dst() returns wrong type.
2745 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2746
2747 # dst() at the edge.
2748 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2749 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2750
2751 # dst() out of range.
2752 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2753 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2754
2755 def test_utctimetuple(self):
2756 class DST(tzinfo):
2757 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002758 if isinstance(dstvalue, int):
2759 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002760 self.dstvalue = dstvalue
2761 def dst(self, dt):
2762 return self.dstvalue
2763
2764 cls = self.theclass
2765 # This can't work: DST didn't implement utcoffset.
2766 self.assertRaises(NotImplementedError,
2767 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2768
2769 class UOFS(DST):
2770 def __init__(self, uofs, dofs=None):
2771 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002772 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002773 def utcoffset(self, dt):
2774 return self.uofs
2775
2776 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2777 # in effect for a UTC time.
2778 for dstvalue in -33, 33, 0, None:
2779 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2780 t = d.utctimetuple()
2781 self.assertEqual(d.year, t.tm_year)
2782 self.assertEqual(d.month, t.tm_mon)
2783 self.assertEqual(d.day, t.tm_mday)
2784 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2785 self.assertEqual(13, t.tm_min)
2786 self.assertEqual(d.second, t.tm_sec)
2787 self.assertEqual(d.weekday(), t.tm_wday)
2788 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2789 t.tm_yday)
2790 self.assertEqual(0, t.tm_isdst)
2791
2792 # At the edges, UTC adjustment can normalize into years out-of-range
2793 # for a datetime object. Ensure that a correct timetuple is
2794 # created anyway.
2795 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2796 # That goes back 1 minute less than a full day.
2797 t = tiny.utctimetuple()
2798 self.assertEqual(t.tm_year, MINYEAR-1)
2799 self.assertEqual(t.tm_mon, 12)
2800 self.assertEqual(t.tm_mday, 31)
2801 self.assertEqual(t.tm_hour, 0)
2802 self.assertEqual(t.tm_min, 1)
2803 self.assertEqual(t.tm_sec, 37)
2804 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2805 self.assertEqual(t.tm_isdst, 0)
2806
2807 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2808 # That goes forward 1 minute less than a full day.
2809 t = huge.utctimetuple()
2810 self.assertEqual(t.tm_year, MAXYEAR+1)
2811 self.assertEqual(t.tm_mon, 1)
2812 self.assertEqual(t.tm_mday, 1)
2813 self.assertEqual(t.tm_hour, 23)
2814 self.assertEqual(t.tm_min, 58)
2815 self.assertEqual(t.tm_sec, 37)
2816 self.assertEqual(t.tm_yday, 1)
2817 self.assertEqual(t.tm_isdst, 0)
2818
2819 def test_tzinfo_isoformat(self):
2820 zero = FixedOffset(0, "+00:00")
2821 plus = FixedOffset(220, "+03:40")
2822 minus = FixedOffset(-231, "-03:51")
2823 unknown = FixedOffset(None, "")
2824
2825 cls = self.theclass
2826 datestr = '0001-02-03'
2827 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002828 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002829 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2830 timestr = '04:05:59' + (us and '.987001' or '')
2831 ofsstr = ofs is not None and d.tzname() or ''
2832 tailstr = timestr + ofsstr
2833 iso = d.isoformat()
2834 self.assertEqual(iso, datestr + 'T' + tailstr)
2835 self.assertEqual(iso, d.isoformat('T'))
2836 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2837 self.assertEqual(str(d), datestr + ' ' + tailstr)
2838
Tim Peters12bf3392002-12-24 05:41:27 +00002839 def test_replace(self):
2840 cls = self.theclass
2841 z100 = FixedOffset(100, "+100")
2842 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2843 args = [1, 2, 3, 4, 5, 6, 7, z100]
2844 base = cls(*args)
2845 self.assertEqual(base, base.replace())
2846
2847 i = 0
2848 for name, newval in (("year", 2),
2849 ("month", 3),
2850 ("day", 4),
2851 ("hour", 5),
2852 ("minute", 6),
2853 ("second", 7),
2854 ("microsecond", 8),
2855 ("tzinfo", zm200)):
2856 newargs = args[:]
2857 newargs[i] = newval
2858 expected = cls(*newargs)
2859 got = base.replace(**{name: newval})
2860 self.assertEqual(expected, got)
2861 i += 1
2862
2863 # Ensure we can get rid of a tzinfo.
2864 self.assertEqual(base.tzname(), "+100")
2865 base2 = base.replace(tzinfo=None)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002866 self.assertIsNone(base2.tzinfo)
2867 self.assertIsNone(base2.tzname())
Tim Peters12bf3392002-12-24 05:41:27 +00002868
2869 # Ensure we can add one.
2870 base3 = base2.replace(tzinfo=z100)
2871 self.assertEqual(base, base3)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002872 self.assertIs(base.tzinfo, base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002873
2874 # Out of bounds.
2875 base = cls(2000, 2, 29)
2876 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002877
Tim Peters80475bb2002-12-25 07:40:55 +00002878 def test_more_astimezone(self):
2879 # The inherited test_astimezone covered some trivial and error cases.
2880 fnone = FixedOffset(None, "None")
2881 f44m = FixedOffset(44, "44")
2882 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2883
Tim Peters10cadce2003-01-23 19:58:02 +00002884 dt = self.theclass.now(tz=f44m)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002885 self.assertIs(dt.tzinfo, f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002886 # Replacing with degenerate tzinfo raises an exception.
2887 self.assertRaises(ValueError, dt.astimezone, fnone)
2888 # Ditto with None tz.
2889 self.assertRaises(TypeError, dt.astimezone, None)
2890 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002891 x = dt.astimezone(dt.tzinfo)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002892 self.assertIs(x.tzinfo, f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002893 self.assertEqual(x.date(), dt.date())
2894 self.assertEqual(x.time(), dt.time())
2895
2896 # Replacing with different tzinfo does adjust.
2897 got = dt.astimezone(fm5h)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002898 self.assertIs(got.tzinfo, fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002899 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2900 expected = dt - dt.utcoffset() # in effect, convert to UTC
2901 expected += fm5h.utcoffset(dt) # and from there to local time
2902 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2903 self.assertEqual(got.date(), expected.date())
2904 self.assertEqual(got.time(), expected.time())
2905 self.assertEqual(got.timetz(), expected.timetz())
Serhiy Storchaka1438b982013-11-17 13:03:21 +02002906 self.assertIs(got.tzinfo, expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002907 self.assertEqual(got, expected)
2908
Tim Peters4c0db782002-12-26 05:01:19 +00002909 def test_aware_subtract(self):
2910 cls = self.theclass
2911
Tim Peters60c76e42002-12-27 00:41:11 +00002912 # Ensure that utcoffset() is ignored when the operands have the
2913 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002914 class OperandDependentOffset(tzinfo):
2915 def utcoffset(self, t):
2916 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002917 # d0 and d1 equal after adjustment
2918 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002919 else:
Tim Peters397301e2003-01-02 21:28:08 +00002920 # d2 off in the weeds
2921 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002922
2923 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2924 d0 = base.replace(minute=3)
2925 d1 = base.replace(minute=9)
2926 d2 = base.replace(minute=11)
2927 for x in d0, d1, d2:
2928 for y in d0, d1, d2:
2929 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002930 expected = timedelta(minutes=x.minute - y.minute)
2931 self.assertEqual(got, expected)
2932
2933 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2934 # ignored.
2935 base = cls(8, 9, 10, 11, 12, 13, 14)
2936 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2937 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2938 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2939 for x in d0, d1, d2:
2940 for y in d0, d1, d2:
2941 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002942 if (x is d0 or x is d1) and (y is d0 or y is d1):
2943 expected = timedelta(0)
2944 elif x is y is d2:
2945 expected = timedelta(0)
2946 elif x is d2:
2947 expected = timedelta(minutes=(11-59)-0)
2948 else:
2949 assert y is d2
2950 expected = timedelta(minutes=0-(11-59))
2951 self.assertEqual(got, expected)
2952
Tim Peters60c76e42002-12-27 00:41:11 +00002953 def test_mixed_compare(self):
2954 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002955 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002956 self.assertEqual(t1, t2)
2957 t2 = t2.replace(tzinfo=None)
2958 self.assertEqual(t1, t2)
2959 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2960 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002961 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2962 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002963
Tim Peters0bf60bd2003-01-08 20:40:01 +00002964 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002965 class Varies(tzinfo):
2966 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002967 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002968 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002969 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002970 return self.offset
2971
2972 v = Varies()
2973 t1 = t2.replace(tzinfo=v)
2974 t2 = t2.replace(tzinfo=v)
2975 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2976 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2977 self.assertEqual(t1, t2)
2978
2979 # But if they're not identical, it isn't ignored.
2980 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002981 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002982
Tim Petersa98924a2003-05-17 05:55:19 +00002983 def test_subclass_datetimetz(self):
2984
2985 class C(self.theclass):
2986 theAnswer = 42
2987
2988 def __new__(cls, *args, **kws):
2989 temp = kws.copy()
2990 extra = temp.pop('extra')
2991 result = self.theclass.__new__(cls, *args, **temp)
2992 result.extra = extra
2993 return result
2994
2995 def newmeth(self, start):
2996 return start + self.hour + self.year
2997
2998 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2999
3000 dt1 = self.theclass(*args)
3001 dt2 = C(*args, **{'extra': 7})
3002
3003 self.assertEqual(dt2.__class__, C)
3004 self.assertEqual(dt2.theAnswer, 42)
3005 self.assertEqual(dt2.extra, 7)
3006 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3007 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3008
Tim Peters621818b2002-12-29 23:44:49 +00003009# Pain to set up DST-aware tzinfo classes.
3010
3011def first_sunday_on_or_after(dt):
3012 days_to_go = 6 - dt.weekday()
3013 if days_to_go:
3014 dt += timedelta(days_to_go)
3015 return dt
3016
3017ZERO = timedelta(0)
3018HOUR = timedelta(hours=1)
3019DAY = timedelta(days=1)
3020# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3021DSTSTART = datetime(1, 4, 1, 2)
3022# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003023# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3024# being standard time on that day, there is no spelling in local time of
3025# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3026DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003027
3028class USTimeZone(tzinfo):
3029
3030 def __init__(self, hours, reprname, stdname, dstname):
3031 self.stdoffset = timedelta(hours=hours)
3032 self.reprname = reprname
3033 self.stdname = stdname
3034 self.dstname = dstname
3035
3036 def __repr__(self):
3037 return self.reprname
3038
3039 def tzname(self, dt):
3040 if self.dst(dt):
3041 return self.dstname
3042 else:
3043 return self.stdname
3044
3045 def utcoffset(self, dt):
3046 return self.stdoffset + self.dst(dt)
3047
3048 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003049 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003050 # An exception instead may be sensible here, in one or more of
3051 # the cases.
3052 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003053 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003054
3055 # Find first Sunday in April.
3056 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3057 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3058
3059 # Find last Sunday in October.
3060 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3061 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3062
Tim Peters621818b2002-12-29 23:44:49 +00003063 # Can't compare naive to aware objects, so strip the timezone from
3064 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003065 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003066 return HOUR
3067 else:
3068 return ZERO
3069
Tim Peters521fc152002-12-31 17:36:56 +00003070Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3071Central = USTimeZone(-6, "Central", "CST", "CDT")
3072Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3073Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003074utc_real = FixedOffset(0, "UTC", 0)
3075# For better test coverage, we want another flavor of UTC that's west of
3076# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003077utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003078
3079class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003080 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003081 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003082 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003083
Tim Peters0bf60bd2003-01-08 20:40:01 +00003084 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003085
Tim Peters521fc152002-12-31 17:36:56 +00003086 # Check a time that's inside DST.
3087 def checkinside(self, dt, tz, utc, dston, dstoff):
3088 self.assertEqual(dt.dst(), HOUR)
3089
3090 # Conversion to our own timezone is always an identity.
3091 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003092
3093 asutc = dt.astimezone(utc)
3094 there_and_back = asutc.astimezone(tz)
3095
3096 # Conversion to UTC and back isn't always an identity here,
3097 # because there are redundant spellings (in local time) of
3098 # UTC time when DST begins: the clock jumps from 1:59:59
3099 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3100 # make sense then. The classes above treat 2:MM:SS as
3101 # daylight time then (it's "after 2am"), really an alias
3102 # for 1:MM:SS standard time. The latter form is what
3103 # conversion back from UTC produces.
3104 if dt.date() == dston.date() and dt.hour == 2:
3105 # We're in the redundant hour, and coming back from
3106 # UTC gives the 1:MM:SS standard-time spelling.
3107 self.assertEqual(there_and_back + HOUR, dt)
3108 # Although during was considered to be in daylight
3109 # time, there_and_back is not.
3110 self.assertEqual(there_and_back.dst(), ZERO)
3111 # They're the same times in UTC.
3112 self.assertEqual(there_and_back.astimezone(utc),
3113 dt.astimezone(utc))
3114 else:
3115 # We're not in the redundant hour.
3116 self.assertEqual(dt, there_and_back)
3117
Tim Peters327098a2003-01-20 22:54:38 +00003118 # Because we have a redundant spelling when DST begins, there is
Ezio Melottic2077b02011-03-16 12:34:31 +02003119 # (unfortunately) an hour when DST ends that can't be spelled at all in
Tim Peters327098a2003-01-20 22:54:38 +00003120 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3121 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3122 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3123 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3124 # expressed in local time. Nevertheless, we want conversion back
3125 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003126 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003127 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003128 if dt.date() == dstoff.date() and dt.hour == 0:
3129 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003130 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003131 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3132 nexthour_utc += HOUR
3133 nexthour_tz = nexthour_utc.astimezone(tz)
3134 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003135 else:
Tim Peters327098a2003-01-20 22:54:38 +00003136 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003137
3138 # Check a time that's outside DST.
3139 def checkoutside(self, dt, tz, utc):
3140 self.assertEqual(dt.dst(), ZERO)
3141
3142 # Conversion to our own timezone is always an identity.
3143 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003144
3145 # Converting to UTC and back is an identity too.
3146 asutc = dt.astimezone(utc)
3147 there_and_back = asutc.astimezone(tz)
3148 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003149
Tim Peters1024bf82002-12-30 17:09:40 +00003150 def convert_between_tz_and_utc(self, tz, utc):
3151 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003152 # Because 1:MM on the day DST ends is taken as being standard time,
3153 # there is no spelling in tz for the last hour of daylight time.
3154 # For purposes of the test, the last hour of DST is 0:MM, which is
3155 # taken as being daylight time (and 1:MM is taken as being standard
3156 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003157 dstoff = self.dstoff.replace(tzinfo=tz)
3158 for delta in (timedelta(weeks=13),
3159 DAY,
3160 HOUR,
3161 timedelta(minutes=1),
3162 timedelta(microseconds=1)):
3163
Tim Peters521fc152002-12-31 17:36:56 +00003164 self.checkinside(dston, tz, utc, dston, dstoff)
3165 for during in dston + delta, dstoff - delta:
3166 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003167
Tim Peters521fc152002-12-31 17:36:56 +00003168 self.checkoutside(dstoff, tz, utc)
3169 for outside in dston - delta, dstoff + delta:
3170 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003171
Tim Peters621818b2002-12-29 23:44:49 +00003172 def test_easy(self):
3173 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003174 self.convert_between_tz_and_utc(Eastern, utc_real)
3175 self.convert_between_tz_and_utc(Pacific, utc_real)
3176 self.convert_between_tz_and_utc(Eastern, utc_fake)
3177 self.convert_between_tz_and_utc(Pacific, utc_fake)
3178 # The next is really dancing near the edge. It works because
3179 # Pacific and Eastern are far enough apart that their "problem
3180 # hours" don't overlap.
3181 self.convert_between_tz_and_utc(Eastern, Pacific)
3182 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003183 # OTOH, these fail! Don't enable them. The difficulty is that
3184 # the edge case tests assume that every hour is representable in
3185 # the "utc" class. This is always true for a fixed-offset tzinfo
3186 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3187 # For these adjacent DST-aware time zones, the range of time offsets
3188 # tested ends up creating hours in the one that aren't representable
3189 # in the other. For the same reason, we would see failures in the
3190 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3191 # offset deltas in convert_between_tz_and_utc().
3192 #
3193 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3194 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003195
Tim Petersf3615152003-01-01 21:51:37 +00003196 def test_tricky(self):
3197 # 22:00 on day before daylight starts.
3198 fourback = self.dston - timedelta(hours=4)
3199 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003200 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003201 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3202 # 2", we should get the 3 spelling.
3203 # If we plug 22:00 the day before into Eastern, it "looks like std
3204 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3205 # to 22:00 lands on 2:00, which makes no sense in local time (the
3206 # local clock jumps from 1 to 3). The point here is to make sure we
3207 # get the 3 spelling.
3208 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003209 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003210 self.assertEqual(expected, got)
3211
3212 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3213 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003214 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003215 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3216 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3217 # spelling.
3218 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003219 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003220 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003221
Tim Petersadf64202003-01-04 06:03:15 +00003222 # Now on the day DST ends, we want "repeat an hour" behavior.
3223 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3224 # EST 23:MM 0:MM 1:MM 2:MM
3225 # EDT 0:MM 1:MM 2:MM 3:MM
3226 # wall 0:MM 1:MM 1:MM 2:MM against these
3227 for utc in utc_real, utc_fake:
3228 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003229 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003230 # Convert that to UTC.
3231 first_std_hour -= tz.utcoffset(None)
3232 # Adjust for possibly fake UTC.
3233 asutc = first_std_hour + utc.utcoffset(None)
3234 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3235 # tz=Eastern.
3236 asutcbase = asutc.replace(tzinfo=utc)
3237 for tzhour in (0, 1, 1, 2):
3238 expectedbase = self.dstoff.replace(hour=tzhour)
3239 for minute in 0, 30, 59:
3240 expected = expectedbase.replace(minute=minute)
3241 asutc = asutcbase.replace(minute=minute)
3242 astz = asutc.astimezone(tz)
3243 self.assertEqual(astz.replace(tzinfo=None), expected)
3244 asutcbase += HOUR
3245
3246
Tim Peters710fb152003-01-02 19:35:54 +00003247 def test_bogus_dst(self):
3248 class ok(tzinfo):
3249 def utcoffset(self, dt): return HOUR
3250 def dst(self, dt): return HOUR
3251
3252 now = self.theclass.now().replace(tzinfo=utc_real)
3253 # Doesn't blow up.
3254 now.astimezone(ok())
3255
3256 # Does blow up.
3257 class notok(ok):
3258 def dst(self, dt): return None
3259 self.assertRaises(ValueError, now.astimezone, notok())
3260
Tim Peters52dcce22003-01-23 16:36:11 +00003261 def test_fromutc(self):
3262 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3263 now = datetime.utcnow().replace(tzinfo=utc_real)
3264 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3265 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3266 enow = Eastern.fromutc(now) # doesn't blow up
3267 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3268 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3269 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3270
3271 # Always converts UTC to standard time.
3272 class FauxUSTimeZone(USTimeZone):
3273 def fromutc(self, dt):
3274 return dt + self.stdoffset
3275 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3276
3277 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3278 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3279 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3280
3281 # Check around DST start.
3282 start = self.dston.replace(hour=4, tzinfo=Eastern)
3283 fstart = start.replace(tzinfo=FEastern)
3284 for wall in 23, 0, 1, 3, 4, 5:
3285 expected = start.replace(hour=wall)
3286 if wall == 23:
3287 expected -= timedelta(days=1)
3288 got = Eastern.fromutc(start)
3289 self.assertEqual(expected, got)
3290
3291 expected = fstart + FEastern.stdoffset
3292 got = FEastern.fromutc(fstart)
3293 self.assertEqual(expected, got)
3294
3295 # Ensure astimezone() calls fromutc() too.
3296 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3297 self.assertEqual(expected, got)
3298
3299 start += HOUR
3300 fstart += HOUR
3301
3302 # Check around DST end.
3303 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3304 fstart = start.replace(tzinfo=FEastern)
3305 for wall in 0, 1, 1, 2, 3, 4:
3306 expected = start.replace(hour=wall)
3307 got = Eastern.fromutc(start)
3308 self.assertEqual(expected, got)
3309
3310 expected = fstart + FEastern.stdoffset
3311 got = FEastern.fromutc(fstart)
3312 self.assertEqual(expected, got)
3313
3314 # Ensure astimezone() calls fromutc() too.
3315 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3316 self.assertEqual(expected, got)
3317
3318 start += HOUR
3319 fstart += HOUR
3320
Tim Peters710fb152003-01-02 19:35:54 +00003321
Tim Peters528ca532004-09-16 01:30:50 +00003322#############################################################################
3323# oddballs
3324
3325class Oddballs(unittest.TestCase):
3326
3327 def test_bug_1028306(self):
3328 # Trying to compare a date to a datetime should act like a mixed-
3329 # type comparison, despite that datetime is a subclass of date.
3330 as_date = date.today()
3331 as_datetime = datetime.combine(as_date, time())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003332 self.assertTrue(as_date != as_datetime)
3333 self.assertTrue(as_datetime != as_date)
Serhiy Storchaka1438b982013-11-17 13:03:21 +02003334 self.assertFalse(as_date == as_datetime)
3335 self.assertFalse(as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003336 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3337 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3338 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3339 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3340 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3341 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3342 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3343 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3344
3345 # Neverthelss, comparison should work with the base-class (date)
3346 # projection if use of a date method is forced.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003347 self.assertTrue(as_date.__eq__(as_datetime))
Tim Peters528ca532004-09-16 01:30:50 +00003348 different_day = (as_date.day + 1) % 20 + 1
Serhiy Storchaka1438b982013-11-17 13:03:21 +02003349 self.assertFalse(as_date.__eq__(as_datetime.replace(day=different_day)))
Tim Peters528ca532004-09-16 01:30:50 +00003350
3351 # And date should compare with other subclasses of date. If a
3352 # subclass wants to stop this, it's up to the subclass to do so.
3353 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3354 self.assertEqual(as_date, date_sc)
3355 self.assertEqual(date_sc, as_date)
3356
3357 # Ditto for datetimes.
3358 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3359 as_date.day, 0, 0, 0)
3360 self.assertEqual(as_datetime, datetime_sc)
3361 self.assertEqual(datetime_sc, as_datetime)
3362
Tim Peters2a799bf2002-12-16 20:18:38 +00003363def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003364 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003365
3366if __name__ == "__main__":
3367 test_main()