blob: 671c259a3ace4480180eeb5d6d162c7e89e165d5 [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)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000104 self.assertTrue(type(orig) is 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)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000108 self.assertTrue(type(derived) is 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#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000127# Base clase for testing a particular aspect of timedelta, time, date and
128# 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
234 def test_disallowed_computations(self):
235 a = timedelta(42)
236
237 # Add/sub ints, longs, floats should be illegal
238 for i in 1, 1L, 1.0:
239 self.assertRaises(TypeError, lambda: a+i)
240 self.assertRaises(TypeError, lambda: a-i)
241 self.assertRaises(TypeError, lambda: i+a)
242 self.assertRaises(TypeError, lambda: i-a)
243
244 # Mul/div by float isn't supported.
245 x = 2.3
246 self.assertRaises(TypeError, lambda: a*x)
247 self.assertRaises(TypeError, lambda: x*a)
248 self.assertRaises(TypeError, lambda: a/x)
249 self.assertRaises(TypeError, lambda: x/a)
250 self.assertRaises(TypeError, lambda: a // x)
251 self.assertRaises(TypeError, lambda: x // a)
252
Andrew M. Kuchling081bb452008-10-03 16:42:52 +0000253 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000254 # Division by zero doesn't make sense.
255 for zero in 0, 0L:
256 self.assertRaises(TypeError, lambda: zero // a)
257 self.assertRaises(ZeroDivisionError, lambda: a // zero)
258
259 def test_basic_attributes(self):
260 days, seconds, us = 1, 7, 31
261 td = timedelta(days, seconds, us)
262 self.assertEqual(td.days, days)
263 self.assertEqual(td.seconds, seconds)
264 self.assertEqual(td.microseconds, us)
265
Antoine Pitroubcfaf802009-11-25 22:59:36 +0000266 def test_total_seconds(self):
267 td = timedelta(days=365)
268 self.assertEqual(td.total_seconds(), 31536000.0)
269 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
270 td = timedelta(seconds=total_seconds)
271 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson7000e9e2010-05-09 09:30:06 +0000272 # Issue8644: Test that td.total_seconds() has the same
273 # accuracy as td / timedelta(seconds=1).
274 for ms in [-1, -2, -123]:
275 td = timedelta(microseconds=ms)
276 self.assertEqual(td.total_seconds(),
277 ((24*3600*td.days + td.seconds)*10**6
278 + td.microseconds)/10**6)
Antoine Pitroubcfaf802009-11-25 22:59:36 +0000279
Tim Peters2a799bf2002-12-16 20:18:38 +0000280 def test_carries(self):
281 t1 = timedelta(days=100,
282 weeks=-7,
283 hours=-24*(100-49),
284 minutes=-3,
285 seconds=12,
286 microseconds=(3*60 - 12) * 1e6 + 1)
287 t2 = timedelta(microseconds=1)
288 self.assertEqual(t1, t2)
289
290 def test_hash_equality(self):
291 t1 = timedelta(days=100,
292 weeks=-7,
293 hours=-24*(100-49),
294 minutes=-3,
295 seconds=12,
296 microseconds=(3*60 - 12) * 1000000)
297 t2 = timedelta()
298 self.assertEqual(hash(t1), hash(t2))
299
300 t1 += timedelta(weeks=7)
301 t2 += timedelta(days=7*7)
302 self.assertEqual(t1, t2)
303 self.assertEqual(hash(t1), hash(t2))
304
305 d = {t1: 1}
306 d[t2] = 2
307 self.assertEqual(len(d), 1)
308 self.assertEqual(d[t1], 2)
309
310 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000311 args = 12, 34, 56
312 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000313 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000314 green = pickler.dumps(orig, proto)
315 derived = unpickler.loads(green)
316 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000317
318 def test_compare(self):
319 t1 = timedelta(2, 3, 4)
320 t2 = timedelta(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000321 self.assertTrue(t1 == t2)
322 self.assertTrue(t1 <= t2)
323 self.assertTrue(t1 >= t2)
324 self.assertTrue(not t1 != t2)
325 self.assertTrue(not t1 < t2)
326 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000327 self.assertEqual(cmp(t1, t2), 0)
328 self.assertEqual(cmp(t2, t1), 0)
329
330 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
331 t2 = timedelta(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000332 self.assertTrue(t1 < t2)
333 self.assertTrue(t2 > t1)
334 self.assertTrue(t1 <= t2)
335 self.assertTrue(t2 >= t1)
336 self.assertTrue(t1 != t2)
337 self.assertTrue(t2 != t1)
338 self.assertTrue(not t1 == t2)
339 self.assertTrue(not t2 == t1)
340 self.assertTrue(not t1 > t2)
341 self.assertTrue(not t2 < t1)
342 self.assertTrue(not t1 >= t2)
343 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000344 self.assertEqual(cmp(t1, t2), -1)
345 self.assertEqual(cmp(t2, t1), 1)
346
Tim Peters68124bb2003-02-08 03:46:31 +0000347 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000348 self.assertEqual(t1 == badarg, False)
349 self.assertEqual(t1 != badarg, True)
350 self.assertEqual(badarg == t1, False)
351 self.assertEqual(badarg != t1, True)
352
Tim Peters2a799bf2002-12-16 20:18:38 +0000353 self.assertRaises(TypeError, lambda: t1 <= badarg)
354 self.assertRaises(TypeError, lambda: t1 < badarg)
355 self.assertRaises(TypeError, lambda: t1 > badarg)
356 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000357 self.assertRaises(TypeError, lambda: badarg <= t1)
358 self.assertRaises(TypeError, lambda: badarg < t1)
359 self.assertRaises(TypeError, lambda: badarg > t1)
360 self.assertRaises(TypeError, lambda: badarg >= t1)
361
362 def test_str(self):
363 td = timedelta
364 eq = self.assertEqual
365
366 eq(str(td(1)), "1 day, 0:00:00")
367 eq(str(td(-1)), "-1 day, 0:00:00")
368 eq(str(td(2)), "2 days, 0:00:00")
369 eq(str(td(-2)), "-2 days, 0:00:00")
370
371 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
372 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
373 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
374 "-210 days, 23:12:34")
375
376 eq(str(td(milliseconds=1)), "0:00:00.001000")
377 eq(str(td(microseconds=3)), "0:00:00.000003")
378
379 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
380 microseconds=999999)),
381 "999999999 days, 23:59:59.999999")
382
383 def test_roundtrip(self):
384 for td in (timedelta(days=999999999, hours=23, minutes=59,
385 seconds=59, microseconds=999999),
386 timedelta(days=-999999999),
387 timedelta(days=1, seconds=2, microseconds=3)):
388
389 # Verify td -> string -> td identity.
390 s = repr(td)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000391 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000392 s = s[9:]
393 td2 = eval(s)
394 self.assertEqual(td, td2)
395
396 # Verify identity via reconstructing from pieces.
397 td2 = timedelta(td.days, td.seconds, td.microseconds)
398 self.assertEqual(td, td2)
399
400 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000401 self.assertIsInstance(timedelta.min, timedelta)
402 self.assertIsInstance(timedelta.max, timedelta)
403 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000404 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000405 self.assertEqual(timedelta.min, timedelta(-999999999))
406 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
407 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
408
409 def test_overflow(self):
410 tiny = timedelta.resolution
411
412 td = timedelta.min + tiny
413 td -= tiny # no problem
414 self.assertRaises(OverflowError, td.__sub__, tiny)
415 self.assertRaises(OverflowError, td.__add__, -tiny)
416
417 td = timedelta.max - tiny
418 td += tiny # no problem
419 self.assertRaises(OverflowError, td.__add__, tiny)
420 self.assertRaises(OverflowError, td.__sub__, -tiny)
421
422 self.assertRaises(OverflowError, lambda: -timedelta.max)
423
424 def test_microsecond_rounding(self):
425 td = timedelta
426 eq = self.assertEqual
427
428 # Single-field rounding.
429 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
430 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
431 eq(td(milliseconds=0.6/1000), td(microseconds=1))
432 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
433
434 # Rounding due to contributions from more than one field.
435 us_per_hour = 3600e6
436 us_per_day = us_per_hour * 24
437 eq(td(days=.4/us_per_day), td(0))
438 eq(td(hours=.2/us_per_hour), td(0))
439 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
440
441 eq(td(days=-.4/us_per_day), td(0))
442 eq(td(hours=-.2/us_per_hour), td(0))
443 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
444
445 def test_massive_normalization(self):
446 td = timedelta(microseconds=-1)
447 self.assertEqual((td.days, td.seconds, td.microseconds),
448 (-1, 24*3600-1, 999999))
449
450 def test_bool(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000451 self.assertTrue(timedelta(1))
452 self.assertTrue(timedelta(0, 1))
453 self.assertTrue(timedelta(0, 0, 1))
454 self.assertTrue(timedelta(microseconds=1))
455 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000456
Tim Petersb0c854d2003-05-17 15:57:00 +0000457 def test_subclass_timedelta(self):
458
459 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000460 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000461 def from_td(td):
462 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000463
464 def as_hours(self):
465 sum = (self.days * 24 +
466 self.seconds / 3600.0 +
467 self.microseconds / 3600e6)
468 return round(sum)
469
470 t1 = T(days=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000471 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000472 self.assertEqual(t1.as_hours(), 24)
473
474 t2 = T(days=-1, seconds=-3600)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000475 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000476 self.assertEqual(t2.as_hours(), -25)
477
478 t3 = t1 + t2
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000479 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000480 t4 = T.from_td(t3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000481 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000482 self.assertEqual(t3.days, t4.days)
483 self.assertEqual(t3.seconds, t4.seconds)
484 self.assertEqual(t3.microseconds, t4.microseconds)
485 self.assertEqual(str(t3), str(t4))
486 self.assertEqual(t4.as_hours(), -1)
487
Tim Peters2a799bf2002-12-16 20:18:38 +0000488#############################################################################
489# date tests
490
491class TestDateOnly(unittest.TestCase):
492 # Tests here won't pass if also run on datetime objects, so don't
493 # subclass this to test datetimes too.
494
495 def test_delta_non_days_ignored(self):
496 dt = date(2000, 1, 2)
497 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
498 microseconds=5)
499 days = timedelta(delta.days)
500 self.assertEqual(days, timedelta(1))
501
502 dt2 = dt + delta
503 self.assertEqual(dt2, dt + days)
504
505 dt2 = delta + dt
506 self.assertEqual(dt2, dt + days)
507
508 dt2 = dt - delta
509 self.assertEqual(dt2, dt - days)
510
511 delta = -delta
512 days = timedelta(delta.days)
513 self.assertEqual(days, timedelta(-2))
514
515 dt2 = dt + delta
516 self.assertEqual(dt2, dt + days)
517
518 dt2 = delta + dt
519 self.assertEqual(dt2, dt + days)
520
521 dt2 = dt - delta
522 self.assertEqual(dt2, dt - days)
523
Tim Peters604c0132004-06-07 23:04:33 +0000524class SubclassDate(date):
525 sub_var = 1
526
Collin Winterc2898c52007-04-25 17:29:52 +0000527class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000528 # Tests here should pass for both dates and datetimes, except for a
529 # few tests that TestDateTime overrides.
530
531 theclass = date
532
533 def test_basic_attributes(self):
534 dt = self.theclass(2002, 3, 1)
535 self.assertEqual(dt.year, 2002)
536 self.assertEqual(dt.month, 3)
537 self.assertEqual(dt.day, 1)
538
539 def test_roundtrip(self):
540 for dt in (self.theclass(1, 2, 3),
541 self.theclass.today()):
542 # Verify dt -> string -> date identity.
543 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000544 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000545 s = s[9:]
546 dt2 = eval(s)
547 self.assertEqual(dt, dt2)
548
549 # Verify identity via reconstructing from pieces.
550 dt2 = self.theclass(dt.year, dt.month, dt.day)
551 self.assertEqual(dt, dt2)
552
553 def test_ordinal_conversions(self):
554 # Check some fixed values.
555 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
556 (1, 12, 31, 365),
557 (2, 1, 1, 366),
558 # first example from "Calendrical Calculations"
559 (1945, 11, 12, 710347)]:
560 d = self.theclass(y, m, d)
561 self.assertEqual(n, d.toordinal())
562 fromord = self.theclass.fromordinal(n)
563 self.assertEqual(d, fromord)
564 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000565 # if we're checking something fancier than a date, verify
566 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000567 self.assertEqual(fromord.hour, 0)
568 self.assertEqual(fromord.minute, 0)
569 self.assertEqual(fromord.second, 0)
570 self.assertEqual(fromord.microsecond, 0)
571
Tim Peters0bf60bd2003-01-08 20:40:01 +0000572 # Check first and last days of year spottily across the whole
573 # range of years supported.
574 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000575 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
576 d = self.theclass(year, 1, 1)
577 n = d.toordinal()
578 d2 = self.theclass.fromordinal(n)
579 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000580 # Verify that moving back a day gets to the end of year-1.
581 if year > 1:
582 d = self.theclass.fromordinal(n-1)
583 d2 = self.theclass(year-1, 12, 31)
584 self.assertEqual(d, d2)
585 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000586
587 # Test every day in a leap-year and a non-leap year.
588 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
589 for year, isleap in (2000, True), (2002, False):
590 n = self.theclass(year, 1, 1).toordinal()
591 for month, maxday in zip(range(1, 13), dim):
592 if month == 2 and isleap:
593 maxday += 1
594 for day in range(1, maxday+1):
595 d = self.theclass(year, month, day)
596 self.assertEqual(d.toordinal(), n)
597 self.assertEqual(d, self.theclass.fromordinal(n))
598 n += 1
599
600 def test_extreme_ordinals(self):
601 a = self.theclass.min
602 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
603 aord = a.toordinal()
604 b = a.fromordinal(aord)
605 self.assertEqual(a, b)
606
607 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
608
609 b = a + timedelta(days=1)
610 self.assertEqual(b.toordinal(), aord + 1)
611 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
612
613 a = self.theclass.max
614 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
615 aord = a.toordinal()
616 b = a.fromordinal(aord)
617 self.assertEqual(a, b)
618
619 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
620
621 b = a - timedelta(days=1)
622 self.assertEqual(b.toordinal(), aord - 1)
623 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
624
625 def test_bad_constructor_arguments(self):
626 # bad years
627 self.theclass(MINYEAR, 1, 1) # no exception
628 self.theclass(MAXYEAR, 1, 1) # no exception
629 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
630 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
631 # bad months
632 self.theclass(2000, 1, 1) # no exception
633 self.theclass(2000, 12, 1) # no exception
634 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
635 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
636 # bad days
637 self.theclass(2000, 2, 29) # no exception
638 self.theclass(2004, 2, 29) # no exception
639 self.theclass(2400, 2, 29) # no exception
640 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
641 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
642 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
643 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
644 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
645 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
646
647 def test_hash_equality(self):
648 d = self.theclass(2000, 12, 31)
649 # same thing
650 e = self.theclass(2000, 12, 31)
651 self.assertEqual(d, e)
652 self.assertEqual(hash(d), hash(e))
653
654 dic = {d: 1}
655 dic[e] = 2
656 self.assertEqual(len(dic), 1)
657 self.assertEqual(dic[d], 2)
658 self.assertEqual(dic[e], 2)
659
660 d = self.theclass(2001, 1, 1)
661 # same thing
662 e = self.theclass(2001, 1, 1)
663 self.assertEqual(d, e)
664 self.assertEqual(hash(d), hash(e))
665
666 dic = {d: 1}
667 dic[e] = 2
668 self.assertEqual(len(dic), 1)
669 self.assertEqual(dic[d], 2)
670 self.assertEqual(dic[e], 2)
671
672 def test_computations(self):
673 a = self.theclass(2002, 1, 31)
674 b = self.theclass(1956, 1, 31)
675
676 diff = a-b
677 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
678 self.assertEqual(diff.seconds, 0)
679 self.assertEqual(diff.microseconds, 0)
680
681 day = timedelta(1)
682 week = timedelta(7)
683 a = self.theclass(2002, 3, 2)
684 self.assertEqual(a + day, self.theclass(2002, 3, 3))
685 self.assertEqual(day + a, self.theclass(2002, 3, 3))
686 self.assertEqual(a - day, self.theclass(2002, 3, 1))
687 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
688 self.assertEqual(a + week, self.theclass(2002, 3, 9))
689 self.assertEqual(a - week, self.theclass(2002, 2, 23))
690 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
691 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
692 self.assertEqual((a + week) - a, week)
693 self.assertEqual((a + day) - a, day)
694 self.assertEqual((a - week) - a, -week)
695 self.assertEqual((a - day) - a, -day)
696 self.assertEqual(a - (a + week), -week)
697 self.assertEqual(a - (a + day), -day)
698 self.assertEqual(a - (a - week), week)
699 self.assertEqual(a - (a - day), day)
700
701 # Add/sub ints, longs, floats should be illegal
702 for i in 1, 1L, 1.0:
703 self.assertRaises(TypeError, lambda: a+i)
704 self.assertRaises(TypeError, lambda: a-i)
705 self.assertRaises(TypeError, lambda: i+a)
706 self.assertRaises(TypeError, lambda: i-a)
707
708 # delta - date is senseless.
709 self.assertRaises(TypeError, lambda: day - a)
710 # mixing date and (delta or date) via * or // is senseless
711 self.assertRaises(TypeError, lambda: day * a)
712 self.assertRaises(TypeError, lambda: a * day)
713 self.assertRaises(TypeError, lambda: day // a)
714 self.assertRaises(TypeError, lambda: a // day)
715 self.assertRaises(TypeError, lambda: a * a)
716 self.assertRaises(TypeError, lambda: a // a)
717 # date + date is senseless
718 self.assertRaises(TypeError, lambda: a + a)
719
720 def test_overflow(self):
721 tiny = self.theclass.resolution
722
723 dt = self.theclass.min + tiny
724 dt -= tiny # no problem
725 self.assertRaises(OverflowError, dt.__sub__, tiny)
726 self.assertRaises(OverflowError, dt.__add__, -tiny)
727
728 dt = self.theclass.max - tiny
729 dt += tiny # no problem
730 self.assertRaises(OverflowError, dt.__add__, tiny)
731 self.assertRaises(OverflowError, dt.__sub__, -tiny)
732
733 def test_fromtimestamp(self):
734 import time
735
736 # Try an arbitrary fixed value.
737 year, month, day = 1999, 9, 19
738 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
739 d = self.theclass.fromtimestamp(ts)
740 self.assertEqual(d.year, year)
741 self.assertEqual(d.month, month)
742 self.assertEqual(d.day, day)
743
Tim Peters1b6f7a92004-06-20 02:50:16 +0000744 def test_insane_fromtimestamp(self):
745 # It's possible that some platform maps time_t to double,
746 # and that this test will fail there. This test should
747 # exempt such platforms (provided they return reasonable
748 # results!).
749 for insane in -1e200, 1e200:
750 self.assertRaises(ValueError, self.theclass.fromtimestamp,
751 insane)
752
Tim Peters2a799bf2002-12-16 20:18:38 +0000753 def test_today(self):
754 import time
755
756 # We claim that today() is like fromtimestamp(time.time()), so
757 # prove it.
758 for dummy in range(3):
759 today = self.theclass.today()
760 ts = time.time()
761 todayagain = self.theclass.fromtimestamp(ts)
762 if today == todayagain:
763 break
764 # There are several legit reasons that could fail:
765 # 1. It recently became midnight, between the today() and the
766 # time() calls.
767 # 2. The platform time() has such fine resolution that we'll
768 # never get the same value twice.
769 # 3. The platform time() has poor resolution, and we just
770 # happened to call today() right before a resolution quantum
771 # boundary.
772 # 4. The system clock got fiddled between calls.
773 # In any case, wait a little while and try again.
774 time.sleep(0.1)
775
776 # It worked or it didn't. If it didn't, assume it's reason #2, and
777 # let the test pass if they're within half a second of each other.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000778 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000779 abs(todayagain - today) < timedelta(seconds=0.5))
780
781 def test_weekday(self):
782 for i in range(7):
783 # March 4, 2002 is a Monday
784 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
785 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
786 # January 2, 1956 is a Monday
787 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
788 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
789
790 def test_isocalendar(self):
791 # Check examples from
792 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
793 for i in range(7):
794 d = self.theclass(2003, 12, 22+i)
795 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
796 d = self.theclass(2003, 12, 29) + timedelta(i)
797 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
798 d = self.theclass(2004, 1, 5+i)
799 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
800 d = self.theclass(2009, 12, 21+i)
801 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
802 d = self.theclass(2009, 12, 28) + timedelta(i)
803 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
804 d = self.theclass(2010, 1, 4+i)
805 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
806
807 def test_iso_long_years(self):
808 # Calculate long ISO years and compare to table from
809 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
810 ISO_LONG_YEARS_TABLE = """
811 4 32 60 88
812 9 37 65 93
813 15 43 71 99
814 20 48 76
815 26 54 82
816
817 105 133 161 189
818 111 139 167 195
819 116 144 172
820 122 150 178
821 128 156 184
822
823 201 229 257 285
824 207 235 263 291
825 212 240 268 296
826 218 246 274
827 224 252 280
828
829 303 331 359 387
830 308 336 364 392
831 314 342 370 398
832 320 348 376
833 325 353 381
834 """
835 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
836 iso_long_years.sort()
837 L = []
838 for i in range(400):
839 d = self.theclass(2000+i, 12, 31)
840 d1 = self.theclass(1600+i, 12, 31)
841 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
842 if d.isocalendar()[1] == 53:
843 L.append(i)
844 self.assertEqual(L, iso_long_years)
845
846 def test_isoformat(self):
847 t = self.theclass(2, 3, 2)
848 self.assertEqual(t.isoformat(), "0002-03-02")
849
850 def test_ctime(self):
851 t = self.theclass(2002, 3, 2)
852 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
853
854 def test_strftime(self):
855 t = self.theclass(2005, 3, 2)
856 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000857 self.assertEqual(t.strftime(""), "") # SF bug #761337
Georg Brandl4ddfcd32006-09-30 11:17:34 +0000858 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000859
860 self.assertRaises(TypeError, t.strftime) # needs an arg
861 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
862 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
863
Gregory P. Smith137d8242008-06-02 04:05:52 +0000864 # test that unicode input is allowed (issue 2782)
865 self.assertEqual(t.strftime(u"%m"), "03")
866
Tim Peters2a799bf2002-12-16 20:18:38 +0000867 # A naive object replaces %z and %Z w/ empty strings.
868 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
869
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000870 #make sure that invalid format specifiers are handled correctly
Kristján Valur Jónsson8adc0b52009-01-15 09:09:13 +0000871 #self.assertRaises(ValueError, t.strftime, "%e")
872 #self.assertRaises(ValueError, t.strftime, "%")
873 #self.assertRaises(ValueError, t.strftime, "%#")
874
875 #oh well, some systems just ignore those invalid ones.
876 #at least, excercise them to make sure that no crashes
877 #are generated
878 for f in ["%e", "%", "%#"]:
879 try:
880 t.strftime(f)
881 except ValueError:
882 pass
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000883
884 #check that this standard extension works
885 t.strftime("%f")
886
Gregory P. Smith137d8242008-06-02 04:05:52 +0000887
Eric Smitha9f7d622008-02-17 19:46:49 +0000888 def test_format(self):
889 dt = self.theclass(2007, 9, 10)
890 self.assertEqual(dt.__format__(''), str(dt))
891
892 # check that a derived class's __str__() gets called
893 class A(self.theclass):
894 def __str__(self):
895 return 'A'
896 a = A(2007, 9, 10)
897 self.assertEqual(a.__format__(''), 'A')
898
899 # check that a derived class's strftime gets called
900 class B(self.theclass):
901 def strftime(self, format_spec):
902 return 'B'
903 b = B(2007, 9, 10)
904 self.assertEqual(b.__format__(''), str(dt))
905
906 for fmt in ["m:%m d:%d y:%y",
907 "m:%m d:%d y:%y H:%H M:%M S:%S",
908 "%z %Z",
909 ]:
910 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
911 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
912 self.assertEqual(b.__format__(fmt), 'B')
913
Tim Peters2a799bf2002-12-16 20:18:38 +0000914 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000915 self.assertIsInstance(self.theclass.min, self.theclass)
916 self.assertIsInstance(self.theclass.max, self.theclass)
917 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000918 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000919
920 def test_extreme_timedelta(self):
921 big = self.theclass.max - self.theclass.min
922 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
923 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
924 # n == 315537897599999999 ~= 2**58.13
925 justasbig = timedelta(0, 0, n)
926 self.assertEqual(big, justasbig)
927 self.assertEqual(self.theclass.min + big, self.theclass.max)
928 self.assertEqual(self.theclass.max - big, self.theclass.min)
929
930 def test_timetuple(self):
931 for i in range(7):
932 # January 2, 1956 is a Monday (0)
933 d = self.theclass(1956, 1, 2+i)
934 t = d.timetuple()
935 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
936 # February 1, 1956 is a Wednesday (2)
937 d = self.theclass(1956, 2, 1+i)
938 t = d.timetuple()
939 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
940 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
941 # of the year.
942 d = self.theclass(1956, 3, 1+i)
943 t = d.timetuple()
944 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
945 self.assertEqual(t.tm_year, 1956)
946 self.assertEqual(t.tm_mon, 3)
947 self.assertEqual(t.tm_mday, 1+i)
948 self.assertEqual(t.tm_hour, 0)
949 self.assertEqual(t.tm_min, 0)
950 self.assertEqual(t.tm_sec, 0)
951 self.assertEqual(t.tm_wday, (3+i)%7)
952 self.assertEqual(t.tm_yday, 61+i)
953 self.assertEqual(t.tm_isdst, -1)
954
955 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000956 args = 6, 7, 23
957 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000958 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000959 green = pickler.dumps(orig, proto)
960 derived = unpickler.loads(green)
961 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000962
963 def test_compare(self):
964 t1 = self.theclass(2, 3, 4)
965 t2 = self.theclass(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000966 self.assertTrue(t1 == t2)
967 self.assertTrue(t1 <= t2)
968 self.assertTrue(t1 >= t2)
969 self.assertTrue(not t1 != t2)
970 self.assertTrue(not t1 < t2)
971 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000972 self.assertEqual(cmp(t1, t2), 0)
973 self.assertEqual(cmp(t2, t1), 0)
974
975 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
976 t2 = self.theclass(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000977 self.assertTrue(t1 < t2)
978 self.assertTrue(t2 > t1)
979 self.assertTrue(t1 <= t2)
980 self.assertTrue(t2 >= t1)
981 self.assertTrue(t1 != t2)
982 self.assertTrue(t2 != t1)
983 self.assertTrue(not t1 == t2)
984 self.assertTrue(not t2 == t1)
985 self.assertTrue(not t1 > t2)
986 self.assertTrue(not t2 < t1)
987 self.assertTrue(not t1 >= t2)
988 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000989 self.assertEqual(cmp(t1, t2), -1)
990 self.assertEqual(cmp(t2, t1), 1)
991
Tim Peters68124bb2003-02-08 03:46:31 +0000992 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000993 self.assertEqual(t1 == badarg, False)
994 self.assertEqual(t1 != badarg, True)
995 self.assertEqual(badarg == t1, False)
996 self.assertEqual(badarg != t1, True)
997
Tim Peters2a799bf2002-12-16 20:18:38 +0000998 self.assertRaises(TypeError, lambda: t1 < badarg)
999 self.assertRaises(TypeError, lambda: t1 > badarg)
1000 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001001 self.assertRaises(TypeError, lambda: badarg <= t1)
1002 self.assertRaises(TypeError, lambda: badarg < t1)
1003 self.assertRaises(TypeError, lambda: badarg > t1)
1004 self.assertRaises(TypeError, lambda: badarg >= t1)
1005
Tim Peters8d81a012003-01-24 22:36:34 +00001006 def test_mixed_compare(self):
1007 our = self.theclass(2000, 4, 5)
1008 self.assertRaises(TypeError, cmp, our, 1)
1009 self.assertRaises(TypeError, cmp, 1, our)
1010
1011 class AnotherDateTimeClass(object):
1012 def __cmp__(self, other):
1013 # Return "equal" so calling this can't be confused with
1014 # compare-by-address (which never says "equal" for distinct
1015 # objects).
1016 return 0
Nick Coghlan48361f52008-08-11 15:45:58 +00001017 __hash__ = None # Silence Py3k warning
Tim Peters8d81a012003-01-24 22:36:34 +00001018
1019 # This still errors, because date and datetime comparison raise
1020 # TypeError instead of NotImplemented when they don't know what to
1021 # do, in order to stop comparison from falling back to the default
1022 # compare-by-address.
1023 their = AnotherDateTimeClass()
1024 self.assertRaises(TypeError, cmp, our, their)
1025 # Oops: The next stab raises TypeError in the C implementation,
1026 # but not in the Python implementation of datetime. The difference
1027 # is due to that the Python implementation defines __cmp__ but
1028 # the C implementation defines tp_richcompare. This is more pain
1029 # to fix than it's worth, so commenting out the test.
1030 # self.assertEqual(cmp(their, our), 0)
1031
1032 # But date and datetime comparison return NotImplemented instead if the
1033 # other object has a timetuple attr. This gives the other object a
1034 # chance to do the comparison.
1035 class Comparable(AnotherDateTimeClass):
1036 def timetuple(self):
1037 return ()
1038
1039 their = Comparable()
1040 self.assertEqual(cmp(our, their), 0)
1041 self.assertEqual(cmp(their, our), 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001042 self.assertTrue(our == their)
1043 self.assertTrue(their == our)
Tim Peters8d81a012003-01-24 22:36:34 +00001044
Tim Peters2a799bf2002-12-16 20:18:38 +00001045 def test_bool(self):
1046 # All dates are considered true.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001047 self.assertTrue(self.theclass.min)
1048 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001049
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001050 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001051 # For nasty technical reasons, we can't handle years before 1900.
1052 cls = self.theclass
1053 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1054 for y in 1, 49, 51, 99, 100, 1000, 1899:
1055 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001056
1057 def test_replace(self):
1058 cls = self.theclass
1059 args = [1, 2, 3]
1060 base = cls(*args)
1061 self.assertEqual(base, base.replace())
1062
1063 i = 0
1064 for name, newval in (("year", 2),
1065 ("month", 3),
1066 ("day", 4)):
1067 newargs = args[:]
1068 newargs[i] = newval
1069 expected = cls(*newargs)
1070 got = base.replace(**{name: newval})
1071 self.assertEqual(expected, got)
1072 i += 1
1073
1074 # Out of bounds.
1075 base = cls(2000, 2, 29)
1076 self.assertRaises(ValueError, base.replace, year=2001)
1077
Tim Petersa98924a2003-05-17 05:55:19 +00001078 def test_subclass_date(self):
1079
1080 class C(self.theclass):
1081 theAnswer = 42
1082
1083 def __new__(cls, *args, **kws):
1084 temp = kws.copy()
1085 extra = temp.pop('extra')
1086 result = self.theclass.__new__(cls, *args, **temp)
1087 result.extra = extra
1088 return result
1089
1090 def newmeth(self, start):
1091 return start + self.year + self.month
1092
1093 args = 2003, 4, 14
1094
1095 dt1 = self.theclass(*args)
1096 dt2 = C(*args, **{'extra': 7})
1097
1098 self.assertEqual(dt2.__class__, C)
1099 self.assertEqual(dt2.theAnswer, 42)
1100 self.assertEqual(dt2.extra, 7)
1101 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1102 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1103
Tim Peters604c0132004-06-07 23:04:33 +00001104 def test_pickling_subclass_date(self):
1105
1106 args = 6, 7, 23
1107 orig = SubclassDate(*args)
1108 for pickler, unpickler, proto in pickle_choices:
1109 green = pickler.dumps(orig, proto)
1110 derived = unpickler.loads(green)
1111 self.assertEqual(orig, derived)
1112
Tim Peters3f606292004-03-21 23:38:41 +00001113 def test_backdoor_resistance(self):
1114 # For fast unpickling, the constructor accepts a pickle string.
1115 # This is a low-overhead backdoor. A user can (by intent or
1116 # mistake) pass a string directly, which (if it's the right length)
1117 # will get treated like a pickle, and bypass the normal sanity
1118 # checks in the constructor. This can create insane objects.
1119 # The constructor doesn't want to burn the time to validate all
1120 # fields, but does check the month field. This stops, e.g.,
1121 # datetime.datetime('1995-03-25') from yielding an insane object.
1122 base = '1995-03-25'
1123 if not issubclass(self.theclass, datetime):
1124 base = base[:4]
1125 for month_byte in '9', chr(0), chr(13), '\xff':
1126 self.assertRaises(TypeError, self.theclass,
1127 base[:2] + month_byte + base[3:])
1128 for ord_byte in range(1, 13):
1129 # This shouldn't blow up because of the month byte alone. If
1130 # the implementation changes to do more-careful checking, it may
1131 # blow up because other fields are insane.
1132 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001133
Tim Peters2a799bf2002-12-16 20:18:38 +00001134#############################################################################
1135# datetime tests
1136
Tim Peters604c0132004-06-07 23:04:33 +00001137class SubclassDatetime(datetime):
1138 sub_var = 1
1139
Tim Peters2a799bf2002-12-16 20:18:38 +00001140class TestDateTime(TestDate):
1141
1142 theclass = datetime
1143
1144 def test_basic_attributes(self):
1145 dt = self.theclass(2002, 3, 1, 12, 0)
1146 self.assertEqual(dt.year, 2002)
1147 self.assertEqual(dt.month, 3)
1148 self.assertEqual(dt.day, 1)
1149 self.assertEqual(dt.hour, 12)
1150 self.assertEqual(dt.minute, 0)
1151 self.assertEqual(dt.second, 0)
1152 self.assertEqual(dt.microsecond, 0)
1153
1154 def test_basic_attributes_nonzero(self):
1155 # Make sure all attributes are non-zero so bugs in
1156 # bit-shifting access show up.
1157 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1158 self.assertEqual(dt.year, 2002)
1159 self.assertEqual(dt.month, 3)
1160 self.assertEqual(dt.day, 1)
1161 self.assertEqual(dt.hour, 12)
1162 self.assertEqual(dt.minute, 59)
1163 self.assertEqual(dt.second, 59)
1164 self.assertEqual(dt.microsecond, 8000)
1165
1166 def test_roundtrip(self):
1167 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1168 self.theclass.now()):
1169 # Verify dt -> string -> datetime identity.
1170 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001171 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001172 s = s[9:]
1173 dt2 = eval(s)
1174 self.assertEqual(dt, dt2)
1175
1176 # Verify identity via reconstructing from pieces.
1177 dt2 = self.theclass(dt.year, dt.month, dt.day,
1178 dt.hour, dt.minute, dt.second,
1179 dt.microsecond)
1180 self.assertEqual(dt, dt2)
1181
1182 def test_isoformat(self):
1183 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1184 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1185 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1186 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arc8645a5c2009-12-29 22:03:38 +00001187 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001188 # str is ISO format with the separator forced to a blank.
1189 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1190
1191 t = self.theclass(2, 3, 2)
1192 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1193 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1194 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1195 # str is ISO format with the separator forced to a blank.
1196 self.assertEqual(str(t), "0002-03-02 00:00:00")
1197
Eric Smitha9f7d622008-02-17 19:46:49 +00001198 def test_format(self):
1199 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1200 self.assertEqual(dt.__format__(''), str(dt))
1201
1202 # check that a derived class's __str__() gets called
1203 class A(self.theclass):
1204 def __str__(self):
1205 return 'A'
1206 a = A(2007, 9, 10, 4, 5, 1, 123)
1207 self.assertEqual(a.__format__(''), 'A')
1208
1209 # check that a derived class's strftime gets called
1210 class B(self.theclass):
1211 def strftime(self, format_spec):
1212 return 'B'
1213 b = B(2007, 9, 10, 4, 5, 1, 123)
1214 self.assertEqual(b.__format__(''), str(dt))
1215
1216 for fmt in ["m:%m d:%d y:%y",
1217 "m:%m d:%d y:%y H:%H M:%M S:%S",
1218 "%z %Z",
1219 ]:
1220 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1221 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1222 self.assertEqual(b.__format__(fmt), 'B')
1223
Tim Peters2a799bf2002-12-16 20:18:38 +00001224 def test_more_ctime(self):
1225 # Test fields that TestDate doesn't touch.
1226 import time
1227
1228 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1229 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1230 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1231 # out. The difference is that t.ctime() produces " 2" for the day,
1232 # but platform ctime() produces "02" for the day. According to
1233 # C99, t.ctime() is correct here.
1234 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1235
1236 # So test a case where that difference doesn't matter.
1237 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1238 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1239
1240 def test_tz_independent_comparing(self):
1241 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1242 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1243 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1244 self.assertEqual(dt1, dt3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001245 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001246
1247 # Make sure comparison doesn't forget microseconds, and isn't done
1248 # via comparing a float timestamp (an IEEE double doesn't have enough
1249 # precision to span microsecond resolution across years 1 thru 9999,
1250 # so comparing via timestamp necessarily calls some distinct values
1251 # equal).
1252 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1253 us = timedelta(microseconds=1)
1254 dt2 = dt1 + us
1255 self.assertEqual(dt2 - dt1, us)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001256 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001257
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001258 def test_strftime_with_bad_tzname_replace(self):
1259 # verify ok if tzinfo.tzname().replace() returns a non-string
1260 class MyTzInfo(FixedOffset):
1261 def tzname(self, dt):
1262 class MyStr(str):
1263 def replace(self, *args):
1264 return None
1265 return MyStr('name')
1266 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1267 self.assertRaises(TypeError, t.strftime, '%Z')
1268
Tim Peters2a799bf2002-12-16 20:18:38 +00001269 def test_bad_constructor_arguments(self):
1270 # bad years
1271 self.theclass(MINYEAR, 1, 1) # no exception
1272 self.theclass(MAXYEAR, 1, 1) # no exception
1273 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1274 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1275 # bad months
1276 self.theclass(2000, 1, 1) # no exception
1277 self.theclass(2000, 12, 1) # no exception
1278 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1279 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1280 # bad days
1281 self.theclass(2000, 2, 29) # no exception
1282 self.theclass(2004, 2, 29) # no exception
1283 self.theclass(2400, 2, 29) # no exception
1284 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1285 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1286 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1287 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1288 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1289 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1290 # bad hours
1291 self.theclass(2000, 1, 31, 0) # no exception
1292 self.theclass(2000, 1, 31, 23) # no exception
1293 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1294 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1295 # bad minutes
1296 self.theclass(2000, 1, 31, 23, 0) # no exception
1297 self.theclass(2000, 1, 31, 23, 59) # no exception
1298 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1299 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1300 # bad seconds
1301 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1302 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1303 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1304 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1305 # bad microseconds
1306 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1307 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1308 self.assertRaises(ValueError, self.theclass,
1309 2000, 1, 31, 23, 59, 59, -1)
1310 self.assertRaises(ValueError, self.theclass,
1311 2000, 1, 31, 23, 59, 59,
1312 1000000)
1313
1314 def test_hash_equality(self):
1315 d = self.theclass(2000, 12, 31, 23, 30, 17)
1316 e = self.theclass(2000, 12, 31, 23, 30, 17)
1317 self.assertEqual(d, e)
1318 self.assertEqual(hash(d), hash(e))
1319
1320 dic = {d: 1}
1321 dic[e] = 2
1322 self.assertEqual(len(dic), 1)
1323 self.assertEqual(dic[d], 2)
1324 self.assertEqual(dic[e], 2)
1325
1326 d = self.theclass(2001, 1, 1, 0, 5, 17)
1327 e = self.theclass(2001, 1, 1, 0, 5, 17)
1328 self.assertEqual(d, e)
1329 self.assertEqual(hash(d), hash(e))
1330
1331 dic = {d: 1}
1332 dic[e] = 2
1333 self.assertEqual(len(dic), 1)
1334 self.assertEqual(dic[d], 2)
1335 self.assertEqual(dic[e], 2)
1336
1337 def test_computations(self):
1338 a = self.theclass(2002, 1, 31)
1339 b = self.theclass(1956, 1, 31)
1340 diff = a-b
1341 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1342 self.assertEqual(diff.seconds, 0)
1343 self.assertEqual(diff.microseconds, 0)
1344 a = self.theclass(2002, 3, 2, 17, 6)
1345 millisec = timedelta(0, 0, 1000)
1346 hour = timedelta(0, 3600)
1347 day = timedelta(1)
1348 week = timedelta(7)
1349 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1350 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1351 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1352 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1353 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1354 self.assertEqual(a - hour, a + -hour)
1355 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1356 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1357 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1358 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1359 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1360 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1361 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1362 self.assertEqual((a + week) - a, week)
1363 self.assertEqual((a + day) - a, day)
1364 self.assertEqual((a + hour) - a, hour)
1365 self.assertEqual((a + millisec) - a, millisec)
1366 self.assertEqual((a - week) - a, -week)
1367 self.assertEqual((a - day) - a, -day)
1368 self.assertEqual((a - hour) - a, -hour)
1369 self.assertEqual((a - millisec) - a, -millisec)
1370 self.assertEqual(a - (a + week), -week)
1371 self.assertEqual(a - (a + day), -day)
1372 self.assertEqual(a - (a + hour), -hour)
1373 self.assertEqual(a - (a + millisec), -millisec)
1374 self.assertEqual(a - (a - week), week)
1375 self.assertEqual(a - (a - day), day)
1376 self.assertEqual(a - (a - hour), hour)
1377 self.assertEqual(a - (a - millisec), millisec)
1378 self.assertEqual(a + (week + day + hour + millisec),
1379 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1380 self.assertEqual(a + (week + day + hour + millisec),
1381 (((a + week) + day) + hour) + millisec)
1382 self.assertEqual(a - (week + day + hour + millisec),
1383 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1384 self.assertEqual(a - (week + day + hour + millisec),
1385 (((a - week) - day) - hour) - millisec)
1386 # Add/sub ints, longs, floats should be illegal
1387 for i in 1, 1L, 1.0:
1388 self.assertRaises(TypeError, lambda: a+i)
1389 self.assertRaises(TypeError, lambda: a-i)
1390 self.assertRaises(TypeError, lambda: i+a)
1391 self.assertRaises(TypeError, lambda: i-a)
1392
1393 # delta - datetime is senseless.
1394 self.assertRaises(TypeError, lambda: day - a)
1395 # mixing datetime and (delta or datetime) via * or // is senseless
1396 self.assertRaises(TypeError, lambda: day * a)
1397 self.assertRaises(TypeError, lambda: a * day)
1398 self.assertRaises(TypeError, lambda: day // a)
1399 self.assertRaises(TypeError, lambda: a // day)
1400 self.assertRaises(TypeError, lambda: a * a)
1401 self.assertRaises(TypeError, lambda: a // a)
1402 # datetime + datetime is senseless
1403 self.assertRaises(TypeError, lambda: a + a)
1404
1405 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001406 args = 6, 7, 23, 20, 59, 1, 64**2
1407 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001408 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001409 green = pickler.dumps(orig, proto)
1410 derived = unpickler.loads(green)
1411 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001412
Guido van Rossum275666f2003-02-07 21:49:01 +00001413 def test_more_pickling(self):
1414 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1415 s = pickle.dumps(a)
1416 b = pickle.loads(s)
1417 self.assertEqual(b.year, 2003)
1418 self.assertEqual(b.month, 2)
1419 self.assertEqual(b.day, 7)
1420
Tim Peters604c0132004-06-07 23:04:33 +00001421 def test_pickling_subclass_datetime(self):
1422 args = 6, 7, 23, 20, 59, 1, 64**2
1423 orig = SubclassDatetime(*args)
1424 for pickler, unpickler, proto in pickle_choices:
1425 green = pickler.dumps(orig, proto)
1426 derived = unpickler.loads(green)
1427 self.assertEqual(orig, derived)
1428
Tim Peters2a799bf2002-12-16 20:18:38 +00001429 def test_more_compare(self):
1430 # The test_compare() inherited from TestDate covers the error cases.
1431 # We just want to test lexicographic ordering on the members datetime
1432 # has that date lacks.
1433 args = [2000, 11, 29, 20, 58, 16, 999998]
1434 t1 = self.theclass(*args)
1435 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001436 self.assertTrue(t1 == t2)
1437 self.assertTrue(t1 <= t2)
1438 self.assertTrue(t1 >= t2)
1439 self.assertTrue(not t1 != t2)
1440 self.assertTrue(not t1 < t2)
1441 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001442 self.assertEqual(cmp(t1, t2), 0)
1443 self.assertEqual(cmp(t2, t1), 0)
1444
1445 for i in range(len(args)):
1446 newargs = args[:]
1447 newargs[i] = args[i] + 1
1448 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001449 self.assertTrue(t1 < t2)
1450 self.assertTrue(t2 > t1)
1451 self.assertTrue(t1 <= t2)
1452 self.assertTrue(t2 >= t1)
1453 self.assertTrue(t1 != t2)
1454 self.assertTrue(t2 != t1)
1455 self.assertTrue(not t1 == t2)
1456 self.assertTrue(not t2 == t1)
1457 self.assertTrue(not t1 > t2)
1458 self.assertTrue(not t2 < t1)
1459 self.assertTrue(not t1 >= t2)
1460 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001461 self.assertEqual(cmp(t1, t2), -1)
1462 self.assertEqual(cmp(t2, t1), 1)
1463
1464
1465 # A helper for timestamp constructor tests.
1466 def verify_field_equality(self, expected, got):
1467 self.assertEqual(expected.tm_year, got.year)
1468 self.assertEqual(expected.tm_mon, got.month)
1469 self.assertEqual(expected.tm_mday, got.day)
1470 self.assertEqual(expected.tm_hour, got.hour)
1471 self.assertEqual(expected.tm_min, got.minute)
1472 self.assertEqual(expected.tm_sec, got.second)
1473
1474 def test_fromtimestamp(self):
1475 import time
1476
1477 ts = time.time()
1478 expected = time.localtime(ts)
1479 got = self.theclass.fromtimestamp(ts)
1480 self.verify_field_equality(expected, got)
1481
1482 def test_utcfromtimestamp(self):
1483 import time
1484
1485 ts = time.time()
1486 expected = time.gmtime(ts)
1487 got = self.theclass.utcfromtimestamp(ts)
1488 self.verify_field_equality(expected, got)
1489
Georg Brandl6d78a582006-04-28 19:09:24 +00001490 def test_microsecond_rounding(self):
1491 # Test whether fromtimestamp "rounds up" floats that are less
1492 # than one microsecond smaller than an integer.
1493 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1494 self.theclass.fromtimestamp(1))
1495
Tim Peters1b6f7a92004-06-20 02:50:16 +00001496 def test_insane_fromtimestamp(self):
1497 # It's possible that some platform maps time_t to double,
1498 # and that this test will fail there. This test should
1499 # exempt such platforms (provided they return reasonable
1500 # results!).
1501 for insane in -1e200, 1e200:
1502 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1503 insane)
1504
1505 def test_insane_utcfromtimestamp(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.utcfromtimestamp,
1512 insane)
Alexander Belopolsky58451d22010-05-26 20:45:37 +00001513 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossum2054ee92007-03-06 15:50:01 +00001514 def test_negative_float_fromtimestamp(self):
1515 # The result is tz-dependent; at least test that this doesn't
1516 # fail (like it did before bug 1646728 was fixed).
1517 self.theclass.fromtimestamp(-1.05)
1518
Alexander Belopolsky58451d22010-05-26 20:45:37 +00001519 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossum2054ee92007-03-06 15:50:01 +00001520 def test_negative_float_utcfromtimestamp(self):
1521 d = self.theclass.utcfromtimestamp(-1.05)
1522 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1523
Tim Peters2a799bf2002-12-16 20:18:38 +00001524 def test_utcnow(self):
1525 import time
1526
1527 # Call it a success if utcnow() and utcfromtimestamp() are within
1528 # a second of each other.
1529 tolerance = timedelta(seconds=1)
1530 for dummy in range(3):
1531 from_now = self.theclass.utcnow()
1532 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1533 if abs(from_timestamp - from_now) <= tolerance:
1534 break
1535 # Else try again a few times.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001536 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001537
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001538 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001539 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001540
Skip Montanarofc070d22008-03-15 16:04:45 +00001541 string = '2004-12-01 13:02:47.197'
1542 format = '%Y-%m-%d %H:%M:%S.%f'
1543 result, frac = _strptime._strptime(string, format)
1544 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001545 got = self.theclass.strptime(string, format)
1546 self.assertEqual(expected, got)
1547
Tim Peters2a799bf2002-12-16 20:18:38 +00001548 def test_more_timetuple(self):
1549 # This tests fields beyond those tested by the TestDate.test_timetuple.
1550 t = self.theclass(2004, 12, 31, 6, 22, 33)
1551 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1552 self.assertEqual(t.timetuple(),
1553 (t.year, t.month, t.day,
1554 t.hour, t.minute, t.second,
1555 t.weekday(),
1556 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1557 -1))
1558 tt = t.timetuple()
1559 self.assertEqual(tt.tm_year, t.year)
1560 self.assertEqual(tt.tm_mon, t.month)
1561 self.assertEqual(tt.tm_mday, t.day)
1562 self.assertEqual(tt.tm_hour, t.hour)
1563 self.assertEqual(tt.tm_min, t.minute)
1564 self.assertEqual(tt.tm_sec, t.second)
1565 self.assertEqual(tt.tm_wday, t.weekday())
1566 self.assertEqual(tt.tm_yday, t.toordinal() -
1567 date(t.year, 1, 1).toordinal() + 1)
1568 self.assertEqual(tt.tm_isdst, -1)
1569
1570 def test_more_strftime(self):
1571 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001572 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1573 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1574 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001575
1576 def test_extract(self):
1577 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1578 self.assertEqual(dt.date(), date(2002, 3, 4))
1579 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1580
1581 def test_combine(self):
1582 d = date(2002, 3, 4)
1583 t = time(18, 45, 3, 1234)
1584 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1585 combine = self.theclass.combine
1586 dt = combine(d, t)
1587 self.assertEqual(dt, expected)
1588
1589 dt = combine(time=t, date=d)
1590 self.assertEqual(dt, expected)
1591
1592 self.assertEqual(d, dt.date())
1593 self.assertEqual(t, dt.time())
1594 self.assertEqual(dt, combine(dt.date(), dt.time()))
1595
1596 self.assertRaises(TypeError, combine) # need an arg
1597 self.assertRaises(TypeError, combine, d) # need two args
1598 self.assertRaises(TypeError, combine, t, d) # args reversed
1599 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1600 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1601
Tim Peters12bf3392002-12-24 05:41:27 +00001602 def test_replace(self):
1603 cls = self.theclass
1604 args = [1, 2, 3, 4, 5, 6, 7]
1605 base = cls(*args)
1606 self.assertEqual(base, base.replace())
1607
1608 i = 0
1609 for name, newval in (("year", 2),
1610 ("month", 3),
1611 ("day", 4),
1612 ("hour", 5),
1613 ("minute", 6),
1614 ("second", 7),
1615 ("microsecond", 8)):
1616 newargs = args[:]
1617 newargs[i] = newval
1618 expected = cls(*newargs)
1619 got = base.replace(**{name: newval})
1620 self.assertEqual(expected, got)
1621 i += 1
1622
1623 # Out of bounds.
1624 base = cls(2000, 2, 29)
1625 self.assertRaises(ValueError, base.replace, year=2001)
1626
Tim Peters80475bb2002-12-25 07:40:55 +00001627 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001628 # Pretty boring! The TZ test is more interesting here. astimezone()
1629 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001630 dt = self.theclass.now()
1631 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001632 self.assertRaises(TypeError, dt.astimezone) # not enough args
1633 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1634 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001635 self.assertRaises(ValueError, dt.astimezone, f) # naive
1636 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001637
Tim Peters52dcce22003-01-23 16:36:11 +00001638 class Bogus(tzinfo):
1639 def utcoffset(self, dt): return None
1640 def dst(self, dt): return timedelta(0)
1641 bog = Bogus()
1642 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1643
1644 class AlsoBogus(tzinfo):
1645 def utcoffset(self, dt): return timedelta(0)
1646 def dst(self, dt): return None
1647 alsobog = AlsoBogus()
1648 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001649
Tim Petersa98924a2003-05-17 05:55:19 +00001650 def test_subclass_datetime(self):
1651
1652 class C(self.theclass):
1653 theAnswer = 42
1654
1655 def __new__(cls, *args, **kws):
1656 temp = kws.copy()
1657 extra = temp.pop('extra')
1658 result = self.theclass.__new__(cls, *args, **temp)
1659 result.extra = extra
1660 return result
1661
1662 def newmeth(self, start):
1663 return start + self.year + self.month + self.second
1664
1665 args = 2003, 4, 14, 12, 13, 41
1666
1667 dt1 = self.theclass(*args)
1668 dt2 = C(*args, **{'extra': 7})
1669
1670 self.assertEqual(dt2.__class__, C)
1671 self.assertEqual(dt2.theAnswer, 42)
1672 self.assertEqual(dt2.extra, 7)
1673 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1674 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1675 dt1.second - 7)
1676
Tim Peters604c0132004-06-07 23:04:33 +00001677class SubclassTime(time):
1678 sub_var = 1
1679
Collin Winterc2898c52007-04-25 17:29:52 +00001680class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001681
1682 theclass = time
1683
1684 def test_basic_attributes(self):
1685 t = self.theclass(12, 0)
1686 self.assertEqual(t.hour, 12)
1687 self.assertEqual(t.minute, 0)
1688 self.assertEqual(t.second, 0)
1689 self.assertEqual(t.microsecond, 0)
1690
1691 def test_basic_attributes_nonzero(self):
1692 # Make sure all attributes are non-zero so bugs in
1693 # bit-shifting access show up.
1694 t = self.theclass(12, 59, 59, 8000)
1695 self.assertEqual(t.hour, 12)
1696 self.assertEqual(t.minute, 59)
1697 self.assertEqual(t.second, 59)
1698 self.assertEqual(t.microsecond, 8000)
1699
1700 def test_roundtrip(self):
1701 t = self.theclass(1, 2, 3, 4)
1702
1703 # Verify t -> string -> time identity.
1704 s = repr(t)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001705 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001706 s = s[9:]
1707 t2 = eval(s)
1708 self.assertEqual(t, t2)
1709
1710 # Verify identity via reconstructing from pieces.
1711 t2 = self.theclass(t.hour, t.minute, t.second,
1712 t.microsecond)
1713 self.assertEqual(t, t2)
1714
1715 def test_comparing(self):
1716 args = [1, 2, 3, 4]
1717 t1 = self.theclass(*args)
1718 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001719 self.assertTrue(t1 == t2)
1720 self.assertTrue(t1 <= t2)
1721 self.assertTrue(t1 >= t2)
1722 self.assertTrue(not t1 != t2)
1723 self.assertTrue(not t1 < t2)
1724 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001725 self.assertEqual(cmp(t1, t2), 0)
1726 self.assertEqual(cmp(t2, t1), 0)
1727
1728 for i in range(len(args)):
1729 newargs = args[:]
1730 newargs[i] = args[i] + 1
1731 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001732 self.assertTrue(t1 < t2)
1733 self.assertTrue(t2 > t1)
1734 self.assertTrue(t1 <= t2)
1735 self.assertTrue(t2 >= t1)
1736 self.assertTrue(t1 != t2)
1737 self.assertTrue(t2 != t1)
1738 self.assertTrue(not t1 == t2)
1739 self.assertTrue(not t2 == t1)
1740 self.assertTrue(not t1 > t2)
1741 self.assertTrue(not t2 < t1)
1742 self.assertTrue(not t1 >= t2)
1743 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001744 self.assertEqual(cmp(t1, t2), -1)
1745 self.assertEqual(cmp(t2, t1), 1)
1746
Tim Peters68124bb2003-02-08 03:46:31 +00001747 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001748 self.assertEqual(t1 == badarg, False)
1749 self.assertEqual(t1 != badarg, True)
1750 self.assertEqual(badarg == t1, False)
1751 self.assertEqual(badarg != t1, True)
1752
Tim Peters2a799bf2002-12-16 20:18:38 +00001753 self.assertRaises(TypeError, lambda: t1 <= badarg)
1754 self.assertRaises(TypeError, lambda: t1 < badarg)
1755 self.assertRaises(TypeError, lambda: t1 > badarg)
1756 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001757 self.assertRaises(TypeError, lambda: badarg <= t1)
1758 self.assertRaises(TypeError, lambda: badarg < t1)
1759 self.assertRaises(TypeError, lambda: badarg > t1)
1760 self.assertRaises(TypeError, lambda: badarg >= t1)
1761
1762 def test_bad_constructor_arguments(self):
1763 # bad hours
1764 self.theclass(0, 0) # no exception
1765 self.theclass(23, 0) # no exception
1766 self.assertRaises(ValueError, self.theclass, -1, 0)
1767 self.assertRaises(ValueError, self.theclass, 24, 0)
1768 # bad minutes
1769 self.theclass(23, 0) # no exception
1770 self.theclass(23, 59) # no exception
1771 self.assertRaises(ValueError, self.theclass, 23, -1)
1772 self.assertRaises(ValueError, self.theclass, 23, 60)
1773 # bad seconds
1774 self.theclass(23, 59, 0) # no exception
1775 self.theclass(23, 59, 59) # no exception
1776 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1777 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1778 # bad microseconds
1779 self.theclass(23, 59, 59, 0) # no exception
1780 self.theclass(23, 59, 59, 999999) # no exception
1781 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1782 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1783
1784 def test_hash_equality(self):
1785 d = self.theclass(23, 30, 17)
1786 e = self.theclass(23, 30, 17)
1787 self.assertEqual(d, e)
1788 self.assertEqual(hash(d), hash(e))
1789
1790 dic = {d: 1}
1791 dic[e] = 2
1792 self.assertEqual(len(dic), 1)
1793 self.assertEqual(dic[d], 2)
1794 self.assertEqual(dic[e], 2)
1795
1796 d = self.theclass(0, 5, 17)
1797 e = self.theclass(0, 5, 17)
1798 self.assertEqual(d, e)
1799 self.assertEqual(hash(d), hash(e))
1800
1801 dic = {d: 1}
1802 dic[e] = 2
1803 self.assertEqual(len(dic), 1)
1804 self.assertEqual(dic[d], 2)
1805 self.assertEqual(dic[e], 2)
1806
1807 def test_isoformat(self):
1808 t = self.theclass(4, 5, 1, 123)
1809 self.assertEqual(t.isoformat(), "04:05:01.000123")
1810 self.assertEqual(t.isoformat(), str(t))
1811
1812 t = self.theclass()
1813 self.assertEqual(t.isoformat(), "00:00:00")
1814 self.assertEqual(t.isoformat(), str(t))
1815
1816 t = self.theclass(microsecond=1)
1817 self.assertEqual(t.isoformat(), "00:00:00.000001")
1818 self.assertEqual(t.isoformat(), str(t))
1819
1820 t = self.theclass(microsecond=10)
1821 self.assertEqual(t.isoformat(), "00:00:00.000010")
1822 self.assertEqual(t.isoformat(), str(t))
1823
1824 t = self.theclass(microsecond=100)
1825 self.assertEqual(t.isoformat(), "00:00:00.000100")
1826 self.assertEqual(t.isoformat(), str(t))
1827
1828 t = self.theclass(microsecond=1000)
1829 self.assertEqual(t.isoformat(), "00:00:00.001000")
1830 self.assertEqual(t.isoformat(), str(t))
1831
1832 t = self.theclass(microsecond=10000)
1833 self.assertEqual(t.isoformat(), "00:00:00.010000")
1834 self.assertEqual(t.isoformat(), str(t))
1835
1836 t = self.theclass(microsecond=100000)
1837 self.assertEqual(t.isoformat(), "00:00:00.100000")
1838 self.assertEqual(t.isoformat(), str(t))
1839
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001840 def test_1653736(self):
1841 # verify it doesn't accept extra keyword arguments
1842 t = self.theclass(second=1)
1843 self.assertRaises(TypeError, t.isoformat, foo=3)
1844
Tim Peters2a799bf2002-12-16 20:18:38 +00001845 def test_strftime(self):
1846 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001847 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001848 # A naive object replaces %z and %Z with empty strings.
1849 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1850
Eric Smitha9f7d622008-02-17 19:46:49 +00001851 def test_format(self):
1852 t = self.theclass(1, 2, 3, 4)
1853 self.assertEqual(t.__format__(''), str(t))
1854
1855 # check that a derived class's __str__() gets called
1856 class A(self.theclass):
1857 def __str__(self):
1858 return 'A'
1859 a = A(1, 2, 3, 4)
1860 self.assertEqual(a.__format__(''), 'A')
1861
1862 # check that a derived class's strftime gets called
1863 class B(self.theclass):
1864 def strftime(self, format_spec):
1865 return 'B'
1866 b = B(1, 2, 3, 4)
1867 self.assertEqual(b.__format__(''), str(t))
1868
1869 for fmt in ['%H %M %S',
1870 ]:
1871 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1872 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1873 self.assertEqual(b.__format__(fmt), 'B')
1874
Tim Peters2a799bf2002-12-16 20:18:38 +00001875 def test_str(self):
1876 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1877 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1878 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1879 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1880 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1881
1882 def test_repr(self):
1883 name = 'datetime.' + self.theclass.__name__
1884 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1885 "%s(1, 2, 3, 4)" % name)
1886 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1887 "%s(10, 2, 3, 4000)" % name)
1888 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1889 "%s(0, 2, 3, 400000)" % name)
1890 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1891 "%s(12, 2, 3)" % name)
1892 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1893 "%s(23, 15)" % name)
1894
1895 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +00001896 self.assertIsInstance(self.theclass.min, self.theclass)
1897 self.assertIsInstance(self.theclass.max, self.theclass)
1898 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001899 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001900
1901 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001902 args = 20, 59, 16, 64**2
1903 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001904 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001905 green = pickler.dumps(orig, proto)
1906 derived = unpickler.loads(green)
1907 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001908
Tim Peters604c0132004-06-07 23:04:33 +00001909 def test_pickling_subclass_time(self):
1910 args = 20, 59, 16, 64**2
1911 orig = SubclassTime(*args)
1912 for pickler, unpickler, proto in pickle_choices:
1913 green = pickler.dumps(orig, proto)
1914 derived = unpickler.loads(green)
1915 self.assertEqual(orig, derived)
1916
Tim Peters2a799bf2002-12-16 20:18:38 +00001917 def test_bool(self):
1918 cls = self.theclass
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001919 self.assertTrue(cls(1))
1920 self.assertTrue(cls(0, 1))
1921 self.assertTrue(cls(0, 0, 1))
1922 self.assertTrue(cls(0, 0, 0, 1))
1923 self.assertTrue(not cls(0))
1924 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001925
Tim Peters12bf3392002-12-24 05:41:27 +00001926 def test_replace(self):
1927 cls = self.theclass
1928 args = [1, 2, 3, 4]
1929 base = cls(*args)
1930 self.assertEqual(base, base.replace())
1931
1932 i = 0
1933 for name, newval in (("hour", 5),
1934 ("minute", 6),
1935 ("second", 7),
1936 ("microsecond", 8)):
1937 newargs = args[:]
1938 newargs[i] = newval
1939 expected = cls(*newargs)
1940 got = base.replace(**{name: newval})
1941 self.assertEqual(expected, got)
1942 i += 1
1943
1944 # Out of bounds.
1945 base = cls(1)
1946 self.assertRaises(ValueError, base.replace, hour=24)
1947 self.assertRaises(ValueError, base.replace, minute=-1)
1948 self.assertRaises(ValueError, base.replace, second=100)
1949 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1950
Tim Petersa98924a2003-05-17 05:55:19 +00001951 def test_subclass_time(self):
1952
1953 class C(self.theclass):
1954 theAnswer = 42
1955
1956 def __new__(cls, *args, **kws):
1957 temp = kws.copy()
1958 extra = temp.pop('extra')
1959 result = self.theclass.__new__(cls, *args, **temp)
1960 result.extra = extra
1961 return result
1962
1963 def newmeth(self, start):
1964 return start + self.hour + self.second
1965
1966 args = 4, 5, 6
1967
1968 dt1 = self.theclass(*args)
1969 dt2 = C(*args, **{'extra': 7})
1970
1971 self.assertEqual(dt2.__class__, C)
1972 self.assertEqual(dt2.theAnswer, 42)
1973 self.assertEqual(dt2.extra, 7)
1974 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1975 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1976
Armin Rigof4afb212005-11-07 07:15:48 +00001977 def test_backdoor_resistance(self):
1978 # see TestDate.test_backdoor_resistance().
1979 base = '2:59.0'
1980 for hour_byte in ' ', '9', chr(24), '\xff':
1981 self.assertRaises(TypeError, self.theclass,
1982 hour_byte + base[1:])
1983
Tim Peters855fe882002-12-22 03:43:39 +00001984# A mixin for classes with a tzinfo= argument. Subclasses must define
1985# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001986# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001987class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001988
Tim Petersbad8ff02002-12-30 20:52:32 +00001989 def test_argument_passing(self):
1990 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001991 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001992 class introspective(tzinfo):
1993 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001994 def utcoffset(self, dt):
1995 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001996 dst = utcoffset
1997
1998 obj = cls(1, 2, 3, tzinfo=introspective())
1999
Tim Peters0bf60bd2003-01-08 20:40:01 +00002000 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002001 self.assertEqual(obj.tzname(), expected)
2002
Tim Peters0bf60bd2003-01-08 20:40:01 +00002003 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002004 self.assertEqual(obj.utcoffset(), expected)
2005 self.assertEqual(obj.dst(), expected)
2006
Tim Peters855fe882002-12-22 03:43:39 +00002007 def test_bad_tzinfo_classes(self):
2008 cls = self.theclass
2009 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002010
Tim Peters855fe882002-12-22 03:43:39 +00002011 class NiceTry(object):
2012 def __init__(self): pass
2013 def utcoffset(self, dt): pass
2014 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2015
2016 class BetterTry(tzinfo):
2017 def __init__(self): pass
2018 def utcoffset(self, dt): pass
2019 b = BetterTry()
2020 t = cls(1, 1, 1, tzinfo=b)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002021 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002022
2023 def test_utc_offset_out_of_bounds(self):
2024 class Edgy(tzinfo):
2025 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002026 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002027 def utcoffset(self, dt):
2028 return self.offset
2029
2030 cls = self.theclass
2031 for offset, legit in ((-1440, False),
2032 (-1439, True),
2033 (1439, True),
2034 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002035 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002036 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002037 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002038 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002039 else:
2040 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002041 if legit:
2042 aofs = abs(offset)
2043 h, m = divmod(aofs, 60)
2044 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002045 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002046 t = t.timetz()
2047 self.assertEqual(str(t), "01:02:03" + tag)
2048 else:
2049 self.assertRaises(ValueError, str, t)
2050
2051 def test_tzinfo_classes(self):
2052 cls = self.theclass
2053 class C1(tzinfo):
2054 def utcoffset(self, dt): return None
2055 def dst(self, dt): return None
2056 def tzname(self, dt): return None
2057 for t in (cls(1, 1, 1),
2058 cls(1, 1, 1, tzinfo=None),
2059 cls(1, 1, 1, tzinfo=C1())):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002060 self.assertTrue(t.utcoffset() is None)
2061 self.assertTrue(t.dst() is None)
2062 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002063
Tim Peters855fe882002-12-22 03:43:39 +00002064 class C3(tzinfo):
2065 def utcoffset(self, dt): return timedelta(minutes=-1439)
2066 def dst(self, dt): return timedelta(minutes=1439)
2067 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002068 t = cls(1, 1, 1, tzinfo=C3())
2069 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2070 self.assertEqual(t.dst(), timedelta(minutes=1439))
2071 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002072
2073 # Wrong types.
2074 class C4(tzinfo):
2075 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002076 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002077 def tzname(self, dt): return 0
2078 t = cls(1, 1, 1, tzinfo=C4())
2079 self.assertRaises(TypeError, t.utcoffset)
2080 self.assertRaises(TypeError, t.dst)
2081 self.assertRaises(TypeError, t.tzname)
2082
2083 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002084 class C6(tzinfo):
2085 def utcoffset(self, dt): return timedelta(hours=-24)
2086 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002087 t = cls(1, 1, 1, tzinfo=C6())
2088 self.assertRaises(ValueError, t.utcoffset)
2089 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002090
2091 # Not a whole number of minutes.
2092 class C7(tzinfo):
2093 def utcoffset(self, dt): return timedelta(seconds=61)
2094 def dst(self, dt): return timedelta(microseconds=-81)
2095 t = cls(1, 1, 1, tzinfo=C7())
2096 self.assertRaises(ValueError, t.utcoffset)
2097 self.assertRaises(ValueError, t.dst)
2098
Tim Peters4c0db782002-12-26 05:01:19 +00002099 def test_aware_compare(self):
2100 cls = self.theclass
2101
Tim Peters60c76e42002-12-27 00:41:11 +00002102 # Ensure that utcoffset() gets ignored if the comparands have
2103 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002104 class OperandDependentOffset(tzinfo):
2105 def utcoffset(self, t):
2106 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002107 # d0 and d1 equal after adjustment
2108 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002109 else:
Tim Peters397301e2003-01-02 21:28:08 +00002110 # d2 off in the weeds
2111 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002112
2113 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2114 d0 = base.replace(minute=3)
2115 d1 = base.replace(minute=9)
2116 d2 = base.replace(minute=11)
2117 for x in d0, d1, d2:
2118 for y in d0, d1, d2:
2119 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002120 expected = cmp(x.minute, y.minute)
2121 self.assertEqual(got, expected)
2122
2123 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002124 # Note that a time can't actually have an operand-depedent offset,
2125 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2126 # so skip this test for time.
2127 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002128 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2129 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2130 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2131 for x in d0, d1, d2:
2132 for y in d0, d1, d2:
2133 got = cmp(x, y)
2134 if (x is d0 or x is d1) and (y is d0 or y is d1):
2135 expected = 0
2136 elif x is y is d2:
2137 expected = 0
2138 elif x is d2:
2139 expected = -1
2140 else:
2141 assert y is d2
2142 expected = 1
2143 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002144
Tim Peters855fe882002-12-22 03:43:39 +00002145
Tim Peters0bf60bd2003-01-08 20:40:01 +00002146# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002147class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002148 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002149
2150 def test_empty(self):
2151 t = self.theclass()
2152 self.assertEqual(t.hour, 0)
2153 self.assertEqual(t.minute, 0)
2154 self.assertEqual(t.second, 0)
2155 self.assertEqual(t.microsecond, 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002156 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002157
Tim Peters2a799bf2002-12-16 20:18:38 +00002158 def test_zones(self):
2159 est = FixedOffset(-300, "EST", 1)
2160 utc = FixedOffset(0, "UTC", -2)
2161 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002162 t1 = time( 7, 47, tzinfo=est)
2163 t2 = time(12, 47, tzinfo=utc)
2164 t3 = time(13, 47, tzinfo=met)
2165 t4 = time(microsecond=40)
2166 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002167
2168 self.assertEqual(t1.tzinfo, est)
2169 self.assertEqual(t2.tzinfo, utc)
2170 self.assertEqual(t3.tzinfo, met)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002171 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002172 self.assertEqual(t5.tzinfo, utc)
2173
Tim Peters855fe882002-12-22 03:43:39 +00002174 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2175 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2176 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002177 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002178 self.assertRaises(TypeError, t1.utcoffset, "no args")
2179
2180 self.assertEqual(t1.tzname(), "EST")
2181 self.assertEqual(t2.tzname(), "UTC")
2182 self.assertEqual(t3.tzname(), "MET")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002183 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002184 self.assertRaises(TypeError, t1.tzname, "no args")
2185
Tim Peters855fe882002-12-22 03:43:39 +00002186 self.assertEqual(t1.dst(), timedelta(minutes=1))
2187 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2188 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002189 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002190 self.assertRaises(TypeError, t1.dst, "no args")
2191
2192 self.assertEqual(hash(t1), hash(t2))
2193 self.assertEqual(hash(t1), hash(t3))
2194 self.assertEqual(hash(t2), hash(t3))
2195
2196 self.assertEqual(t1, t2)
2197 self.assertEqual(t1, t3)
2198 self.assertEqual(t2, t3)
2199 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2200 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2201 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2202
2203 self.assertEqual(str(t1), "07:47:00-05:00")
2204 self.assertEqual(str(t2), "12:47:00+00:00")
2205 self.assertEqual(str(t3), "13:47:00+01:00")
2206 self.assertEqual(str(t4), "00:00:00.000040")
2207 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2208
2209 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2210 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2211 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2212 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2213 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2214
Tim Peters0bf60bd2003-01-08 20:40:01 +00002215 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002216 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2217 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2218 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2219 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2220 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2221
2222 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2223 "07:47:00 %Z=EST %z=-0500")
2224 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2225 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2226
2227 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002228 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002229 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2230 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2231
Tim Petersb92bb712002-12-21 17:44:07 +00002232 # Check that an invalid tzname result raises an exception.
2233 class Badtzname(tzinfo):
2234 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002235 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002236 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2237 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002238
2239 def test_hash_edge_cases(self):
2240 # Offsets that overflow a basic time.
2241 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2242 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2243 self.assertEqual(hash(t1), hash(t2))
2244
2245 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2246 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2247 self.assertEqual(hash(t1), hash(t2))
2248
Tim Peters2a799bf2002-12-16 20:18:38 +00002249 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002250 # Try one without a tzinfo.
2251 args = 20, 59, 16, 64**2
2252 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002253 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002254 green = pickler.dumps(orig, proto)
2255 derived = unpickler.loads(green)
2256 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002257
2258 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002259 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002260 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002261 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002262 green = pickler.dumps(orig, proto)
2263 derived = unpickler.loads(green)
2264 self.assertEqual(orig, derived)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002265 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002266 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2267 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002268
2269 def test_more_bool(self):
2270 # Test cases with non-None tzinfo.
2271 cls = self.theclass
2272
2273 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002274 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002275
2276 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002277 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002278
2279 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002280 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002281
2282 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002283 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002284
2285 # Mostly ensuring this doesn't overflow internally.
2286 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002287 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002288
2289 # But this should yield a value error -- the utcoffset is bogus.
2290 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2291 self.assertRaises(ValueError, lambda: bool(t))
2292
2293 # Likewise.
2294 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2295 self.assertRaises(ValueError, lambda: bool(t))
2296
Tim Peters12bf3392002-12-24 05:41:27 +00002297 def test_replace(self):
2298 cls = self.theclass
2299 z100 = FixedOffset(100, "+100")
2300 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2301 args = [1, 2, 3, 4, z100]
2302 base = cls(*args)
2303 self.assertEqual(base, base.replace())
2304
2305 i = 0
2306 for name, newval in (("hour", 5),
2307 ("minute", 6),
2308 ("second", 7),
2309 ("microsecond", 8),
2310 ("tzinfo", zm200)):
2311 newargs = args[:]
2312 newargs[i] = newval
2313 expected = cls(*newargs)
2314 got = base.replace(**{name: newval})
2315 self.assertEqual(expected, got)
2316 i += 1
2317
2318 # Ensure we can get rid of a tzinfo.
2319 self.assertEqual(base.tzname(), "+100")
2320 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002321 self.assertTrue(base2.tzinfo is None)
2322 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002323
2324 # Ensure we can add one.
2325 base3 = base2.replace(tzinfo=z100)
2326 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002327 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002328
2329 # Out of bounds.
2330 base = cls(1)
2331 self.assertRaises(ValueError, base.replace, hour=24)
2332 self.assertRaises(ValueError, base.replace, minute=-1)
2333 self.assertRaises(ValueError, base.replace, second=100)
2334 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2335
Tim Peters60c76e42002-12-27 00:41:11 +00002336 def test_mixed_compare(self):
2337 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002338 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002339 self.assertEqual(t1, t2)
2340 t2 = t2.replace(tzinfo=None)
2341 self.assertEqual(t1, t2)
2342 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2343 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002344 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2345 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002346
Tim Peters0bf60bd2003-01-08 20:40:01 +00002347 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002348 class Varies(tzinfo):
2349 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002350 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002351 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002352 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002353 return self.offset
2354
2355 v = Varies()
2356 t1 = t2.replace(tzinfo=v)
2357 t2 = t2.replace(tzinfo=v)
2358 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2359 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2360 self.assertEqual(t1, t2)
2361
2362 # But if they're not identical, it isn't ignored.
2363 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002364 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002365
Tim Petersa98924a2003-05-17 05:55:19 +00002366 def test_subclass_timetz(self):
2367
2368 class C(self.theclass):
2369 theAnswer = 42
2370
2371 def __new__(cls, *args, **kws):
2372 temp = kws.copy()
2373 extra = temp.pop('extra')
2374 result = self.theclass.__new__(cls, *args, **temp)
2375 result.extra = extra
2376 return result
2377
2378 def newmeth(self, start):
2379 return start + self.hour + self.second
2380
2381 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2382
2383 dt1 = self.theclass(*args)
2384 dt2 = C(*args, **{'extra': 7})
2385
2386 self.assertEqual(dt2.__class__, C)
2387 self.assertEqual(dt2.theAnswer, 42)
2388 self.assertEqual(dt2.extra, 7)
2389 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2390 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2391
Tim Peters4c0db782002-12-26 05:01:19 +00002392
Tim Peters0bf60bd2003-01-08 20:40:01 +00002393# Testing datetime objects with a non-None tzinfo.
2394
Collin Winterc2898c52007-04-25 17:29:52 +00002395class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002396 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002397
2398 def test_trivial(self):
2399 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2400 self.assertEqual(dt.year, 1)
2401 self.assertEqual(dt.month, 2)
2402 self.assertEqual(dt.day, 3)
2403 self.assertEqual(dt.hour, 4)
2404 self.assertEqual(dt.minute, 5)
2405 self.assertEqual(dt.second, 6)
2406 self.assertEqual(dt.microsecond, 7)
2407 self.assertEqual(dt.tzinfo, None)
2408
2409 def test_even_more_compare(self):
2410 # The test_compare() and test_more_compare() inherited from TestDate
2411 # and TestDateTime covered non-tzinfo cases.
2412
2413 # Smallest possible after UTC adjustment.
2414 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2415 # Largest possible after UTC adjustment.
2416 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2417 tzinfo=FixedOffset(-1439, ""))
2418
2419 # Make sure those compare correctly, and w/o overflow.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002420 self.assertTrue(t1 < t2)
2421 self.assertTrue(t1 != t2)
2422 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002423
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002424 self.assertTrue(t1 == t1)
2425 self.assertTrue(t2 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002426
2427 # Equal afer adjustment.
2428 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2429 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2430 self.assertEqual(t1, t2)
2431
2432 # Change t1 not to subtract a minute, and t1 should be larger.
2433 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002434 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002435
2436 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2437 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002438 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002439
2440 # Back to the original t1, but make seconds resolve it.
2441 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2442 second=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002443 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002444
2445 # Likewise, but make microseconds resolve it.
2446 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2447 microsecond=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002448 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002449
2450 # Make t2 naive and it should fail.
2451 t2 = self.theclass.min
2452 self.assertRaises(TypeError, lambda: t1 == t2)
2453 self.assertEqual(t2, t2)
2454
2455 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2456 class Naive(tzinfo):
2457 def utcoffset(self, dt): return None
2458 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2459 self.assertRaises(TypeError, lambda: t1 == t2)
2460 self.assertEqual(t2, t2)
2461
2462 # OTOH, it's OK to compare two of these mixing the two ways of being
2463 # naive.
2464 t1 = self.theclass(5, 6, 7)
2465 self.assertEqual(t1, t2)
2466
2467 # Try a bogus uctoffset.
2468 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002469 def utcoffset(self, dt):
2470 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002471 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2472 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002473 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002474
Tim Peters2a799bf2002-12-16 20:18:38 +00002475 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002476 # Try one without a tzinfo.
2477 args = 6, 7, 23, 20, 59, 1, 64**2
2478 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002479 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002480 green = pickler.dumps(orig, proto)
2481 derived = unpickler.loads(green)
2482 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002483
2484 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002485 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002486 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002487 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
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)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002492 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002493 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2494 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002495
2496 def test_extreme_hashes(self):
2497 # If an attempt is made to hash these via subtracting the offset
2498 # then hashing a datetime object, OverflowError results. The
2499 # Python implementation used to blow up here.
2500 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2501 hash(t)
2502 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2503 tzinfo=FixedOffset(-1439, ""))
2504 hash(t)
2505
2506 # OTOH, an OOB offset should blow up.
2507 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2508 self.assertRaises(ValueError, hash, t)
2509
2510 def test_zones(self):
2511 est = FixedOffset(-300, "EST")
2512 utc = FixedOffset(0, "UTC")
2513 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002514 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2515 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2516 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002517 self.assertEqual(t1.tzinfo, est)
2518 self.assertEqual(t2.tzinfo, utc)
2519 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002520 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2521 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2522 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002523 self.assertEqual(t1.tzname(), "EST")
2524 self.assertEqual(t2.tzname(), "UTC")
2525 self.assertEqual(t3.tzname(), "MET")
2526 self.assertEqual(hash(t1), hash(t2))
2527 self.assertEqual(hash(t1), hash(t3))
2528 self.assertEqual(hash(t2), hash(t3))
2529 self.assertEqual(t1, t2)
2530 self.assertEqual(t1, t3)
2531 self.assertEqual(t2, t3)
2532 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2533 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2534 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002535 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002536 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2537 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2538 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2539
2540 def test_combine(self):
2541 met = FixedOffset(60, "MET")
2542 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002543 tz = time(18, 45, 3, 1234, tzinfo=met)
2544 dt = datetime.combine(d, tz)
2545 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002546 tzinfo=met))
2547
2548 def test_extract(self):
2549 met = FixedOffset(60, "MET")
2550 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2551 self.assertEqual(dt.date(), date(2002, 3, 4))
2552 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002553 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002554
2555 def test_tz_aware_arithmetic(self):
2556 import random
2557
2558 now = self.theclass.now()
2559 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002560 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002561 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002562 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002563 self.assertEqual(nowaware.timetz(), timeaware)
2564
2565 # Can't mix aware and non-aware.
2566 self.assertRaises(TypeError, lambda: now - nowaware)
2567 self.assertRaises(TypeError, lambda: nowaware - now)
2568
Tim Peters0bf60bd2003-01-08 20:40:01 +00002569 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002570 self.assertRaises(TypeError, lambda: now + nowaware)
2571 self.assertRaises(TypeError, lambda: nowaware + now)
2572 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2573
2574 # Subtracting should yield 0.
2575 self.assertEqual(now - now, timedelta(0))
2576 self.assertEqual(nowaware - nowaware, timedelta(0))
2577
2578 # Adding a delta should preserve tzinfo.
2579 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2580 nowawareplus = nowaware + delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002581 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002582 nowawareplus2 = delta + nowaware
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002583 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002584 self.assertEqual(nowawareplus, nowawareplus2)
2585
2586 # that - delta should be what we started with, and that - what we
2587 # started with should be delta.
2588 diff = nowawareplus - delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002589 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002590 self.assertEqual(nowaware, diff)
2591 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2592 self.assertEqual(nowawareplus - nowaware, delta)
2593
2594 # Make up a random timezone.
2595 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002596 # Attach it to nowawareplus.
2597 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002598 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002599 # Make sure the difference takes the timezone adjustments into account.
2600 got = nowaware - nowawareplus
2601 # Expected: (nowaware base - nowaware offset) -
2602 # (nowawareplus base - nowawareplus offset) =
2603 # (nowaware base - nowawareplus base) +
2604 # (nowawareplus offset - nowaware offset) =
2605 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002606 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002607 self.assertEqual(got, expected)
2608
2609 # Try max possible difference.
2610 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2611 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2612 tzinfo=FixedOffset(-1439, "max"))
2613 maxdiff = max - min
2614 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2615 timedelta(minutes=2*1439))
2616
2617 def test_tzinfo_now(self):
2618 meth = self.theclass.now
2619 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2620 base = meth()
2621 # Try with and without naming the keyword.
2622 off42 = FixedOffset(42, "42")
2623 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002624 again = meth(tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002625 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002626 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002627 # Bad argument with and w/o naming the keyword.
2628 self.assertRaises(TypeError, meth, 16)
2629 self.assertRaises(TypeError, meth, tzinfo=16)
2630 # Bad keyword name.
2631 self.assertRaises(TypeError, meth, tinfo=off42)
2632 # Too many args.
2633 self.assertRaises(TypeError, meth, off42, off42)
2634
Tim Peters10cadce2003-01-23 19:58:02 +00002635 # We don't know which time zone we're in, and don't have a tzinfo
2636 # class to represent it, so seeing whether a tz argument actually
2637 # does a conversion is tricky.
2638 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2639 utc = FixedOffset(0, "utc", 0)
2640 for dummy in range(3):
2641 now = datetime.now(weirdtz)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002642 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002643 utcnow = datetime.utcnow().replace(tzinfo=utc)
2644 now2 = utcnow.astimezone(weirdtz)
2645 if abs(now - now2) < timedelta(seconds=30):
2646 break
2647 # Else the code is broken, or more than 30 seconds passed between
2648 # calls; assuming the latter, just try again.
2649 else:
2650 # Three strikes and we're out.
2651 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2652
Tim Peters2a799bf2002-12-16 20:18:38 +00002653 def test_tzinfo_fromtimestamp(self):
2654 import time
2655 meth = self.theclass.fromtimestamp
2656 ts = time.time()
2657 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2658 base = meth(ts)
2659 # Try with and without naming the keyword.
2660 off42 = FixedOffset(42, "42")
2661 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002662 again = meth(ts, tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002663 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002664 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002665 # Bad argument with and w/o naming the keyword.
2666 self.assertRaises(TypeError, meth, ts, 16)
2667 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2668 # Bad keyword name.
2669 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2670 # Too many args.
2671 self.assertRaises(TypeError, meth, ts, off42, off42)
2672 # Too few args.
2673 self.assertRaises(TypeError, meth)
2674
Tim Peters2a44a8d2003-01-23 20:53:10 +00002675 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002676 timestamp = 1000000000
2677 utcdatetime = datetime.utcfromtimestamp(timestamp)
2678 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2679 # But on some flavor of Mac, it's nowhere near that. So we can't have
2680 # any idea here what time that actually is, we can only test that
2681 # relative changes match.
2682 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2683 tz = FixedOffset(utcoffset, "tz", 0)
2684 expected = utcdatetime + utcoffset
2685 got = datetime.fromtimestamp(timestamp, tz)
2686 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002687
Tim Peters2a799bf2002-12-16 20:18:38 +00002688 def test_tzinfo_utcnow(self):
2689 meth = self.theclass.utcnow
2690 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2691 base = meth()
2692 # Try with and without naming the keyword; for whatever reason,
2693 # utcnow() doesn't accept a tzinfo argument.
2694 off42 = FixedOffset(42, "42")
2695 self.assertRaises(TypeError, meth, off42)
2696 self.assertRaises(TypeError, meth, tzinfo=off42)
2697
2698 def test_tzinfo_utcfromtimestamp(self):
2699 import time
2700 meth = self.theclass.utcfromtimestamp
2701 ts = time.time()
2702 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2703 base = meth(ts)
2704 # Try with and without naming the keyword; for whatever reason,
2705 # utcfromtimestamp() doesn't accept a tzinfo argument.
2706 off42 = FixedOffset(42, "42")
2707 self.assertRaises(TypeError, meth, ts, off42)
2708 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2709
2710 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002711 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002712 # DST flag.
2713 class DST(tzinfo):
2714 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002715 if isinstance(dstvalue, int):
2716 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002717 self.dstvalue = dstvalue
2718 def dst(self, dt):
2719 return self.dstvalue
2720
2721 cls = self.theclass
2722 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2723 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2724 t = d.timetuple()
2725 self.assertEqual(1, t.tm_year)
2726 self.assertEqual(1, t.tm_mon)
2727 self.assertEqual(1, t.tm_mday)
2728 self.assertEqual(10, t.tm_hour)
2729 self.assertEqual(20, t.tm_min)
2730 self.assertEqual(30, t.tm_sec)
2731 self.assertEqual(0, t.tm_wday)
2732 self.assertEqual(1, t.tm_yday)
2733 self.assertEqual(flag, t.tm_isdst)
2734
2735 # dst() returns wrong type.
2736 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2737
2738 # dst() at the edge.
2739 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2740 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2741
2742 # dst() out of range.
2743 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2744 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2745
2746 def test_utctimetuple(self):
2747 class DST(tzinfo):
2748 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002749 if isinstance(dstvalue, int):
2750 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002751 self.dstvalue = dstvalue
2752 def dst(self, dt):
2753 return self.dstvalue
2754
2755 cls = self.theclass
2756 # This can't work: DST didn't implement utcoffset.
2757 self.assertRaises(NotImplementedError,
2758 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2759
2760 class UOFS(DST):
2761 def __init__(self, uofs, dofs=None):
2762 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002763 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002764 def utcoffset(self, dt):
2765 return self.uofs
2766
2767 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2768 # in effect for a UTC time.
2769 for dstvalue in -33, 33, 0, None:
2770 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2771 t = d.utctimetuple()
2772 self.assertEqual(d.year, t.tm_year)
2773 self.assertEqual(d.month, t.tm_mon)
2774 self.assertEqual(d.day, t.tm_mday)
2775 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2776 self.assertEqual(13, t.tm_min)
2777 self.assertEqual(d.second, t.tm_sec)
2778 self.assertEqual(d.weekday(), t.tm_wday)
2779 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2780 t.tm_yday)
2781 self.assertEqual(0, t.tm_isdst)
2782
2783 # At the edges, UTC adjustment can normalize into years out-of-range
2784 # for a datetime object. Ensure that a correct timetuple is
2785 # created anyway.
2786 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2787 # That goes back 1 minute less than a full day.
2788 t = tiny.utctimetuple()
2789 self.assertEqual(t.tm_year, MINYEAR-1)
2790 self.assertEqual(t.tm_mon, 12)
2791 self.assertEqual(t.tm_mday, 31)
2792 self.assertEqual(t.tm_hour, 0)
2793 self.assertEqual(t.tm_min, 1)
2794 self.assertEqual(t.tm_sec, 37)
2795 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2796 self.assertEqual(t.tm_isdst, 0)
2797
2798 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2799 # That goes forward 1 minute less than a full day.
2800 t = huge.utctimetuple()
2801 self.assertEqual(t.tm_year, MAXYEAR+1)
2802 self.assertEqual(t.tm_mon, 1)
2803 self.assertEqual(t.tm_mday, 1)
2804 self.assertEqual(t.tm_hour, 23)
2805 self.assertEqual(t.tm_min, 58)
2806 self.assertEqual(t.tm_sec, 37)
2807 self.assertEqual(t.tm_yday, 1)
2808 self.assertEqual(t.tm_isdst, 0)
2809
2810 def test_tzinfo_isoformat(self):
2811 zero = FixedOffset(0, "+00:00")
2812 plus = FixedOffset(220, "+03:40")
2813 minus = FixedOffset(-231, "-03:51")
2814 unknown = FixedOffset(None, "")
2815
2816 cls = self.theclass
2817 datestr = '0001-02-03'
2818 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002819 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002820 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2821 timestr = '04:05:59' + (us and '.987001' or '')
2822 ofsstr = ofs is not None and d.tzname() or ''
2823 tailstr = timestr + ofsstr
2824 iso = d.isoformat()
2825 self.assertEqual(iso, datestr + 'T' + tailstr)
2826 self.assertEqual(iso, d.isoformat('T'))
2827 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2828 self.assertEqual(str(d), datestr + ' ' + tailstr)
2829
Tim Peters12bf3392002-12-24 05:41:27 +00002830 def test_replace(self):
2831 cls = self.theclass
2832 z100 = FixedOffset(100, "+100")
2833 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2834 args = [1, 2, 3, 4, 5, 6, 7, z100]
2835 base = cls(*args)
2836 self.assertEqual(base, base.replace())
2837
2838 i = 0
2839 for name, newval in (("year", 2),
2840 ("month", 3),
2841 ("day", 4),
2842 ("hour", 5),
2843 ("minute", 6),
2844 ("second", 7),
2845 ("microsecond", 8),
2846 ("tzinfo", zm200)):
2847 newargs = args[:]
2848 newargs[i] = newval
2849 expected = cls(*newargs)
2850 got = base.replace(**{name: newval})
2851 self.assertEqual(expected, got)
2852 i += 1
2853
2854 # Ensure we can get rid of a tzinfo.
2855 self.assertEqual(base.tzname(), "+100")
2856 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002857 self.assertTrue(base2.tzinfo is None)
2858 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002859
2860 # Ensure we can add one.
2861 base3 = base2.replace(tzinfo=z100)
2862 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002863 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002864
2865 # Out of bounds.
2866 base = cls(2000, 2, 29)
2867 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002868
Tim Peters80475bb2002-12-25 07:40:55 +00002869 def test_more_astimezone(self):
2870 # The inherited test_astimezone covered some trivial and error cases.
2871 fnone = FixedOffset(None, "None")
2872 f44m = FixedOffset(44, "44")
2873 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2874
Tim Peters10cadce2003-01-23 19:58:02 +00002875 dt = self.theclass.now(tz=f44m)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002876 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002877 # Replacing with degenerate tzinfo raises an exception.
2878 self.assertRaises(ValueError, dt.astimezone, fnone)
2879 # Ditto with None tz.
2880 self.assertRaises(TypeError, dt.astimezone, None)
2881 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002882 x = dt.astimezone(dt.tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002883 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002884 self.assertEqual(x.date(), dt.date())
2885 self.assertEqual(x.time(), dt.time())
2886
2887 # Replacing with different tzinfo does adjust.
2888 got = dt.astimezone(fm5h)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002889 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002890 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2891 expected = dt - dt.utcoffset() # in effect, convert to UTC
2892 expected += fm5h.utcoffset(dt) # and from there to local time
2893 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2894 self.assertEqual(got.date(), expected.date())
2895 self.assertEqual(got.time(), expected.time())
2896 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002897 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002898 self.assertEqual(got, expected)
2899
Tim Peters4c0db782002-12-26 05:01:19 +00002900 def test_aware_subtract(self):
2901 cls = self.theclass
2902
Tim Peters60c76e42002-12-27 00:41:11 +00002903 # Ensure that utcoffset() is ignored when the operands have the
2904 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002905 class OperandDependentOffset(tzinfo):
2906 def utcoffset(self, t):
2907 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002908 # d0 and d1 equal after adjustment
2909 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002910 else:
Tim Peters397301e2003-01-02 21:28:08 +00002911 # d2 off in the weeds
2912 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002913
2914 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2915 d0 = base.replace(minute=3)
2916 d1 = base.replace(minute=9)
2917 d2 = base.replace(minute=11)
2918 for x in d0, d1, d2:
2919 for y in d0, d1, d2:
2920 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002921 expected = timedelta(minutes=x.minute - y.minute)
2922 self.assertEqual(got, expected)
2923
2924 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2925 # ignored.
2926 base = cls(8, 9, 10, 11, 12, 13, 14)
2927 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2928 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2929 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2930 for x in d0, d1, d2:
2931 for y in d0, d1, d2:
2932 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002933 if (x is d0 or x is d1) and (y is d0 or y is d1):
2934 expected = timedelta(0)
2935 elif x is y is d2:
2936 expected = timedelta(0)
2937 elif x is d2:
2938 expected = timedelta(minutes=(11-59)-0)
2939 else:
2940 assert y is d2
2941 expected = timedelta(minutes=0-(11-59))
2942 self.assertEqual(got, expected)
2943
Tim Peters60c76e42002-12-27 00:41:11 +00002944 def test_mixed_compare(self):
2945 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002946 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002947 self.assertEqual(t1, t2)
2948 t2 = t2.replace(tzinfo=None)
2949 self.assertEqual(t1, t2)
2950 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2951 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002952 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2953 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002954
Tim Peters0bf60bd2003-01-08 20:40:01 +00002955 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002956 class Varies(tzinfo):
2957 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002958 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002959 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002960 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002961 return self.offset
2962
2963 v = Varies()
2964 t1 = t2.replace(tzinfo=v)
2965 t2 = t2.replace(tzinfo=v)
2966 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2967 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2968 self.assertEqual(t1, t2)
2969
2970 # But if they're not identical, it isn't ignored.
2971 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002972 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002973
Tim Petersa98924a2003-05-17 05:55:19 +00002974 def test_subclass_datetimetz(self):
2975
2976 class C(self.theclass):
2977 theAnswer = 42
2978
2979 def __new__(cls, *args, **kws):
2980 temp = kws.copy()
2981 extra = temp.pop('extra')
2982 result = self.theclass.__new__(cls, *args, **temp)
2983 result.extra = extra
2984 return result
2985
2986 def newmeth(self, start):
2987 return start + self.hour + self.year
2988
2989 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2990
2991 dt1 = self.theclass(*args)
2992 dt2 = C(*args, **{'extra': 7})
2993
2994 self.assertEqual(dt2.__class__, C)
2995 self.assertEqual(dt2.theAnswer, 42)
2996 self.assertEqual(dt2.extra, 7)
2997 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2998 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2999
Tim Peters621818b2002-12-29 23:44:49 +00003000# Pain to set up DST-aware tzinfo classes.
3001
3002def first_sunday_on_or_after(dt):
3003 days_to_go = 6 - dt.weekday()
3004 if days_to_go:
3005 dt += timedelta(days_to_go)
3006 return dt
3007
3008ZERO = timedelta(0)
3009HOUR = timedelta(hours=1)
3010DAY = timedelta(days=1)
3011# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3012DSTSTART = datetime(1, 4, 1, 2)
3013# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003014# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3015# being standard time on that day, there is no spelling in local time of
3016# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3017DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003018
3019class USTimeZone(tzinfo):
3020
3021 def __init__(self, hours, reprname, stdname, dstname):
3022 self.stdoffset = timedelta(hours=hours)
3023 self.reprname = reprname
3024 self.stdname = stdname
3025 self.dstname = dstname
3026
3027 def __repr__(self):
3028 return self.reprname
3029
3030 def tzname(self, dt):
3031 if self.dst(dt):
3032 return self.dstname
3033 else:
3034 return self.stdname
3035
3036 def utcoffset(self, dt):
3037 return self.stdoffset + self.dst(dt)
3038
3039 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003040 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003041 # An exception instead may be sensible here, in one or more of
3042 # the cases.
3043 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003044 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003045
3046 # Find first Sunday in April.
3047 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3048 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3049
3050 # Find last Sunday in October.
3051 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3052 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3053
Tim Peters621818b2002-12-29 23:44:49 +00003054 # Can't compare naive to aware objects, so strip the timezone from
3055 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003056 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003057 return HOUR
3058 else:
3059 return ZERO
3060
Tim Peters521fc152002-12-31 17:36:56 +00003061Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3062Central = USTimeZone(-6, "Central", "CST", "CDT")
3063Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3064Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003065utc_real = FixedOffset(0, "UTC", 0)
3066# For better test coverage, we want another flavor of UTC that's west of
3067# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003068utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003069
3070class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003071 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003072 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003073 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003074
Tim Peters0bf60bd2003-01-08 20:40:01 +00003075 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003076
Tim Peters521fc152002-12-31 17:36:56 +00003077 # Check a time that's inside DST.
3078 def checkinside(self, dt, tz, utc, dston, dstoff):
3079 self.assertEqual(dt.dst(), HOUR)
3080
3081 # Conversion to our own timezone is always an identity.
3082 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003083
3084 asutc = dt.astimezone(utc)
3085 there_and_back = asutc.astimezone(tz)
3086
3087 # Conversion to UTC and back isn't always an identity here,
3088 # because there are redundant spellings (in local time) of
3089 # UTC time when DST begins: the clock jumps from 1:59:59
3090 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3091 # make sense then. The classes above treat 2:MM:SS as
3092 # daylight time then (it's "after 2am"), really an alias
3093 # for 1:MM:SS standard time. The latter form is what
3094 # conversion back from UTC produces.
3095 if dt.date() == dston.date() and dt.hour == 2:
3096 # We're in the redundant hour, and coming back from
3097 # UTC gives the 1:MM:SS standard-time spelling.
3098 self.assertEqual(there_and_back + HOUR, dt)
3099 # Although during was considered to be in daylight
3100 # time, there_and_back is not.
3101 self.assertEqual(there_and_back.dst(), ZERO)
3102 # They're the same times in UTC.
3103 self.assertEqual(there_and_back.astimezone(utc),
3104 dt.astimezone(utc))
3105 else:
3106 # We're not in the redundant hour.
3107 self.assertEqual(dt, there_and_back)
3108
Tim Peters327098a2003-01-20 22:54:38 +00003109 # Because we have a redundant spelling when DST begins, there is
3110 # (unforunately) an hour when DST ends that can't be spelled at all in
3111 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3112 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3113 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3114 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3115 # expressed in local time. Nevertheless, we want conversion back
3116 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003117 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003118 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003119 if dt.date() == dstoff.date() and dt.hour == 0:
3120 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003121 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003122 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3123 nexthour_utc += HOUR
3124 nexthour_tz = nexthour_utc.astimezone(tz)
3125 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003126 else:
Tim Peters327098a2003-01-20 22:54:38 +00003127 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003128
3129 # Check a time that's outside DST.
3130 def checkoutside(self, dt, tz, utc):
3131 self.assertEqual(dt.dst(), ZERO)
3132
3133 # Conversion to our own timezone is always an identity.
3134 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003135
3136 # Converting to UTC and back is an identity too.
3137 asutc = dt.astimezone(utc)
3138 there_and_back = asutc.astimezone(tz)
3139 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003140
Tim Peters1024bf82002-12-30 17:09:40 +00003141 def convert_between_tz_and_utc(self, tz, utc):
3142 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003143 # Because 1:MM on the day DST ends is taken as being standard time,
3144 # there is no spelling in tz for the last hour of daylight time.
3145 # For purposes of the test, the last hour of DST is 0:MM, which is
3146 # taken as being daylight time (and 1:MM is taken as being standard
3147 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003148 dstoff = self.dstoff.replace(tzinfo=tz)
3149 for delta in (timedelta(weeks=13),
3150 DAY,
3151 HOUR,
3152 timedelta(minutes=1),
3153 timedelta(microseconds=1)):
3154
Tim Peters521fc152002-12-31 17:36:56 +00003155 self.checkinside(dston, tz, utc, dston, dstoff)
3156 for during in dston + delta, dstoff - delta:
3157 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003158
Tim Peters521fc152002-12-31 17:36:56 +00003159 self.checkoutside(dstoff, tz, utc)
3160 for outside in dston - delta, dstoff + delta:
3161 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003162
Tim Peters621818b2002-12-29 23:44:49 +00003163 def test_easy(self):
3164 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003165 self.convert_between_tz_and_utc(Eastern, utc_real)
3166 self.convert_between_tz_and_utc(Pacific, utc_real)
3167 self.convert_between_tz_and_utc(Eastern, utc_fake)
3168 self.convert_between_tz_and_utc(Pacific, utc_fake)
3169 # The next is really dancing near the edge. It works because
3170 # Pacific and Eastern are far enough apart that their "problem
3171 # hours" don't overlap.
3172 self.convert_between_tz_and_utc(Eastern, Pacific)
3173 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003174 # OTOH, these fail! Don't enable them. The difficulty is that
3175 # the edge case tests assume that every hour is representable in
3176 # the "utc" class. This is always true for a fixed-offset tzinfo
3177 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3178 # For these adjacent DST-aware time zones, the range of time offsets
3179 # tested ends up creating hours in the one that aren't representable
3180 # in the other. For the same reason, we would see failures in the
3181 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3182 # offset deltas in convert_between_tz_and_utc().
3183 #
3184 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3185 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003186
Tim Petersf3615152003-01-01 21:51:37 +00003187 def test_tricky(self):
3188 # 22:00 on day before daylight starts.
3189 fourback = self.dston - timedelta(hours=4)
3190 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003191 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003192 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3193 # 2", we should get the 3 spelling.
3194 # If we plug 22:00 the day before into Eastern, it "looks like std
3195 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3196 # to 22:00 lands on 2:00, which makes no sense in local time (the
3197 # local clock jumps from 1 to 3). The point here is to make sure we
3198 # get the 3 spelling.
3199 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003200 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003201 self.assertEqual(expected, got)
3202
3203 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3204 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003205 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003206 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3207 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3208 # spelling.
3209 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003210 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003211 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003212
Tim Petersadf64202003-01-04 06:03:15 +00003213 # Now on the day DST ends, we want "repeat an hour" behavior.
3214 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3215 # EST 23:MM 0:MM 1:MM 2:MM
3216 # EDT 0:MM 1:MM 2:MM 3:MM
3217 # wall 0:MM 1:MM 1:MM 2:MM against these
3218 for utc in utc_real, utc_fake:
3219 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003220 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003221 # Convert that to UTC.
3222 first_std_hour -= tz.utcoffset(None)
3223 # Adjust for possibly fake UTC.
3224 asutc = first_std_hour + utc.utcoffset(None)
3225 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3226 # tz=Eastern.
3227 asutcbase = asutc.replace(tzinfo=utc)
3228 for tzhour in (0, 1, 1, 2):
3229 expectedbase = self.dstoff.replace(hour=tzhour)
3230 for minute in 0, 30, 59:
3231 expected = expectedbase.replace(minute=minute)
3232 asutc = asutcbase.replace(minute=minute)
3233 astz = asutc.astimezone(tz)
3234 self.assertEqual(astz.replace(tzinfo=None), expected)
3235 asutcbase += HOUR
3236
3237
Tim Peters710fb152003-01-02 19:35:54 +00003238 def test_bogus_dst(self):
3239 class ok(tzinfo):
3240 def utcoffset(self, dt): return HOUR
3241 def dst(self, dt): return HOUR
3242
3243 now = self.theclass.now().replace(tzinfo=utc_real)
3244 # Doesn't blow up.
3245 now.astimezone(ok())
3246
3247 # Does blow up.
3248 class notok(ok):
3249 def dst(self, dt): return None
3250 self.assertRaises(ValueError, now.astimezone, notok())
3251
Tim Peters52dcce22003-01-23 16:36:11 +00003252 def test_fromutc(self):
3253 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3254 now = datetime.utcnow().replace(tzinfo=utc_real)
3255 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3256 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3257 enow = Eastern.fromutc(now) # doesn't blow up
3258 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3259 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3260 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3261
3262 # Always converts UTC to standard time.
3263 class FauxUSTimeZone(USTimeZone):
3264 def fromutc(self, dt):
3265 return dt + self.stdoffset
3266 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3267
3268 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3269 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3270 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3271
3272 # Check around DST start.
3273 start = self.dston.replace(hour=4, tzinfo=Eastern)
3274 fstart = start.replace(tzinfo=FEastern)
3275 for wall in 23, 0, 1, 3, 4, 5:
3276 expected = start.replace(hour=wall)
3277 if wall == 23:
3278 expected -= timedelta(days=1)
3279 got = Eastern.fromutc(start)
3280 self.assertEqual(expected, got)
3281
3282 expected = fstart + FEastern.stdoffset
3283 got = FEastern.fromutc(fstart)
3284 self.assertEqual(expected, got)
3285
3286 # Ensure astimezone() calls fromutc() too.
3287 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3288 self.assertEqual(expected, got)
3289
3290 start += HOUR
3291 fstart += HOUR
3292
3293 # Check around DST end.
3294 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3295 fstart = start.replace(tzinfo=FEastern)
3296 for wall in 0, 1, 1, 2, 3, 4:
3297 expected = start.replace(hour=wall)
3298 got = Eastern.fromutc(start)
3299 self.assertEqual(expected, got)
3300
3301 expected = fstart + FEastern.stdoffset
3302 got = FEastern.fromutc(fstart)
3303 self.assertEqual(expected, got)
3304
3305 # Ensure astimezone() calls fromutc() too.
3306 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3307 self.assertEqual(expected, got)
3308
3309 start += HOUR
3310 fstart += HOUR
3311
Tim Peters710fb152003-01-02 19:35:54 +00003312
Tim Peters528ca532004-09-16 01:30:50 +00003313#############################################################################
3314# oddballs
3315
3316class Oddballs(unittest.TestCase):
3317
3318 def test_bug_1028306(self):
3319 # Trying to compare a date to a datetime should act like a mixed-
3320 # type comparison, despite that datetime is a subclass of date.
3321 as_date = date.today()
3322 as_datetime = datetime.combine(as_date, time())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003323 self.assertTrue(as_date != as_datetime)
3324 self.assertTrue(as_datetime != as_date)
3325 self.assertTrue(not as_date == as_datetime)
3326 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003327 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3328 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3329 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3330 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3331 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3332 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3333 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3334 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3335
3336 # Neverthelss, comparison should work with the base-class (date)
3337 # projection if use of a date method is forced.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003338 self.assertTrue(as_date.__eq__(as_datetime))
Tim Peters528ca532004-09-16 01:30:50 +00003339 different_day = (as_date.day + 1) % 20 + 1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003340 self.assertTrue(not as_date.__eq__(as_datetime.replace(day=
Tim Peters528ca532004-09-16 01:30:50 +00003341 different_day)))
3342
3343 # And date should compare with other subclasses of date. If a
3344 # subclass wants to stop this, it's up to the subclass to do so.
3345 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3346 self.assertEqual(as_date, date_sc)
3347 self.assertEqual(date_sc, as_date)
3348
3349 # Ditto for datetimes.
3350 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3351 as_date.day, 0, 0, 0)
3352 self.assertEqual(as_datetime, datetime_sc)
3353 self.assertEqual(datetime_sc, as_datetime)
3354
Tim Peters2a799bf2002-12-16 20:18:38 +00003355def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003356 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003357
3358if __name__ == "__main__":
3359 test_main()