blob: 905f7ef30aed13bb0296f0bdce798d813e78bcbf [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
Alexander Belopolsky9292ee02010-05-27 20:55:27 +0000723 for delta in [tiny, timedelta(1), timedelta(2)]:
724 dt = self.theclass.min + delta
725 dt -= delta # no problem
726 self.assertRaises(OverflowError, dt.__sub__, delta)
727 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000728
Alexander Belopolsky9292ee02010-05-27 20:55:27 +0000729 dt = self.theclass.max - delta
730 dt += delta # no problem
731 self.assertRaises(OverflowError, dt.__add__, delta)
732 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000733
734 def test_fromtimestamp(self):
735 import time
736
737 # Try an arbitrary fixed value.
738 year, month, day = 1999, 9, 19
739 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
740 d = self.theclass.fromtimestamp(ts)
741 self.assertEqual(d.year, year)
742 self.assertEqual(d.month, month)
743 self.assertEqual(d.day, day)
744
Tim Peters1b6f7a92004-06-20 02:50:16 +0000745 def test_insane_fromtimestamp(self):
746 # It's possible that some platform maps time_t to double,
747 # and that this test will fail there. This test should
748 # exempt such platforms (provided they return reasonable
749 # results!).
750 for insane in -1e200, 1e200:
751 self.assertRaises(ValueError, self.theclass.fromtimestamp,
752 insane)
753
Tim Peters2a799bf2002-12-16 20:18:38 +0000754 def test_today(self):
755 import time
756
757 # We claim that today() is like fromtimestamp(time.time()), so
758 # prove it.
759 for dummy in range(3):
760 today = self.theclass.today()
761 ts = time.time()
762 todayagain = self.theclass.fromtimestamp(ts)
763 if today == todayagain:
764 break
765 # There are several legit reasons that could fail:
766 # 1. It recently became midnight, between the today() and the
767 # time() calls.
768 # 2. The platform time() has such fine resolution that we'll
769 # never get the same value twice.
770 # 3. The platform time() has poor resolution, and we just
771 # happened to call today() right before a resolution quantum
772 # boundary.
773 # 4. The system clock got fiddled between calls.
774 # In any case, wait a little while and try again.
775 time.sleep(0.1)
776
777 # It worked or it didn't. If it didn't, assume it's reason #2, and
778 # let the test pass if they're within half a second of each other.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000779 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000780 abs(todayagain - today) < timedelta(seconds=0.5))
781
782 def test_weekday(self):
783 for i in range(7):
784 # March 4, 2002 is a Monday
785 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
786 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
787 # January 2, 1956 is a Monday
788 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
789 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
790
791 def test_isocalendar(self):
792 # Check examples from
793 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
794 for i in range(7):
795 d = self.theclass(2003, 12, 22+i)
796 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
797 d = self.theclass(2003, 12, 29) + timedelta(i)
798 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
799 d = self.theclass(2004, 1, 5+i)
800 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
801 d = self.theclass(2009, 12, 21+i)
802 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
803 d = self.theclass(2009, 12, 28) + timedelta(i)
804 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
805 d = self.theclass(2010, 1, 4+i)
806 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
807
808 def test_iso_long_years(self):
809 # Calculate long ISO years and compare to table from
810 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
811 ISO_LONG_YEARS_TABLE = """
812 4 32 60 88
813 9 37 65 93
814 15 43 71 99
815 20 48 76
816 26 54 82
817
818 105 133 161 189
819 111 139 167 195
820 116 144 172
821 122 150 178
822 128 156 184
823
824 201 229 257 285
825 207 235 263 291
826 212 240 268 296
827 218 246 274
828 224 252 280
829
830 303 331 359 387
831 308 336 364 392
832 314 342 370 398
833 320 348 376
834 325 353 381
835 """
836 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
837 iso_long_years.sort()
838 L = []
839 for i in range(400):
840 d = self.theclass(2000+i, 12, 31)
841 d1 = self.theclass(1600+i, 12, 31)
842 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
843 if d.isocalendar()[1] == 53:
844 L.append(i)
845 self.assertEqual(L, iso_long_years)
846
847 def test_isoformat(self):
848 t = self.theclass(2, 3, 2)
849 self.assertEqual(t.isoformat(), "0002-03-02")
850
851 def test_ctime(self):
852 t = self.theclass(2002, 3, 2)
853 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
854
855 def test_strftime(self):
856 t = self.theclass(2005, 3, 2)
857 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000858 self.assertEqual(t.strftime(""), "") # SF bug #761337
Georg Brandl4ddfcd32006-09-30 11:17:34 +0000859 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000860
861 self.assertRaises(TypeError, t.strftime) # needs an arg
862 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
863 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
864
Gregory P. Smith137d8242008-06-02 04:05:52 +0000865 # test that unicode input is allowed (issue 2782)
866 self.assertEqual(t.strftime(u"%m"), "03")
867
Tim Peters2a799bf2002-12-16 20:18:38 +0000868 # A naive object replaces %z and %Z w/ empty strings.
869 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
870
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000871 #make sure that invalid format specifiers are handled correctly
Kristján Valur Jónsson8adc0b52009-01-15 09:09:13 +0000872 #self.assertRaises(ValueError, t.strftime, "%e")
873 #self.assertRaises(ValueError, t.strftime, "%")
874 #self.assertRaises(ValueError, t.strftime, "%#")
875
876 #oh well, some systems just ignore those invalid ones.
877 #at least, excercise them to make sure that no crashes
878 #are generated
879 for f in ["%e", "%", "%#"]:
880 try:
881 t.strftime(f)
882 except ValueError:
883 pass
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000884
885 #check that this standard extension works
886 t.strftime("%f")
887
Gregory P. Smith137d8242008-06-02 04:05:52 +0000888
Eric Smitha9f7d622008-02-17 19:46:49 +0000889 def test_format(self):
890 dt = self.theclass(2007, 9, 10)
891 self.assertEqual(dt.__format__(''), str(dt))
892
893 # check that a derived class's __str__() gets called
894 class A(self.theclass):
895 def __str__(self):
896 return 'A'
897 a = A(2007, 9, 10)
898 self.assertEqual(a.__format__(''), 'A')
899
900 # check that a derived class's strftime gets called
901 class B(self.theclass):
902 def strftime(self, format_spec):
903 return 'B'
904 b = B(2007, 9, 10)
905 self.assertEqual(b.__format__(''), str(dt))
906
907 for fmt in ["m:%m d:%d y:%y",
908 "m:%m d:%d y:%y H:%H M:%M S:%S",
909 "%z %Z",
910 ]:
911 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
912 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
913 self.assertEqual(b.__format__(fmt), 'B')
914
Tim Peters2a799bf2002-12-16 20:18:38 +0000915 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000916 self.assertIsInstance(self.theclass.min, self.theclass)
917 self.assertIsInstance(self.theclass.max, self.theclass)
918 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000919 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000920
921 def test_extreme_timedelta(self):
922 big = self.theclass.max - self.theclass.min
923 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
924 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
925 # n == 315537897599999999 ~= 2**58.13
926 justasbig = timedelta(0, 0, n)
927 self.assertEqual(big, justasbig)
928 self.assertEqual(self.theclass.min + big, self.theclass.max)
929 self.assertEqual(self.theclass.max - big, self.theclass.min)
930
931 def test_timetuple(self):
932 for i in range(7):
933 # January 2, 1956 is a Monday (0)
934 d = self.theclass(1956, 1, 2+i)
935 t = d.timetuple()
936 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
937 # February 1, 1956 is a Wednesday (2)
938 d = self.theclass(1956, 2, 1+i)
939 t = d.timetuple()
940 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
941 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
942 # of the year.
943 d = self.theclass(1956, 3, 1+i)
944 t = d.timetuple()
945 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
946 self.assertEqual(t.tm_year, 1956)
947 self.assertEqual(t.tm_mon, 3)
948 self.assertEqual(t.tm_mday, 1+i)
949 self.assertEqual(t.tm_hour, 0)
950 self.assertEqual(t.tm_min, 0)
951 self.assertEqual(t.tm_sec, 0)
952 self.assertEqual(t.tm_wday, (3+i)%7)
953 self.assertEqual(t.tm_yday, 61+i)
954 self.assertEqual(t.tm_isdst, -1)
955
956 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000957 args = 6, 7, 23
958 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000959 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000960 green = pickler.dumps(orig, proto)
961 derived = unpickler.loads(green)
962 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000963
964 def test_compare(self):
965 t1 = self.theclass(2, 3, 4)
966 t2 = self.theclass(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000967 self.assertTrue(t1 == t2)
968 self.assertTrue(t1 <= t2)
969 self.assertTrue(t1 >= t2)
970 self.assertTrue(not t1 != t2)
971 self.assertTrue(not t1 < t2)
972 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000973 self.assertEqual(cmp(t1, t2), 0)
974 self.assertEqual(cmp(t2, t1), 0)
975
976 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
977 t2 = self.theclass(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000978 self.assertTrue(t1 < t2)
979 self.assertTrue(t2 > t1)
980 self.assertTrue(t1 <= t2)
981 self.assertTrue(t2 >= t1)
982 self.assertTrue(t1 != t2)
983 self.assertTrue(t2 != t1)
984 self.assertTrue(not t1 == t2)
985 self.assertTrue(not t2 == t1)
986 self.assertTrue(not t1 > t2)
987 self.assertTrue(not t2 < t1)
988 self.assertTrue(not t1 >= t2)
989 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000990 self.assertEqual(cmp(t1, t2), -1)
991 self.assertEqual(cmp(t2, t1), 1)
992
Tim Peters68124bb2003-02-08 03:46:31 +0000993 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000994 self.assertEqual(t1 == badarg, False)
995 self.assertEqual(t1 != badarg, True)
996 self.assertEqual(badarg == t1, False)
997 self.assertEqual(badarg != t1, True)
998
Tim Peters2a799bf2002-12-16 20:18:38 +0000999 self.assertRaises(TypeError, lambda: t1 < badarg)
1000 self.assertRaises(TypeError, lambda: t1 > badarg)
1001 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001002 self.assertRaises(TypeError, lambda: badarg <= t1)
1003 self.assertRaises(TypeError, lambda: badarg < t1)
1004 self.assertRaises(TypeError, lambda: badarg > t1)
1005 self.assertRaises(TypeError, lambda: badarg >= t1)
1006
Tim Peters8d81a012003-01-24 22:36:34 +00001007 def test_mixed_compare(self):
1008 our = self.theclass(2000, 4, 5)
1009 self.assertRaises(TypeError, cmp, our, 1)
1010 self.assertRaises(TypeError, cmp, 1, our)
1011
1012 class AnotherDateTimeClass(object):
1013 def __cmp__(self, other):
1014 # Return "equal" so calling this can't be confused with
1015 # compare-by-address (which never says "equal" for distinct
1016 # objects).
1017 return 0
Nick Coghlan48361f52008-08-11 15:45:58 +00001018 __hash__ = None # Silence Py3k warning
Tim Peters8d81a012003-01-24 22:36:34 +00001019
1020 # This still errors, because date and datetime comparison raise
1021 # TypeError instead of NotImplemented when they don't know what to
1022 # do, in order to stop comparison from falling back to the default
1023 # compare-by-address.
1024 their = AnotherDateTimeClass()
1025 self.assertRaises(TypeError, cmp, our, their)
1026 # Oops: The next stab raises TypeError in the C implementation,
1027 # but not in the Python implementation of datetime. The difference
1028 # is due to that the Python implementation defines __cmp__ but
1029 # the C implementation defines tp_richcompare. This is more pain
1030 # to fix than it's worth, so commenting out the test.
1031 # self.assertEqual(cmp(their, our), 0)
1032
1033 # But date and datetime comparison return NotImplemented instead if the
1034 # other object has a timetuple attr. This gives the other object a
1035 # chance to do the comparison.
1036 class Comparable(AnotherDateTimeClass):
1037 def timetuple(self):
1038 return ()
1039
1040 their = Comparable()
1041 self.assertEqual(cmp(our, their), 0)
1042 self.assertEqual(cmp(their, our), 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001043 self.assertTrue(our == their)
1044 self.assertTrue(their == our)
Tim Peters8d81a012003-01-24 22:36:34 +00001045
Tim Peters2a799bf2002-12-16 20:18:38 +00001046 def test_bool(self):
1047 # All dates are considered true.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001048 self.assertTrue(self.theclass.min)
1049 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001050
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001051 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001052 # For nasty technical reasons, we can't handle years before 1900.
1053 cls = self.theclass
1054 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1055 for y in 1, 49, 51, 99, 100, 1000, 1899:
1056 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001057
1058 def test_replace(self):
1059 cls = self.theclass
1060 args = [1, 2, 3]
1061 base = cls(*args)
1062 self.assertEqual(base, base.replace())
1063
1064 i = 0
1065 for name, newval in (("year", 2),
1066 ("month", 3),
1067 ("day", 4)):
1068 newargs = args[:]
1069 newargs[i] = newval
1070 expected = cls(*newargs)
1071 got = base.replace(**{name: newval})
1072 self.assertEqual(expected, got)
1073 i += 1
1074
1075 # Out of bounds.
1076 base = cls(2000, 2, 29)
1077 self.assertRaises(ValueError, base.replace, year=2001)
1078
Tim Petersa98924a2003-05-17 05:55:19 +00001079 def test_subclass_date(self):
1080
1081 class C(self.theclass):
1082 theAnswer = 42
1083
1084 def __new__(cls, *args, **kws):
1085 temp = kws.copy()
1086 extra = temp.pop('extra')
1087 result = self.theclass.__new__(cls, *args, **temp)
1088 result.extra = extra
1089 return result
1090
1091 def newmeth(self, start):
1092 return start + self.year + self.month
1093
1094 args = 2003, 4, 14
1095
1096 dt1 = self.theclass(*args)
1097 dt2 = C(*args, **{'extra': 7})
1098
1099 self.assertEqual(dt2.__class__, C)
1100 self.assertEqual(dt2.theAnswer, 42)
1101 self.assertEqual(dt2.extra, 7)
1102 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1103 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1104
Tim Peters604c0132004-06-07 23:04:33 +00001105 def test_pickling_subclass_date(self):
1106
1107 args = 6, 7, 23
1108 orig = SubclassDate(*args)
1109 for pickler, unpickler, proto in pickle_choices:
1110 green = pickler.dumps(orig, proto)
1111 derived = unpickler.loads(green)
1112 self.assertEqual(orig, derived)
1113
Tim Peters3f606292004-03-21 23:38:41 +00001114 def test_backdoor_resistance(self):
1115 # For fast unpickling, the constructor accepts a pickle string.
1116 # This is a low-overhead backdoor. A user can (by intent or
1117 # mistake) pass a string directly, which (if it's the right length)
1118 # will get treated like a pickle, and bypass the normal sanity
1119 # checks in the constructor. This can create insane objects.
1120 # The constructor doesn't want to burn the time to validate all
1121 # fields, but does check the month field. This stops, e.g.,
1122 # datetime.datetime('1995-03-25') from yielding an insane object.
1123 base = '1995-03-25'
1124 if not issubclass(self.theclass, datetime):
1125 base = base[:4]
1126 for month_byte in '9', chr(0), chr(13), '\xff':
1127 self.assertRaises(TypeError, self.theclass,
1128 base[:2] + month_byte + base[3:])
1129 for ord_byte in range(1, 13):
1130 # This shouldn't blow up because of the month byte alone. If
1131 # the implementation changes to do more-careful checking, it may
1132 # blow up because other fields are insane.
1133 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001134
Tim Peters2a799bf2002-12-16 20:18:38 +00001135#############################################################################
1136# datetime tests
1137
Tim Peters604c0132004-06-07 23:04:33 +00001138class SubclassDatetime(datetime):
1139 sub_var = 1
1140
Tim Peters2a799bf2002-12-16 20:18:38 +00001141class TestDateTime(TestDate):
1142
1143 theclass = datetime
1144
1145 def test_basic_attributes(self):
1146 dt = self.theclass(2002, 3, 1, 12, 0)
1147 self.assertEqual(dt.year, 2002)
1148 self.assertEqual(dt.month, 3)
1149 self.assertEqual(dt.day, 1)
1150 self.assertEqual(dt.hour, 12)
1151 self.assertEqual(dt.minute, 0)
1152 self.assertEqual(dt.second, 0)
1153 self.assertEqual(dt.microsecond, 0)
1154
1155 def test_basic_attributes_nonzero(self):
1156 # Make sure all attributes are non-zero so bugs in
1157 # bit-shifting access show up.
1158 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1159 self.assertEqual(dt.year, 2002)
1160 self.assertEqual(dt.month, 3)
1161 self.assertEqual(dt.day, 1)
1162 self.assertEqual(dt.hour, 12)
1163 self.assertEqual(dt.minute, 59)
1164 self.assertEqual(dt.second, 59)
1165 self.assertEqual(dt.microsecond, 8000)
1166
1167 def test_roundtrip(self):
1168 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1169 self.theclass.now()):
1170 # Verify dt -> string -> datetime identity.
1171 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001172 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001173 s = s[9:]
1174 dt2 = eval(s)
1175 self.assertEqual(dt, dt2)
1176
1177 # Verify identity via reconstructing from pieces.
1178 dt2 = self.theclass(dt.year, dt.month, dt.day,
1179 dt.hour, dt.minute, dt.second,
1180 dt.microsecond)
1181 self.assertEqual(dt, dt2)
1182
1183 def test_isoformat(self):
1184 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1185 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1186 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1187 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arc8645a5c2009-12-29 22:03:38 +00001188 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001189 # str is ISO format with the separator forced to a blank.
1190 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1191
1192 t = self.theclass(2, 3, 2)
1193 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1194 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1195 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1196 # str is ISO format with the separator forced to a blank.
1197 self.assertEqual(str(t), "0002-03-02 00:00:00")
1198
Eric Smitha9f7d622008-02-17 19:46:49 +00001199 def test_format(self):
1200 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1201 self.assertEqual(dt.__format__(''), str(dt))
1202
1203 # check that a derived class's __str__() gets called
1204 class A(self.theclass):
1205 def __str__(self):
1206 return 'A'
1207 a = A(2007, 9, 10, 4, 5, 1, 123)
1208 self.assertEqual(a.__format__(''), 'A')
1209
1210 # check that a derived class's strftime gets called
1211 class B(self.theclass):
1212 def strftime(self, format_spec):
1213 return 'B'
1214 b = B(2007, 9, 10, 4, 5, 1, 123)
1215 self.assertEqual(b.__format__(''), str(dt))
1216
1217 for fmt in ["m:%m d:%d y:%y",
1218 "m:%m d:%d y:%y H:%H M:%M S:%S",
1219 "%z %Z",
1220 ]:
1221 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1222 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1223 self.assertEqual(b.__format__(fmt), 'B')
1224
Tim Peters2a799bf2002-12-16 20:18:38 +00001225 def test_more_ctime(self):
1226 # Test fields that TestDate doesn't touch.
1227 import time
1228
1229 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1230 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1231 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1232 # out. The difference is that t.ctime() produces " 2" for the day,
1233 # but platform ctime() produces "02" for the day. According to
1234 # C99, t.ctime() is correct here.
1235 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1236
1237 # So test a case where that difference doesn't matter.
1238 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1239 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1240
1241 def test_tz_independent_comparing(self):
1242 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1243 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1244 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1245 self.assertEqual(dt1, dt3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001246 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001247
1248 # Make sure comparison doesn't forget microseconds, and isn't done
1249 # via comparing a float timestamp (an IEEE double doesn't have enough
1250 # precision to span microsecond resolution across years 1 thru 9999,
1251 # so comparing via timestamp necessarily calls some distinct values
1252 # equal).
1253 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1254 us = timedelta(microseconds=1)
1255 dt2 = dt1 + us
1256 self.assertEqual(dt2 - dt1, us)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001257 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001258
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001259 def test_strftime_with_bad_tzname_replace(self):
1260 # verify ok if tzinfo.tzname().replace() returns a non-string
1261 class MyTzInfo(FixedOffset):
1262 def tzname(self, dt):
1263 class MyStr(str):
1264 def replace(self, *args):
1265 return None
1266 return MyStr('name')
1267 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1268 self.assertRaises(TypeError, t.strftime, '%Z')
1269
Tim Peters2a799bf2002-12-16 20:18:38 +00001270 def test_bad_constructor_arguments(self):
1271 # bad years
1272 self.theclass(MINYEAR, 1, 1) # no exception
1273 self.theclass(MAXYEAR, 1, 1) # no exception
1274 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1275 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1276 # bad months
1277 self.theclass(2000, 1, 1) # no exception
1278 self.theclass(2000, 12, 1) # no exception
1279 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1280 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1281 # bad days
1282 self.theclass(2000, 2, 29) # no exception
1283 self.theclass(2004, 2, 29) # no exception
1284 self.theclass(2400, 2, 29) # no exception
1285 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1286 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1287 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1288 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1289 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1290 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1291 # bad hours
1292 self.theclass(2000, 1, 31, 0) # no exception
1293 self.theclass(2000, 1, 31, 23) # no exception
1294 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1295 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1296 # bad minutes
1297 self.theclass(2000, 1, 31, 23, 0) # no exception
1298 self.theclass(2000, 1, 31, 23, 59) # no exception
1299 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1300 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1301 # bad seconds
1302 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1303 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1304 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1305 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1306 # bad microseconds
1307 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1308 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1309 self.assertRaises(ValueError, self.theclass,
1310 2000, 1, 31, 23, 59, 59, -1)
1311 self.assertRaises(ValueError, self.theclass,
1312 2000, 1, 31, 23, 59, 59,
1313 1000000)
1314
1315 def test_hash_equality(self):
1316 d = self.theclass(2000, 12, 31, 23, 30, 17)
1317 e = self.theclass(2000, 12, 31, 23, 30, 17)
1318 self.assertEqual(d, e)
1319 self.assertEqual(hash(d), hash(e))
1320
1321 dic = {d: 1}
1322 dic[e] = 2
1323 self.assertEqual(len(dic), 1)
1324 self.assertEqual(dic[d], 2)
1325 self.assertEqual(dic[e], 2)
1326
1327 d = self.theclass(2001, 1, 1, 0, 5, 17)
1328 e = self.theclass(2001, 1, 1, 0, 5, 17)
1329 self.assertEqual(d, e)
1330 self.assertEqual(hash(d), hash(e))
1331
1332 dic = {d: 1}
1333 dic[e] = 2
1334 self.assertEqual(len(dic), 1)
1335 self.assertEqual(dic[d], 2)
1336 self.assertEqual(dic[e], 2)
1337
1338 def test_computations(self):
1339 a = self.theclass(2002, 1, 31)
1340 b = self.theclass(1956, 1, 31)
1341 diff = a-b
1342 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1343 self.assertEqual(diff.seconds, 0)
1344 self.assertEqual(diff.microseconds, 0)
1345 a = self.theclass(2002, 3, 2, 17, 6)
1346 millisec = timedelta(0, 0, 1000)
1347 hour = timedelta(0, 3600)
1348 day = timedelta(1)
1349 week = timedelta(7)
1350 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1351 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1352 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1353 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1354 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1355 self.assertEqual(a - hour, a + -hour)
1356 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1357 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1358 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1359 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1360 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1361 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1362 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1363 self.assertEqual((a + week) - a, week)
1364 self.assertEqual((a + day) - a, day)
1365 self.assertEqual((a + hour) - a, hour)
1366 self.assertEqual((a + millisec) - a, millisec)
1367 self.assertEqual((a - week) - a, -week)
1368 self.assertEqual((a - day) - a, -day)
1369 self.assertEqual((a - hour) - a, -hour)
1370 self.assertEqual((a - millisec) - a, -millisec)
1371 self.assertEqual(a - (a + week), -week)
1372 self.assertEqual(a - (a + day), -day)
1373 self.assertEqual(a - (a + hour), -hour)
1374 self.assertEqual(a - (a + millisec), -millisec)
1375 self.assertEqual(a - (a - week), week)
1376 self.assertEqual(a - (a - day), day)
1377 self.assertEqual(a - (a - hour), hour)
1378 self.assertEqual(a - (a - millisec), millisec)
1379 self.assertEqual(a + (week + day + hour + millisec),
1380 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1381 self.assertEqual(a + (week + day + hour + millisec),
1382 (((a + week) + day) + hour) + millisec)
1383 self.assertEqual(a - (week + day + hour + millisec),
1384 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1385 self.assertEqual(a - (week + day + hour + millisec),
1386 (((a - week) - day) - hour) - millisec)
1387 # Add/sub ints, longs, floats should be illegal
1388 for i in 1, 1L, 1.0:
1389 self.assertRaises(TypeError, lambda: a+i)
1390 self.assertRaises(TypeError, lambda: a-i)
1391 self.assertRaises(TypeError, lambda: i+a)
1392 self.assertRaises(TypeError, lambda: i-a)
1393
1394 # delta - datetime is senseless.
1395 self.assertRaises(TypeError, lambda: day - a)
1396 # mixing datetime and (delta or datetime) via * or // is senseless
1397 self.assertRaises(TypeError, lambda: day * a)
1398 self.assertRaises(TypeError, lambda: a * day)
1399 self.assertRaises(TypeError, lambda: day // a)
1400 self.assertRaises(TypeError, lambda: a // day)
1401 self.assertRaises(TypeError, lambda: a * a)
1402 self.assertRaises(TypeError, lambda: a // a)
1403 # datetime + datetime is senseless
1404 self.assertRaises(TypeError, lambda: a + a)
1405
1406 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001407 args = 6, 7, 23, 20, 59, 1, 64**2
1408 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001409 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001410 green = pickler.dumps(orig, proto)
1411 derived = unpickler.loads(green)
1412 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001413
Guido van Rossum275666f2003-02-07 21:49:01 +00001414 def test_more_pickling(self):
1415 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1416 s = pickle.dumps(a)
1417 b = pickle.loads(s)
1418 self.assertEqual(b.year, 2003)
1419 self.assertEqual(b.month, 2)
1420 self.assertEqual(b.day, 7)
1421
Tim Peters604c0132004-06-07 23:04:33 +00001422 def test_pickling_subclass_datetime(self):
1423 args = 6, 7, 23, 20, 59, 1, 64**2
1424 orig = SubclassDatetime(*args)
1425 for pickler, unpickler, proto in pickle_choices:
1426 green = pickler.dumps(orig, proto)
1427 derived = unpickler.loads(green)
1428 self.assertEqual(orig, derived)
1429
Tim Peters2a799bf2002-12-16 20:18:38 +00001430 def test_more_compare(self):
1431 # The test_compare() inherited from TestDate covers the error cases.
1432 # We just want to test lexicographic ordering on the members datetime
1433 # has that date lacks.
1434 args = [2000, 11, 29, 20, 58, 16, 999998]
1435 t1 = self.theclass(*args)
1436 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001437 self.assertTrue(t1 == t2)
1438 self.assertTrue(t1 <= t2)
1439 self.assertTrue(t1 >= t2)
1440 self.assertTrue(not t1 != t2)
1441 self.assertTrue(not t1 < t2)
1442 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001443 self.assertEqual(cmp(t1, t2), 0)
1444 self.assertEqual(cmp(t2, t1), 0)
1445
1446 for i in range(len(args)):
1447 newargs = args[:]
1448 newargs[i] = args[i] + 1
1449 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001450 self.assertTrue(t1 < t2)
1451 self.assertTrue(t2 > t1)
1452 self.assertTrue(t1 <= t2)
1453 self.assertTrue(t2 >= t1)
1454 self.assertTrue(t1 != t2)
1455 self.assertTrue(t2 != t1)
1456 self.assertTrue(not t1 == t2)
1457 self.assertTrue(not t2 == t1)
1458 self.assertTrue(not t1 > t2)
1459 self.assertTrue(not t2 < t1)
1460 self.assertTrue(not t1 >= t2)
1461 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001462 self.assertEqual(cmp(t1, t2), -1)
1463 self.assertEqual(cmp(t2, t1), 1)
1464
1465
1466 # A helper for timestamp constructor tests.
1467 def verify_field_equality(self, expected, got):
1468 self.assertEqual(expected.tm_year, got.year)
1469 self.assertEqual(expected.tm_mon, got.month)
1470 self.assertEqual(expected.tm_mday, got.day)
1471 self.assertEqual(expected.tm_hour, got.hour)
1472 self.assertEqual(expected.tm_min, got.minute)
1473 self.assertEqual(expected.tm_sec, got.second)
1474
1475 def test_fromtimestamp(self):
1476 import time
1477
1478 ts = time.time()
1479 expected = time.localtime(ts)
1480 got = self.theclass.fromtimestamp(ts)
1481 self.verify_field_equality(expected, got)
1482
1483 def test_utcfromtimestamp(self):
1484 import time
1485
1486 ts = time.time()
1487 expected = time.gmtime(ts)
1488 got = self.theclass.utcfromtimestamp(ts)
1489 self.verify_field_equality(expected, got)
1490
Georg Brandl6d78a582006-04-28 19:09:24 +00001491 def test_microsecond_rounding(self):
1492 # Test whether fromtimestamp "rounds up" floats that are less
1493 # than one microsecond smaller than an integer.
1494 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1495 self.theclass.fromtimestamp(1))
1496
Tim Peters1b6f7a92004-06-20 02:50:16 +00001497 def test_insane_fromtimestamp(self):
1498 # It's possible that some platform maps time_t to double,
1499 # and that this test will fail there. This test should
1500 # exempt such platforms (provided they return reasonable
1501 # results!).
1502 for insane in -1e200, 1e200:
1503 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1504 insane)
1505
1506 def test_insane_utcfromtimestamp(self):
1507 # It's possible that some platform maps time_t to double,
1508 # and that this test will fail there. This test should
1509 # exempt such platforms (provided they return reasonable
1510 # results!).
1511 for insane in -1e200, 1e200:
1512 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1513 insane)
Alexander Belopolsky58451d22010-05-26 20:45:37 +00001514 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossum2054ee92007-03-06 15:50:01 +00001515 def test_negative_float_fromtimestamp(self):
1516 # The result is tz-dependent; at least test that this doesn't
1517 # fail (like it did before bug 1646728 was fixed).
1518 self.theclass.fromtimestamp(-1.05)
1519
Alexander Belopolsky58451d22010-05-26 20:45:37 +00001520 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossum2054ee92007-03-06 15:50:01 +00001521 def test_negative_float_utcfromtimestamp(self):
1522 d = self.theclass.utcfromtimestamp(-1.05)
1523 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1524
Tim Peters2a799bf2002-12-16 20:18:38 +00001525 def test_utcnow(self):
1526 import time
1527
1528 # Call it a success if utcnow() and utcfromtimestamp() are within
1529 # a second of each other.
1530 tolerance = timedelta(seconds=1)
1531 for dummy in range(3):
1532 from_now = self.theclass.utcnow()
1533 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1534 if abs(from_timestamp - from_now) <= tolerance:
1535 break
1536 # Else try again a few times.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001537 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001538
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001539 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001540 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001541
Skip Montanarofc070d22008-03-15 16:04:45 +00001542 string = '2004-12-01 13:02:47.197'
1543 format = '%Y-%m-%d %H:%M:%S.%f'
1544 result, frac = _strptime._strptime(string, format)
1545 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001546 got = self.theclass.strptime(string, format)
1547 self.assertEqual(expected, got)
1548
Tim Peters2a799bf2002-12-16 20:18:38 +00001549 def test_more_timetuple(self):
1550 # This tests fields beyond those tested by the TestDate.test_timetuple.
1551 t = self.theclass(2004, 12, 31, 6, 22, 33)
1552 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1553 self.assertEqual(t.timetuple(),
1554 (t.year, t.month, t.day,
1555 t.hour, t.minute, t.second,
1556 t.weekday(),
1557 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1558 -1))
1559 tt = t.timetuple()
1560 self.assertEqual(tt.tm_year, t.year)
1561 self.assertEqual(tt.tm_mon, t.month)
1562 self.assertEqual(tt.tm_mday, t.day)
1563 self.assertEqual(tt.tm_hour, t.hour)
1564 self.assertEqual(tt.tm_min, t.minute)
1565 self.assertEqual(tt.tm_sec, t.second)
1566 self.assertEqual(tt.tm_wday, t.weekday())
1567 self.assertEqual(tt.tm_yday, t.toordinal() -
1568 date(t.year, 1, 1).toordinal() + 1)
1569 self.assertEqual(tt.tm_isdst, -1)
1570
1571 def test_more_strftime(self):
1572 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001573 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1574 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1575 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001576
1577 def test_extract(self):
1578 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1579 self.assertEqual(dt.date(), date(2002, 3, 4))
1580 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1581
1582 def test_combine(self):
1583 d = date(2002, 3, 4)
1584 t = time(18, 45, 3, 1234)
1585 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1586 combine = self.theclass.combine
1587 dt = combine(d, t)
1588 self.assertEqual(dt, expected)
1589
1590 dt = combine(time=t, date=d)
1591 self.assertEqual(dt, expected)
1592
1593 self.assertEqual(d, dt.date())
1594 self.assertEqual(t, dt.time())
1595 self.assertEqual(dt, combine(dt.date(), dt.time()))
1596
1597 self.assertRaises(TypeError, combine) # need an arg
1598 self.assertRaises(TypeError, combine, d) # need two args
1599 self.assertRaises(TypeError, combine, t, d) # args reversed
1600 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1601 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1602
Tim Peters12bf3392002-12-24 05:41:27 +00001603 def test_replace(self):
1604 cls = self.theclass
1605 args = [1, 2, 3, 4, 5, 6, 7]
1606 base = cls(*args)
1607 self.assertEqual(base, base.replace())
1608
1609 i = 0
1610 for name, newval in (("year", 2),
1611 ("month", 3),
1612 ("day", 4),
1613 ("hour", 5),
1614 ("minute", 6),
1615 ("second", 7),
1616 ("microsecond", 8)):
1617 newargs = args[:]
1618 newargs[i] = newval
1619 expected = cls(*newargs)
1620 got = base.replace(**{name: newval})
1621 self.assertEqual(expected, got)
1622 i += 1
1623
1624 # Out of bounds.
1625 base = cls(2000, 2, 29)
1626 self.assertRaises(ValueError, base.replace, year=2001)
1627
Tim Peters80475bb2002-12-25 07:40:55 +00001628 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001629 # Pretty boring! The TZ test is more interesting here. astimezone()
1630 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001631 dt = self.theclass.now()
1632 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001633 self.assertRaises(TypeError, dt.astimezone) # not enough args
1634 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1635 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001636 self.assertRaises(ValueError, dt.astimezone, f) # naive
1637 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001638
Tim Peters52dcce22003-01-23 16:36:11 +00001639 class Bogus(tzinfo):
1640 def utcoffset(self, dt): return None
1641 def dst(self, dt): return timedelta(0)
1642 bog = Bogus()
1643 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1644
1645 class AlsoBogus(tzinfo):
1646 def utcoffset(self, dt): return timedelta(0)
1647 def dst(self, dt): return None
1648 alsobog = AlsoBogus()
1649 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001650
Tim Petersa98924a2003-05-17 05:55:19 +00001651 def test_subclass_datetime(self):
1652
1653 class C(self.theclass):
1654 theAnswer = 42
1655
1656 def __new__(cls, *args, **kws):
1657 temp = kws.copy()
1658 extra = temp.pop('extra')
1659 result = self.theclass.__new__(cls, *args, **temp)
1660 result.extra = extra
1661 return result
1662
1663 def newmeth(self, start):
1664 return start + self.year + self.month + self.second
1665
1666 args = 2003, 4, 14, 12, 13, 41
1667
1668 dt1 = self.theclass(*args)
1669 dt2 = C(*args, **{'extra': 7})
1670
1671 self.assertEqual(dt2.__class__, C)
1672 self.assertEqual(dt2.theAnswer, 42)
1673 self.assertEqual(dt2.extra, 7)
1674 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1675 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1676 dt1.second - 7)
1677
Tim Peters604c0132004-06-07 23:04:33 +00001678class SubclassTime(time):
1679 sub_var = 1
1680
Collin Winterc2898c52007-04-25 17:29:52 +00001681class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001682
1683 theclass = time
1684
1685 def test_basic_attributes(self):
1686 t = self.theclass(12, 0)
1687 self.assertEqual(t.hour, 12)
1688 self.assertEqual(t.minute, 0)
1689 self.assertEqual(t.second, 0)
1690 self.assertEqual(t.microsecond, 0)
1691
1692 def test_basic_attributes_nonzero(self):
1693 # Make sure all attributes are non-zero so bugs in
1694 # bit-shifting access show up.
1695 t = self.theclass(12, 59, 59, 8000)
1696 self.assertEqual(t.hour, 12)
1697 self.assertEqual(t.minute, 59)
1698 self.assertEqual(t.second, 59)
1699 self.assertEqual(t.microsecond, 8000)
1700
1701 def test_roundtrip(self):
1702 t = self.theclass(1, 2, 3, 4)
1703
1704 # Verify t -> string -> time identity.
1705 s = repr(t)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001706 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001707 s = s[9:]
1708 t2 = eval(s)
1709 self.assertEqual(t, t2)
1710
1711 # Verify identity via reconstructing from pieces.
1712 t2 = self.theclass(t.hour, t.minute, t.second,
1713 t.microsecond)
1714 self.assertEqual(t, t2)
1715
1716 def test_comparing(self):
1717 args = [1, 2, 3, 4]
1718 t1 = self.theclass(*args)
1719 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001720 self.assertTrue(t1 == t2)
1721 self.assertTrue(t1 <= t2)
1722 self.assertTrue(t1 >= t2)
1723 self.assertTrue(not t1 != t2)
1724 self.assertTrue(not t1 < t2)
1725 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001726 self.assertEqual(cmp(t1, t2), 0)
1727 self.assertEqual(cmp(t2, t1), 0)
1728
1729 for i in range(len(args)):
1730 newargs = args[:]
1731 newargs[i] = args[i] + 1
1732 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001733 self.assertTrue(t1 < t2)
1734 self.assertTrue(t2 > t1)
1735 self.assertTrue(t1 <= t2)
1736 self.assertTrue(t2 >= t1)
1737 self.assertTrue(t1 != t2)
1738 self.assertTrue(t2 != t1)
1739 self.assertTrue(not t1 == t2)
1740 self.assertTrue(not t2 == t1)
1741 self.assertTrue(not t1 > t2)
1742 self.assertTrue(not t2 < t1)
1743 self.assertTrue(not t1 >= t2)
1744 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001745 self.assertEqual(cmp(t1, t2), -1)
1746 self.assertEqual(cmp(t2, t1), 1)
1747
Tim Peters68124bb2003-02-08 03:46:31 +00001748 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001749 self.assertEqual(t1 == badarg, False)
1750 self.assertEqual(t1 != badarg, True)
1751 self.assertEqual(badarg == t1, False)
1752 self.assertEqual(badarg != t1, True)
1753
Tim Peters2a799bf2002-12-16 20:18:38 +00001754 self.assertRaises(TypeError, lambda: t1 <= badarg)
1755 self.assertRaises(TypeError, lambda: t1 < badarg)
1756 self.assertRaises(TypeError, lambda: t1 > badarg)
1757 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001758 self.assertRaises(TypeError, lambda: badarg <= t1)
1759 self.assertRaises(TypeError, lambda: badarg < t1)
1760 self.assertRaises(TypeError, lambda: badarg > t1)
1761 self.assertRaises(TypeError, lambda: badarg >= t1)
1762
1763 def test_bad_constructor_arguments(self):
1764 # bad hours
1765 self.theclass(0, 0) # no exception
1766 self.theclass(23, 0) # no exception
1767 self.assertRaises(ValueError, self.theclass, -1, 0)
1768 self.assertRaises(ValueError, self.theclass, 24, 0)
1769 # bad minutes
1770 self.theclass(23, 0) # no exception
1771 self.theclass(23, 59) # no exception
1772 self.assertRaises(ValueError, self.theclass, 23, -1)
1773 self.assertRaises(ValueError, self.theclass, 23, 60)
1774 # bad seconds
1775 self.theclass(23, 59, 0) # no exception
1776 self.theclass(23, 59, 59) # no exception
1777 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1778 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1779 # bad microseconds
1780 self.theclass(23, 59, 59, 0) # no exception
1781 self.theclass(23, 59, 59, 999999) # no exception
1782 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1783 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1784
1785 def test_hash_equality(self):
1786 d = self.theclass(23, 30, 17)
1787 e = self.theclass(23, 30, 17)
1788 self.assertEqual(d, e)
1789 self.assertEqual(hash(d), hash(e))
1790
1791 dic = {d: 1}
1792 dic[e] = 2
1793 self.assertEqual(len(dic), 1)
1794 self.assertEqual(dic[d], 2)
1795 self.assertEqual(dic[e], 2)
1796
1797 d = self.theclass(0, 5, 17)
1798 e = self.theclass(0, 5, 17)
1799 self.assertEqual(d, e)
1800 self.assertEqual(hash(d), hash(e))
1801
1802 dic = {d: 1}
1803 dic[e] = 2
1804 self.assertEqual(len(dic), 1)
1805 self.assertEqual(dic[d], 2)
1806 self.assertEqual(dic[e], 2)
1807
1808 def test_isoformat(self):
1809 t = self.theclass(4, 5, 1, 123)
1810 self.assertEqual(t.isoformat(), "04:05:01.000123")
1811 self.assertEqual(t.isoformat(), str(t))
1812
1813 t = self.theclass()
1814 self.assertEqual(t.isoformat(), "00:00:00")
1815 self.assertEqual(t.isoformat(), str(t))
1816
1817 t = self.theclass(microsecond=1)
1818 self.assertEqual(t.isoformat(), "00:00:00.000001")
1819 self.assertEqual(t.isoformat(), str(t))
1820
1821 t = self.theclass(microsecond=10)
1822 self.assertEqual(t.isoformat(), "00:00:00.000010")
1823 self.assertEqual(t.isoformat(), str(t))
1824
1825 t = self.theclass(microsecond=100)
1826 self.assertEqual(t.isoformat(), "00:00:00.000100")
1827 self.assertEqual(t.isoformat(), str(t))
1828
1829 t = self.theclass(microsecond=1000)
1830 self.assertEqual(t.isoformat(), "00:00:00.001000")
1831 self.assertEqual(t.isoformat(), str(t))
1832
1833 t = self.theclass(microsecond=10000)
1834 self.assertEqual(t.isoformat(), "00:00:00.010000")
1835 self.assertEqual(t.isoformat(), str(t))
1836
1837 t = self.theclass(microsecond=100000)
1838 self.assertEqual(t.isoformat(), "00:00:00.100000")
1839 self.assertEqual(t.isoformat(), str(t))
1840
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001841 def test_1653736(self):
1842 # verify it doesn't accept extra keyword arguments
1843 t = self.theclass(second=1)
1844 self.assertRaises(TypeError, t.isoformat, foo=3)
1845
Tim Peters2a799bf2002-12-16 20:18:38 +00001846 def test_strftime(self):
1847 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001848 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001849 # A naive object replaces %z and %Z with empty strings.
1850 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1851
Eric Smitha9f7d622008-02-17 19:46:49 +00001852 def test_format(self):
1853 t = self.theclass(1, 2, 3, 4)
1854 self.assertEqual(t.__format__(''), str(t))
1855
1856 # check that a derived class's __str__() gets called
1857 class A(self.theclass):
1858 def __str__(self):
1859 return 'A'
1860 a = A(1, 2, 3, 4)
1861 self.assertEqual(a.__format__(''), 'A')
1862
1863 # check that a derived class's strftime gets called
1864 class B(self.theclass):
1865 def strftime(self, format_spec):
1866 return 'B'
1867 b = B(1, 2, 3, 4)
1868 self.assertEqual(b.__format__(''), str(t))
1869
1870 for fmt in ['%H %M %S',
1871 ]:
1872 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1873 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1874 self.assertEqual(b.__format__(fmt), 'B')
1875
Tim Peters2a799bf2002-12-16 20:18:38 +00001876 def test_str(self):
1877 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1878 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1879 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1880 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1881 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1882
1883 def test_repr(self):
1884 name = 'datetime.' + self.theclass.__name__
1885 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1886 "%s(1, 2, 3, 4)" % name)
1887 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1888 "%s(10, 2, 3, 4000)" % name)
1889 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1890 "%s(0, 2, 3, 400000)" % name)
1891 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1892 "%s(12, 2, 3)" % name)
1893 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1894 "%s(23, 15)" % name)
1895
1896 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +00001897 self.assertIsInstance(self.theclass.min, self.theclass)
1898 self.assertIsInstance(self.theclass.max, self.theclass)
1899 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001900 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001901
1902 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001903 args = 20, 59, 16, 64**2
1904 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001905 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001906 green = pickler.dumps(orig, proto)
1907 derived = unpickler.loads(green)
1908 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001909
Tim Peters604c0132004-06-07 23:04:33 +00001910 def test_pickling_subclass_time(self):
1911 args = 20, 59, 16, 64**2
1912 orig = SubclassTime(*args)
1913 for pickler, unpickler, proto in pickle_choices:
1914 green = pickler.dumps(orig, proto)
1915 derived = unpickler.loads(green)
1916 self.assertEqual(orig, derived)
1917
Tim Peters2a799bf2002-12-16 20:18:38 +00001918 def test_bool(self):
1919 cls = self.theclass
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001920 self.assertTrue(cls(1))
1921 self.assertTrue(cls(0, 1))
1922 self.assertTrue(cls(0, 0, 1))
1923 self.assertTrue(cls(0, 0, 0, 1))
1924 self.assertTrue(not cls(0))
1925 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001926
Tim Peters12bf3392002-12-24 05:41:27 +00001927 def test_replace(self):
1928 cls = self.theclass
1929 args = [1, 2, 3, 4]
1930 base = cls(*args)
1931 self.assertEqual(base, base.replace())
1932
1933 i = 0
1934 for name, newval in (("hour", 5),
1935 ("minute", 6),
1936 ("second", 7),
1937 ("microsecond", 8)):
1938 newargs = args[:]
1939 newargs[i] = newval
1940 expected = cls(*newargs)
1941 got = base.replace(**{name: newval})
1942 self.assertEqual(expected, got)
1943 i += 1
1944
1945 # Out of bounds.
1946 base = cls(1)
1947 self.assertRaises(ValueError, base.replace, hour=24)
1948 self.assertRaises(ValueError, base.replace, minute=-1)
1949 self.assertRaises(ValueError, base.replace, second=100)
1950 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1951
Tim Petersa98924a2003-05-17 05:55:19 +00001952 def test_subclass_time(self):
1953
1954 class C(self.theclass):
1955 theAnswer = 42
1956
1957 def __new__(cls, *args, **kws):
1958 temp = kws.copy()
1959 extra = temp.pop('extra')
1960 result = self.theclass.__new__(cls, *args, **temp)
1961 result.extra = extra
1962 return result
1963
1964 def newmeth(self, start):
1965 return start + self.hour + self.second
1966
1967 args = 4, 5, 6
1968
1969 dt1 = self.theclass(*args)
1970 dt2 = C(*args, **{'extra': 7})
1971
1972 self.assertEqual(dt2.__class__, C)
1973 self.assertEqual(dt2.theAnswer, 42)
1974 self.assertEqual(dt2.extra, 7)
1975 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1976 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1977
Armin Rigof4afb212005-11-07 07:15:48 +00001978 def test_backdoor_resistance(self):
1979 # see TestDate.test_backdoor_resistance().
1980 base = '2:59.0'
1981 for hour_byte in ' ', '9', chr(24), '\xff':
1982 self.assertRaises(TypeError, self.theclass,
1983 hour_byte + base[1:])
1984
Tim Peters855fe882002-12-22 03:43:39 +00001985# A mixin for classes with a tzinfo= argument. Subclasses must define
1986# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001987# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001988class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001989
Tim Petersbad8ff02002-12-30 20:52:32 +00001990 def test_argument_passing(self):
1991 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001992 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001993 class introspective(tzinfo):
1994 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001995 def utcoffset(self, dt):
1996 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001997 dst = utcoffset
1998
1999 obj = cls(1, 2, 3, tzinfo=introspective())
2000
Tim Peters0bf60bd2003-01-08 20:40:01 +00002001 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002002 self.assertEqual(obj.tzname(), expected)
2003
Tim Peters0bf60bd2003-01-08 20:40:01 +00002004 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002005 self.assertEqual(obj.utcoffset(), expected)
2006 self.assertEqual(obj.dst(), expected)
2007
Tim Peters855fe882002-12-22 03:43:39 +00002008 def test_bad_tzinfo_classes(self):
2009 cls = self.theclass
2010 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002011
Tim Peters855fe882002-12-22 03:43:39 +00002012 class NiceTry(object):
2013 def __init__(self): pass
2014 def utcoffset(self, dt): pass
2015 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2016
2017 class BetterTry(tzinfo):
2018 def __init__(self): pass
2019 def utcoffset(self, dt): pass
2020 b = BetterTry()
2021 t = cls(1, 1, 1, tzinfo=b)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002022 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002023
2024 def test_utc_offset_out_of_bounds(self):
2025 class Edgy(tzinfo):
2026 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002027 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002028 def utcoffset(self, dt):
2029 return self.offset
2030
2031 cls = self.theclass
2032 for offset, legit in ((-1440, False),
2033 (-1439, True),
2034 (1439, True),
2035 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002036 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002037 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002038 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002039 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002040 else:
2041 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002042 if legit:
2043 aofs = abs(offset)
2044 h, m = divmod(aofs, 60)
2045 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002046 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002047 t = t.timetz()
2048 self.assertEqual(str(t), "01:02:03" + tag)
2049 else:
2050 self.assertRaises(ValueError, str, t)
2051
2052 def test_tzinfo_classes(self):
2053 cls = self.theclass
2054 class C1(tzinfo):
2055 def utcoffset(self, dt): return None
2056 def dst(self, dt): return None
2057 def tzname(self, dt): return None
2058 for t in (cls(1, 1, 1),
2059 cls(1, 1, 1, tzinfo=None),
2060 cls(1, 1, 1, tzinfo=C1())):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002061 self.assertTrue(t.utcoffset() is None)
2062 self.assertTrue(t.dst() is None)
2063 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002064
Tim Peters855fe882002-12-22 03:43:39 +00002065 class C3(tzinfo):
2066 def utcoffset(self, dt): return timedelta(minutes=-1439)
2067 def dst(self, dt): return timedelta(minutes=1439)
2068 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002069 t = cls(1, 1, 1, tzinfo=C3())
2070 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2071 self.assertEqual(t.dst(), timedelta(minutes=1439))
2072 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002073
2074 # Wrong types.
2075 class C4(tzinfo):
2076 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002077 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002078 def tzname(self, dt): return 0
2079 t = cls(1, 1, 1, tzinfo=C4())
2080 self.assertRaises(TypeError, t.utcoffset)
2081 self.assertRaises(TypeError, t.dst)
2082 self.assertRaises(TypeError, t.tzname)
2083
2084 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002085 class C6(tzinfo):
2086 def utcoffset(self, dt): return timedelta(hours=-24)
2087 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002088 t = cls(1, 1, 1, tzinfo=C6())
2089 self.assertRaises(ValueError, t.utcoffset)
2090 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002091
2092 # Not a whole number of minutes.
2093 class C7(tzinfo):
2094 def utcoffset(self, dt): return timedelta(seconds=61)
2095 def dst(self, dt): return timedelta(microseconds=-81)
2096 t = cls(1, 1, 1, tzinfo=C7())
2097 self.assertRaises(ValueError, t.utcoffset)
2098 self.assertRaises(ValueError, t.dst)
2099
Tim Peters4c0db782002-12-26 05:01:19 +00002100 def test_aware_compare(self):
2101 cls = self.theclass
2102
Tim Peters60c76e42002-12-27 00:41:11 +00002103 # Ensure that utcoffset() gets ignored if the comparands have
2104 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002105 class OperandDependentOffset(tzinfo):
2106 def utcoffset(self, t):
2107 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002108 # d0 and d1 equal after adjustment
2109 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002110 else:
Tim Peters397301e2003-01-02 21:28:08 +00002111 # d2 off in the weeds
2112 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002113
2114 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2115 d0 = base.replace(minute=3)
2116 d1 = base.replace(minute=9)
2117 d2 = base.replace(minute=11)
2118 for x in d0, d1, d2:
2119 for y in d0, d1, d2:
2120 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002121 expected = cmp(x.minute, y.minute)
2122 self.assertEqual(got, expected)
2123
2124 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002125 # Note that a time can't actually have an operand-depedent offset,
2126 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2127 # so skip this test for time.
2128 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002129 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2130 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2131 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2132 for x in d0, d1, d2:
2133 for y in d0, d1, d2:
2134 got = cmp(x, y)
2135 if (x is d0 or x is d1) and (y is d0 or y is d1):
2136 expected = 0
2137 elif x is y is d2:
2138 expected = 0
2139 elif x is d2:
2140 expected = -1
2141 else:
2142 assert y is d2
2143 expected = 1
2144 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002145
Tim Peters855fe882002-12-22 03:43:39 +00002146
Tim Peters0bf60bd2003-01-08 20:40:01 +00002147# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002148class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002149 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002150
2151 def test_empty(self):
2152 t = self.theclass()
2153 self.assertEqual(t.hour, 0)
2154 self.assertEqual(t.minute, 0)
2155 self.assertEqual(t.second, 0)
2156 self.assertEqual(t.microsecond, 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002157 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002158
Tim Peters2a799bf2002-12-16 20:18:38 +00002159 def test_zones(self):
2160 est = FixedOffset(-300, "EST", 1)
2161 utc = FixedOffset(0, "UTC", -2)
2162 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002163 t1 = time( 7, 47, tzinfo=est)
2164 t2 = time(12, 47, tzinfo=utc)
2165 t3 = time(13, 47, tzinfo=met)
2166 t4 = time(microsecond=40)
2167 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002168
2169 self.assertEqual(t1.tzinfo, est)
2170 self.assertEqual(t2.tzinfo, utc)
2171 self.assertEqual(t3.tzinfo, met)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002172 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002173 self.assertEqual(t5.tzinfo, utc)
2174
Tim Peters855fe882002-12-22 03:43:39 +00002175 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2176 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2177 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002178 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002179 self.assertRaises(TypeError, t1.utcoffset, "no args")
2180
2181 self.assertEqual(t1.tzname(), "EST")
2182 self.assertEqual(t2.tzname(), "UTC")
2183 self.assertEqual(t3.tzname(), "MET")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002184 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002185 self.assertRaises(TypeError, t1.tzname, "no args")
2186
Tim Peters855fe882002-12-22 03:43:39 +00002187 self.assertEqual(t1.dst(), timedelta(minutes=1))
2188 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2189 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002190 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002191 self.assertRaises(TypeError, t1.dst, "no args")
2192
2193 self.assertEqual(hash(t1), hash(t2))
2194 self.assertEqual(hash(t1), hash(t3))
2195 self.assertEqual(hash(t2), hash(t3))
2196
2197 self.assertEqual(t1, t2)
2198 self.assertEqual(t1, t3)
2199 self.assertEqual(t2, t3)
2200 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2201 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2202 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2203
2204 self.assertEqual(str(t1), "07:47:00-05:00")
2205 self.assertEqual(str(t2), "12:47:00+00:00")
2206 self.assertEqual(str(t3), "13:47:00+01:00")
2207 self.assertEqual(str(t4), "00:00:00.000040")
2208 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2209
2210 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2211 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2212 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2213 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2214 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2215
Tim Peters0bf60bd2003-01-08 20:40:01 +00002216 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002217 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2218 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2219 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2220 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2221 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2222
2223 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2224 "07:47:00 %Z=EST %z=-0500")
2225 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2226 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2227
2228 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002229 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002230 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2231 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2232
Tim Petersb92bb712002-12-21 17:44:07 +00002233 # Check that an invalid tzname result raises an exception.
2234 class Badtzname(tzinfo):
2235 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002236 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002237 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2238 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002239
2240 def test_hash_edge_cases(self):
2241 # Offsets that overflow a basic time.
2242 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2243 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2244 self.assertEqual(hash(t1), hash(t2))
2245
2246 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2247 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2248 self.assertEqual(hash(t1), hash(t2))
2249
Tim Peters2a799bf2002-12-16 20:18:38 +00002250 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002251 # Try one without a tzinfo.
2252 args = 20, 59, 16, 64**2
2253 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002254 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002255 green = pickler.dumps(orig, proto)
2256 derived = unpickler.loads(green)
2257 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002258
2259 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002260 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002261 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002262 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002263 green = pickler.dumps(orig, proto)
2264 derived = unpickler.loads(green)
2265 self.assertEqual(orig, derived)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002266 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002267 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2268 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002269
2270 def test_more_bool(self):
2271 # Test cases with non-None tzinfo.
2272 cls = self.theclass
2273
2274 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002275 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002276
2277 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002278 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002279
2280 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002281 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002282
2283 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002284 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002285
2286 # Mostly ensuring this doesn't overflow internally.
2287 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002288 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002289
2290 # But this should yield a value error -- the utcoffset is bogus.
2291 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2292 self.assertRaises(ValueError, lambda: bool(t))
2293
2294 # Likewise.
2295 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2296 self.assertRaises(ValueError, lambda: bool(t))
2297
Tim Peters12bf3392002-12-24 05:41:27 +00002298 def test_replace(self):
2299 cls = self.theclass
2300 z100 = FixedOffset(100, "+100")
2301 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2302 args = [1, 2, 3, 4, z100]
2303 base = cls(*args)
2304 self.assertEqual(base, base.replace())
2305
2306 i = 0
2307 for name, newval in (("hour", 5),
2308 ("minute", 6),
2309 ("second", 7),
2310 ("microsecond", 8),
2311 ("tzinfo", zm200)):
2312 newargs = args[:]
2313 newargs[i] = newval
2314 expected = cls(*newargs)
2315 got = base.replace(**{name: newval})
2316 self.assertEqual(expected, got)
2317 i += 1
2318
2319 # Ensure we can get rid of a tzinfo.
2320 self.assertEqual(base.tzname(), "+100")
2321 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002322 self.assertTrue(base2.tzinfo is None)
2323 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002324
2325 # Ensure we can add one.
2326 base3 = base2.replace(tzinfo=z100)
2327 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002328 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002329
2330 # Out of bounds.
2331 base = cls(1)
2332 self.assertRaises(ValueError, base.replace, hour=24)
2333 self.assertRaises(ValueError, base.replace, minute=-1)
2334 self.assertRaises(ValueError, base.replace, second=100)
2335 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2336
Tim Peters60c76e42002-12-27 00:41:11 +00002337 def test_mixed_compare(self):
2338 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002339 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002340 self.assertEqual(t1, t2)
2341 t2 = t2.replace(tzinfo=None)
2342 self.assertEqual(t1, t2)
2343 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2344 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002345 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2346 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002347
Tim Peters0bf60bd2003-01-08 20:40:01 +00002348 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002349 class Varies(tzinfo):
2350 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002351 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002352 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002353 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002354 return self.offset
2355
2356 v = Varies()
2357 t1 = t2.replace(tzinfo=v)
2358 t2 = t2.replace(tzinfo=v)
2359 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2360 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2361 self.assertEqual(t1, t2)
2362
2363 # But if they're not identical, it isn't ignored.
2364 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002365 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002366
Tim Petersa98924a2003-05-17 05:55:19 +00002367 def test_subclass_timetz(self):
2368
2369 class C(self.theclass):
2370 theAnswer = 42
2371
2372 def __new__(cls, *args, **kws):
2373 temp = kws.copy()
2374 extra = temp.pop('extra')
2375 result = self.theclass.__new__(cls, *args, **temp)
2376 result.extra = extra
2377 return result
2378
2379 def newmeth(self, start):
2380 return start + self.hour + self.second
2381
2382 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2383
2384 dt1 = self.theclass(*args)
2385 dt2 = C(*args, **{'extra': 7})
2386
2387 self.assertEqual(dt2.__class__, C)
2388 self.assertEqual(dt2.theAnswer, 42)
2389 self.assertEqual(dt2.extra, 7)
2390 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2391 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2392
Tim Peters4c0db782002-12-26 05:01:19 +00002393
Tim Peters0bf60bd2003-01-08 20:40:01 +00002394# Testing datetime objects with a non-None tzinfo.
2395
Collin Winterc2898c52007-04-25 17:29:52 +00002396class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002397 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002398
2399 def test_trivial(self):
2400 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2401 self.assertEqual(dt.year, 1)
2402 self.assertEqual(dt.month, 2)
2403 self.assertEqual(dt.day, 3)
2404 self.assertEqual(dt.hour, 4)
2405 self.assertEqual(dt.minute, 5)
2406 self.assertEqual(dt.second, 6)
2407 self.assertEqual(dt.microsecond, 7)
2408 self.assertEqual(dt.tzinfo, None)
2409
2410 def test_even_more_compare(self):
2411 # The test_compare() and test_more_compare() inherited from TestDate
2412 # and TestDateTime covered non-tzinfo cases.
2413
2414 # Smallest possible after UTC adjustment.
2415 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2416 # Largest possible after UTC adjustment.
2417 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2418 tzinfo=FixedOffset(-1439, ""))
2419
2420 # Make sure those compare correctly, and w/o overflow.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002421 self.assertTrue(t1 < t2)
2422 self.assertTrue(t1 != t2)
2423 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002424
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002425 self.assertTrue(t1 == t1)
2426 self.assertTrue(t2 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002427
2428 # Equal afer adjustment.
2429 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2430 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2431 self.assertEqual(t1, t2)
2432
2433 # Change t1 not to subtract a minute, and t1 should be larger.
2434 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002435 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002436
2437 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2438 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002439 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002440
2441 # Back to the original t1, but make seconds resolve it.
2442 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2443 second=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002444 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002445
2446 # Likewise, but make microseconds resolve it.
2447 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2448 microsecond=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002449 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002450
2451 # Make t2 naive and it should fail.
2452 t2 = self.theclass.min
2453 self.assertRaises(TypeError, lambda: t1 == t2)
2454 self.assertEqual(t2, t2)
2455
2456 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2457 class Naive(tzinfo):
2458 def utcoffset(self, dt): return None
2459 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2460 self.assertRaises(TypeError, lambda: t1 == t2)
2461 self.assertEqual(t2, t2)
2462
2463 # OTOH, it's OK to compare two of these mixing the two ways of being
2464 # naive.
2465 t1 = self.theclass(5, 6, 7)
2466 self.assertEqual(t1, t2)
2467
2468 # Try a bogus uctoffset.
2469 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002470 def utcoffset(self, dt):
2471 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002472 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2473 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002474 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002475
Tim Peters2a799bf2002-12-16 20:18:38 +00002476 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002477 # Try one without a tzinfo.
2478 args = 6, 7, 23, 20, 59, 1, 64**2
2479 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002480 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002481 green = pickler.dumps(orig, proto)
2482 derived = unpickler.loads(green)
2483 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002484
2485 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002486 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002487 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002488 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002489 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002490 green = pickler.dumps(orig, proto)
2491 derived = unpickler.loads(green)
2492 self.assertEqual(orig, derived)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002493 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002494 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2495 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002496
2497 def test_extreme_hashes(self):
2498 # If an attempt is made to hash these via subtracting the offset
2499 # then hashing a datetime object, OverflowError results. The
2500 # Python implementation used to blow up here.
2501 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2502 hash(t)
2503 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2504 tzinfo=FixedOffset(-1439, ""))
2505 hash(t)
2506
2507 # OTOH, an OOB offset should blow up.
2508 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2509 self.assertRaises(ValueError, hash, t)
2510
2511 def test_zones(self):
2512 est = FixedOffset(-300, "EST")
2513 utc = FixedOffset(0, "UTC")
2514 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002515 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2516 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2517 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002518 self.assertEqual(t1.tzinfo, est)
2519 self.assertEqual(t2.tzinfo, utc)
2520 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002521 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2522 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2523 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002524 self.assertEqual(t1.tzname(), "EST")
2525 self.assertEqual(t2.tzname(), "UTC")
2526 self.assertEqual(t3.tzname(), "MET")
2527 self.assertEqual(hash(t1), hash(t2))
2528 self.assertEqual(hash(t1), hash(t3))
2529 self.assertEqual(hash(t2), hash(t3))
2530 self.assertEqual(t1, t2)
2531 self.assertEqual(t1, t3)
2532 self.assertEqual(t2, t3)
2533 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2534 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2535 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002536 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002537 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2538 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2539 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2540
2541 def test_combine(self):
2542 met = FixedOffset(60, "MET")
2543 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002544 tz = time(18, 45, 3, 1234, tzinfo=met)
2545 dt = datetime.combine(d, tz)
2546 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002547 tzinfo=met))
2548
2549 def test_extract(self):
2550 met = FixedOffset(60, "MET")
2551 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2552 self.assertEqual(dt.date(), date(2002, 3, 4))
2553 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002554 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002555
2556 def test_tz_aware_arithmetic(self):
2557 import random
2558
2559 now = self.theclass.now()
2560 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002561 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002562 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002563 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002564 self.assertEqual(nowaware.timetz(), timeaware)
2565
2566 # Can't mix aware and non-aware.
2567 self.assertRaises(TypeError, lambda: now - nowaware)
2568 self.assertRaises(TypeError, lambda: nowaware - now)
2569
Tim Peters0bf60bd2003-01-08 20:40:01 +00002570 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002571 self.assertRaises(TypeError, lambda: now + nowaware)
2572 self.assertRaises(TypeError, lambda: nowaware + now)
2573 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2574
2575 # Subtracting should yield 0.
2576 self.assertEqual(now - now, timedelta(0))
2577 self.assertEqual(nowaware - nowaware, timedelta(0))
2578
2579 # Adding a delta should preserve tzinfo.
2580 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2581 nowawareplus = nowaware + delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002582 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002583 nowawareplus2 = delta + nowaware
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002584 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002585 self.assertEqual(nowawareplus, nowawareplus2)
2586
2587 # that - delta should be what we started with, and that - what we
2588 # started with should be delta.
2589 diff = nowawareplus - delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002590 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002591 self.assertEqual(nowaware, diff)
2592 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2593 self.assertEqual(nowawareplus - nowaware, delta)
2594
2595 # Make up a random timezone.
2596 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002597 # Attach it to nowawareplus.
2598 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002599 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002600 # Make sure the difference takes the timezone adjustments into account.
2601 got = nowaware - nowawareplus
2602 # Expected: (nowaware base - nowaware offset) -
2603 # (nowawareplus base - nowawareplus offset) =
2604 # (nowaware base - nowawareplus base) +
2605 # (nowawareplus offset - nowaware offset) =
2606 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002607 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002608 self.assertEqual(got, expected)
2609
2610 # Try max possible difference.
2611 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2612 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2613 tzinfo=FixedOffset(-1439, "max"))
2614 maxdiff = max - min
2615 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2616 timedelta(minutes=2*1439))
2617
2618 def test_tzinfo_now(self):
2619 meth = self.theclass.now
2620 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2621 base = meth()
2622 # Try with and without naming the keyword.
2623 off42 = FixedOffset(42, "42")
2624 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002625 again = meth(tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002626 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002627 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002628 # Bad argument with and w/o naming the keyword.
2629 self.assertRaises(TypeError, meth, 16)
2630 self.assertRaises(TypeError, meth, tzinfo=16)
2631 # Bad keyword name.
2632 self.assertRaises(TypeError, meth, tinfo=off42)
2633 # Too many args.
2634 self.assertRaises(TypeError, meth, off42, off42)
2635
Tim Peters10cadce2003-01-23 19:58:02 +00002636 # We don't know which time zone we're in, and don't have a tzinfo
2637 # class to represent it, so seeing whether a tz argument actually
2638 # does a conversion is tricky.
2639 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2640 utc = FixedOffset(0, "utc", 0)
2641 for dummy in range(3):
2642 now = datetime.now(weirdtz)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002643 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002644 utcnow = datetime.utcnow().replace(tzinfo=utc)
2645 now2 = utcnow.astimezone(weirdtz)
2646 if abs(now - now2) < timedelta(seconds=30):
2647 break
2648 # Else the code is broken, or more than 30 seconds passed between
2649 # calls; assuming the latter, just try again.
2650 else:
2651 # Three strikes and we're out.
2652 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2653
Tim Peters2a799bf2002-12-16 20:18:38 +00002654 def test_tzinfo_fromtimestamp(self):
2655 import time
2656 meth = self.theclass.fromtimestamp
2657 ts = time.time()
2658 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2659 base = meth(ts)
2660 # Try with and without naming the keyword.
2661 off42 = FixedOffset(42, "42")
2662 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002663 again = meth(ts, tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002664 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002665 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002666 # Bad argument with and w/o naming the keyword.
2667 self.assertRaises(TypeError, meth, ts, 16)
2668 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2669 # Bad keyword name.
2670 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2671 # Too many args.
2672 self.assertRaises(TypeError, meth, ts, off42, off42)
2673 # Too few args.
2674 self.assertRaises(TypeError, meth)
2675
Tim Peters2a44a8d2003-01-23 20:53:10 +00002676 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002677 timestamp = 1000000000
2678 utcdatetime = datetime.utcfromtimestamp(timestamp)
2679 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2680 # But on some flavor of Mac, it's nowhere near that. So we can't have
2681 # any idea here what time that actually is, we can only test that
2682 # relative changes match.
2683 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2684 tz = FixedOffset(utcoffset, "tz", 0)
2685 expected = utcdatetime + utcoffset
2686 got = datetime.fromtimestamp(timestamp, tz)
2687 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002688
Tim Peters2a799bf2002-12-16 20:18:38 +00002689 def test_tzinfo_utcnow(self):
2690 meth = self.theclass.utcnow
2691 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2692 base = meth()
2693 # Try with and without naming the keyword; for whatever reason,
2694 # utcnow() doesn't accept a tzinfo argument.
2695 off42 = FixedOffset(42, "42")
2696 self.assertRaises(TypeError, meth, off42)
2697 self.assertRaises(TypeError, meth, tzinfo=off42)
2698
2699 def test_tzinfo_utcfromtimestamp(self):
2700 import time
2701 meth = self.theclass.utcfromtimestamp
2702 ts = time.time()
2703 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2704 base = meth(ts)
2705 # Try with and without naming the keyword; for whatever reason,
2706 # utcfromtimestamp() doesn't accept a tzinfo argument.
2707 off42 = FixedOffset(42, "42")
2708 self.assertRaises(TypeError, meth, ts, off42)
2709 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2710
2711 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002712 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002713 # DST flag.
2714 class DST(tzinfo):
2715 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002716 if isinstance(dstvalue, int):
2717 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002718 self.dstvalue = dstvalue
2719 def dst(self, dt):
2720 return self.dstvalue
2721
2722 cls = self.theclass
2723 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2724 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2725 t = d.timetuple()
2726 self.assertEqual(1, t.tm_year)
2727 self.assertEqual(1, t.tm_mon)
2728 self.assertEqual(1, t.tm_mday)
2729 self.assertEqual(10, t.tm_hour)
2730 self.assertEqual(20, t.tm_min)
2731 self.assertEqual(30, t.tm_sec)
2732 self.assertEqual(0, t.tm_wday)
2733 self.assertEqual(1, t.tm_yday)
2734 self.assertEqual(flag, t.tm_isdst)
2735
2736 # dst() returns wrong type.
2737 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2738
2739 # dst() at the edge.
2740 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2741 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2742
2743 # dst() out of range.
2744 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2745 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2746
2747 def test_utctimetuple(self):
2748 class DST(tzinfo):
2749 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002750 if isinstance(dstvalue, int):
2751 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002752 self.dstvalue = dstvalue
2753 def dst(self, dt):
2754 return self.dstvalue
2755
2756 cls = self.theclass
2757 # This can't work: DST didn't implement utcoffset.
2758 self.assertRaises(NotImplementedError,
2759 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2760
2761 class UOFS(DST):
2762 def __init__(self, uofs, dofs=None):
2763 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002764 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002765 def utcoffset(self, dt):
2766 return self.uofs
2767
2768 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2769 # in effect for a UTC time.
2770 for dstvalue in -33, 33, 0, None:
2771 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2772 t = d.utctimetuple()
2773 self.assertEqual(d.year, t.tm_year)
2774 self.assertEqual(d.month, t.tm_mon)
2775 self.assertEqual(d.day, t.tm_mday)
2776 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2777 self.assertEqual(13, t.tm_min)
2778 self.assertEqual(d.second, t.tm_sec)
2779 self.assertEqual(d.weekday(), t.tm_wday)
2780 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2781 t.tm_yday)
2782 self.assertEqual(0, t.tm_isdst)
2783
2784 # At the edges, UTC adjustment can normalize into years out-of-range
2785 # for a datetime object. Ensure that a correct timetuple is
2786 # created anyway.
2787 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2788 # That goes back 1 minute less than a full day.
2789 t = tiny.utctimetuple()
2790 self.assertEqual(t.tm_year, MINYEAR-1)
2791 self.assertEqual(t.tm_mon, 12)
2792 self.assertEqual(t.tm_mday, 31)
2793 self.assertEqual(t.tm_hour, 0)
2794 self.assertEqual(t.tm_min, 1)
2795 self.assertEqual(t.tm_sec, 37)
2796 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2797 self.assertEqual(t.tm_isdst, 0)
2798
2799 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2800 # That goes forward 1 minute less than a full day.
2801 t = huge.utctimetuple()
2802 self.assertEqual(t.tm_year, MAXYEAR+1)
2803 self.assertEqual(t.tm_mon, 1)
2804 self.assertEqual(t.tm_mday, 1)
2805 self.assertEqual(t.tm_hour, 23)
2806 self.assertEqual(t.tm_min, 58)
2807 self.assertEqual(t.tm_sec, 37)
2808 self.assertEqual(t.tm_yday, 1)
2809 self.assertEqual(t.tm_isdst, 0)
2810
2811 def test_tzinfo_isoformat(self):
2812 zero = FixedOffset(0, "+00:00")
2813 plus = FixedOffset(220, "+03:40")
2814 minus = FixedOffset(-231, "-03:51")
2815 unknown = FixedOffset(None, "")
2816
2817 cls = self.theclass
2818 datestr = '0001-02-03'
2819 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002820 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002821 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2822 timestr = '04:05:59' + (us and '.987001' or '')
2823 ofsstr = ofs is not None and d.tzname() or ''
2824 tailstr = timestr + ofsstr
2825 iso = d.isoformat()
2826 self.assertEqual(iso, datestr + 'T' + tailstr)
2827 self.assertEqual(iso, d.isoformat('T'))
2828 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2829 self.assertEqual(str(d), datestr + ' ' + tailstr)
2830
Tim Peters12bf3392002-12-24 05:41:27 +00002831 def test_replace(self):
2832 cls = self.theclass
2833 z100 = FixedOffset(100, "+100")
2834 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2835 args = [1, 2, 3, 4, 5, 6, 7, z100]
2836 base = cls(*args)
2837 self.assertEqual(base, base.replace())
2838
2839 i = 0
2840 for name, newval in (("year", 2),
2841 ("month", 3),
2842 ("day", 4),
2843 ("hour", 5),
2844 ("minute", 6),
2845 ("second", 7),
2846 ("microsecond", 8),
2847 ("tzinfo", zm200)):
2848 newargs = args[:]
2849 newargs[i] = newval
2850 expected = cls(*newargs)
2851 got = base.replace(**{name: newval})
2852 self.assertEqual(expected, got)
2853 i += 1
2854
2855 # Ensure we can get rid of a tzinfo.
2856 self.assertEqual(base.tzname(), "+100")
2857 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002858 self.assertTrue(base2.tzinfo is None)
2859 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002860
2861 # Ensure we can add one.
2862 base3 = base2.replace(tzinfo=z100)
2863 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002864 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002865
2866 # Out of bounds.
2867 base = cls(2000, 2, 29)
2868 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002869
Tim Peters80475bb2002-12-25 07:40:55 +00002870 def test_more_astimezone(self):
2871 # The inherited test_astimezone covered some trivial and error cases.
2872 fnone = FixedOffset(None, "None")
2873 f44m = FixedOffset(44, "44")
2874 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2875
Tim Peters10cadce2003-01-23 19:58:02 +00002876 dt = self.theclass.now(tz=f44m)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002877 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002878 # Replacing with degenerate tzinfo raises an exception.
2879 self.assertRaises(ValueError, dt.astimezone, fnone)
2880 # Ditto with None tz.
2881 self.assertRaises(TypeError, dt.astimezone, None)
2882 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002883 x = dt.astimezone(dt.tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002884 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002885 self.assertEqual(x.date(), dt.date())
2886 self.assertEqual(x.time(), dt.time())
2887
2888 # Replacing with different tzinfo does adjust.
2889 got = dt.astimezone(fm5h)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002890 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002891 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2892 expected = dt - dt.utcoffset() # in effect, convert to UTC
2893 expected += fm5h.utcoffset(dt) # and from there to local time
2894 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2895 self.assertEqual(got.date(), expected.date())
2896 self.assertEqual(got.time(), expected.time())
2897 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002898 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002899 self.assertEqual(got, expected)
2900
Tim Peters4c0db782002-12-26 05:01:19 +00002901 def test_aware_subtract(self):
2902 cls = self.theclass
2903
Tim Peters60c76e42002-12-27 00:41:11 +00002904 # Ensure that utcoffset() is ignored when the operands have the
2905 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002906 class OperandDependentOffset(tzinfo):
2907 def utcoffset(self, t):
2908 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002909 # d0 and d1 equal after adjustment
2910 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002911 else:
Tim Peters397301e2003-01-02 21:28:08 +00002912 # d2 off in the weeds
2913 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002914
2915 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2916 d0 = base.replace(minute=3)
2917 d1 = base.replace(minute=9)
2918 d2 = base.replace(minute=11)
2919 for x in d0, d1, d2:
2920 for y in d0, d1, d2:
2921 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002922 expected = timedelta(minutes=x.minute - y.minute)
2923 self.assertEqual(got, expected)
2924
2925 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2926 # ignored.
2927 base = cls(8, 9, 10, 11, 12, 13, 14)
2928 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2929 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2930 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2931 for x in d0, d1, d2:
2932 for y in d0, d1, d2:
2933 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002934 if (x is d0 or x is d1) and (y is d0 or y is d1):
2935 expected = timedelta(0)
2936 elif x is y is d2:
2937 expected = timedelta(0)
2938 elif x is d2:
2939 expected = timedelta(minutes=(11-59)-0)
2940 else:
2941 assert y is d2
2942 expected = timedelta(minutes=0-(11-59))
2943 self.assertEqual(got, expected)
2944
Tim Peters60c76e42002-12-27 00:41:11 +00002945 def test_mixed_compare(self):
2946 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002947 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002948 self.assertEqual(t1, t2)
2949 t2 = t2.replace(tzinfo=None)
2950 self.assertEqual(t1, t2)
2951 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2952 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002953 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2954 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002955
Tim Peters0bf60bd2003-01-08 20:40:01 +00002956 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002957 class Varies(tzinfo):
2958 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002959 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002960 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002961 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002962 return self.offset
2963
2964 v = Varies()
2965 t1 = t2.replace(tzinfo=v)
2966 t2 = t2.replace(tzinfo=v)
2967 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2968 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2969 self.assertEqual(t1, t2)
2970
2971 # But if they're not identical, it isn't ignored.
2972 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002973 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002974
Tim Petersa98924a2003-05-17 05:55:19 +00002975 def test_subclass_datetimetz(self):
2976
2977 class C(self.theclass):
2978 theAnswer = 42
2979
2980 def __new__(cls, *args, **kws):
2981 temp = kws.copy()
2982 extra = temp.pop('extra')
2983 result = self.theclass.__new__(cls, *args, **temp)
2984 result.extra = extra
2985 return result
2986
2987 def newmeth(self, start):
2988 return start + self.hour + self.year
2989
2990 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2991
2992 dt1 = self.theclass(*args)
2993 dt2 = C(*args, **{'extra': 7})
2994
2995 self.assertEqual(dt2.__class__, C)
2996 self.assertEqual(dt2.theAnswer, 42)
2997 self.assertEqual(dt2.extra, 7)
2998 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2999 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3000
Tim Peters621818b2002-12-29 23:44:49 +00003001# Pain to set up DST-aware tzinfo classes.
3002
3003def first_sunday_on_or_after(dt):
3004 days_to_go = 6 - dt.weekday()
3005 if days_to_go:
3006 dt += timedelta(days_to_go)
3007 return dt
3008
3009ZERO = timedelta(0)
3010HOUR = timedelta(hours=1)
3011DAY = timedelta(days=1)
3012# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3013DSTSTART = datetime(1, 4, 1, 2)
3014# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003015# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3016# being standard time on that day, there is no spelling in local time of
3017# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3018DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003019
3020class USTimeZone(tzinfo):
3021
3022 def __init__(self, hours, reprname, stdname, dstname):
3023 self.stdoffset = timedelta(hours=hours)
3024 self.reprname = reprname
3025 self.stdname = stdname
3026 self.dstname = dstname
3027
3028 def __repr__(self):
3029 return self.reprname
3030
3031 def tzname(self, dt):
3032 if self.dst(dt):
3033 return self.dstname
3034 else:
3035 return self.stdname
3036
3037 def utcoffset(self, dt):
3038 return self.stdoffset + self.dst(dt)
3039
3040 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003041 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003042 # An exception instead may be sensible here, in one or more of
3043 # the cases.
3044 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003045 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003046
3047 # Find first Sunday in April.
3048 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3049 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3050
3051 # Find last Sunday in October.
3052 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3053 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3054
Tim Peters621818b2002-12-29 23:44:49 +00003055 # Can't compare naive to aware objects, so strip the timezone from
3056 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003057 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003058 return HOUR
3059 else:
3060 return ZERO
3061
Tim Peters521fc152002-12-31 17:36:56 +00003062Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3063Central = USTimeZone(-6, "Central", "CST", "CDT")
3064Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3065Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003066utc_real = FixedOffset(0, "UTC", 0)
3067# For better test coverage, we want another flavor of UTC that's west of
3068# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003069utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003070
3071class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003072 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003073 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003074 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003075
Tim Peters0bf60bd2003-01-08 20:40:01 +00003076 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003077
Tim Peters521fc152002-12-31 17:36:56 +00003078 # Check a time that's inside DST.
3079 def checkinside(self, dt, tz, utc, dston, dstoff):
3080 self.assertEqual(dt.dst(), HOUR)
3081
3082 # Conversion to our own timezone is always an identity.
3083 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003084
3085 asutc = dt.astimezone(utc)
3086 there_and_back = asutc.astimezone(tz)
3087
3088 # Conversion to UTC and back isn't always an identity here,
3089 # because there are redundant spellings (in local time) of
3090 # UTC time when DST begins: the clock jumps from 1:59:59
3091 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3092 # make sense then. The classes above treat 2:MM:SS as
3093 # daylight time then (it's "after 2am"), really an alias
3094 # for 1:MM:SS standard time. The latter form is what
3095 # conversion back from UTC produces.
3096 if dt.date() == dston.date() and dt.hour == 2:
3097 # We're in the redundant hour, and coming back from
3098 # UTC gives the 1:MM:SS standard-time spelling.
3099 self.assertEqual(there_and_back + HOUR, dt)
3100 # Although during was considered to be in daylight
3101 # time, there_and_back is not.
3102 self.assertEqual(there_and_back.dst(), ZERO)
3103 # They're the same times in UTC.
3104 self.assertEqual(there_and_back.astimezone(utc),
3105 dt.astimezone(utc))
3106 else:
3107 # We're not in the redundant hour.
3108 self.assertEqual(dt, there_and_back)
3109
Tim Peters327098a2003-01-20 22:54:38 +00003110 # Because we have a redundant spelling when DST begins, there is
3111 # (unforunately) an hour when DST ends that can't be spelled at all in
3112 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3113 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3114 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3115 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3116 # expressed in local time. Nevertheless, we want conversion back
3117 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003118 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003119 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003120 if dt.date() == dstoff.date() and dt.hour == 0:
3121 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003122 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003123 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3124 nexthour_utc += HOUR
3125 nexthour_tz = nexthour_utc.astimezone(tz)
3126 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003127 else:
Tim Peters327098a2003-01-20 22:54:38 +00003128 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003129
3130 # Check a time that's outside DST.
3131 def checkoutside(self, dt, tz, utc):
3132 self.assertEqual(dt.dst(), ZERO)
3133
3134 # Conversion to our own timezone is always an identity.
3135 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003136
3137 # Converting to UTC and back is an identity too.
3138 asutc = dt.astimezone(utc)
3139 there_and_back = asutc.astimezone(tz)
3140 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003141
Tim Peters1024bf82002-12-30 17:09:40 +00003142 def convert_between_tz_and_utc(self, tz, utc):
3143 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003144 # Because 1:MM on the day DST ends is taken as being standard time,
3145 # there is no spelling in tz for the last hour of daylight time.
3146 # For purposes of the test, the last hour of DST is 0:MM, which is
3147 # taken as being daylight time (and 1:MM is taken as being standard
3148 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003149 dstoff = self.dstoff.replace(tzinfo=tz)
3150 for delta in (timedelta(weeks=13),
3151 DAY,
3152 HOUR,
3153 timedelta(minutes=1),
3154 timedelta(microseconds=1)):
3155
Tim Peters521fc152002-12-31 17:36:56 +00003156 self.checkinside(dston, tz, utc, dston, dstoff)
3157 for during in dston + delta, dstoff - delta:
3158 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003159
Tim Peters521fc152002-12-31 17:36:56 +00003160 self.checkoutside(dstoff, tz, utc)
3161 for outside in dston - delta, dstoff + delta:
3162 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003163
Tim Peters621818b2002-12-29 23:44:49 +00003164 def test_easy(self):
3165 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003166 self.convert_between_tz_and_utc(Eastern, utc_real)
3167 self.convert_between_tz_and_utc(Pacific, utc_real)
3168 self.convert_between_tz_and_utc(Eastern, utc_fake)
3169 self.convert_between_tz_and_utc(Pacific, utc_fake)
3170 # The next is really dancing near the edge. It works because
3171 # Pacific and Eastern are far enough apart that their "problem
3172 # hours" don't overlap.
3173 self.convert_between_tz_and_utc(Eastern, Pacific)
3174 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003175 # OTOH, these fail! Don't enable them. The difficulty is that
3176 # the edge case tests assume that every hour is representable in
3177 # the "utc" class. This is always true for a fixed-offset tzinfo
3178 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3179 # For these adjacent DST-aware time zones, the range of time offsets
3180 # tested ends up creating hours in the one that aren't representable
3181 # in the other. For the same reason, we would see failures in the
3182 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3183 # offset deltas in convert_between_tz_and_utc().
3184 #
3185 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3186 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003187
Tim Petersf3615152003-01-01 21:51:37 +00003188 def test_tricky(self):
3189 # 22:00 on day before daylight starts.
3190 fourback = self.dston - timedelta(hours=4)
3191 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003192 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003193 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3194 # 2", we should get the 3 spelling.
3195 # If we plug 22:00 the day before into Eastern, it "looks like std
3196 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3197 # to 22:00 lands on 2:00, which makes no sense in local time (the
3198 # local clock jumps from 1 to 3). The point here is to make sure we
3199 # get the 3 spelling.
3200 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003201 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003202 self.assertEqual(expected, got)
3203
3204 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3205 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003206 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003207 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3208 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3209 # spelling.
3210 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003211 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003212 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003213
Tim Petersadf64202003-01-04 06:03:15 +00003214 # Now on the day DST ends, we want "repeat an hour" behavior.
3215 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3216 # EST 23:MM 0:MM 1:MM 2:MM
3217 # EDT 0:MM 1:MM 2:MM 3:MM
3218 # wall 0:MM 1:MM 1:MM 2:MM against these
3219 for utc in utc_real, utc_fake:
3220 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003221 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003222 # Convert that to UTC.
3223 first_std_hour -= tz.utcoffset(None)
3224 # Adjust for possibly fake UTC.
3225 asutc = first_std_hour + utc.utcoffset(None)
3226 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3227 # tz=Eastern.
3228 asutcbase = asutc.replace(tzinfo=utc)
3229 for tzhour in (0, 1, 1, 2):
3230 expectedbase = self.dstoff.replace(hour=tzhour)
3231 for minute in 0, 30, 59:
3232 expected = expectedbase.replace(minute=minute)
3233 asutc = asutcbase.replace(minute=minute)
3234 astz = asutc.astimezone(tz)
3235 self.assertEqual(astz.replace(tzinfo=None), expected)
3236 asutcbase += HOUR
3237
3238
Tim Peters710fb152003-01-02 19:35:54 +00003239 def test_bogus_dst(self):
3240 class ok(tzinfo):
3241 def utcoffset(self, dt): return HOUR
3242 def dst(self, dt): return HOUR
3243
3244 now = self.theclass.now().replace(tzinfo=utc_real)
3245 # Doesn't blow up.
3246 now.astimezone(ok())
3247
3248 # Does blow up.
3249 class notok(ok):
3250 def dst(self, dt): return None
3251 self.assertRaises(ValueError, now.astimezone, notok())
3252
Tim Peters52dcce22003-01-23 16:36:11 +00003253 def test_fromutc(self):
3254 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3255 now = datetime.utcnow().replace(tzinfo=utc_real)
3256 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3257 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3258 enow = Eastern.fromutc(now) # doesn't blow up
3259 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3260 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3261 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3262
3263 # Always converts UTC to standard time.
3264 class FauxUSTimeZone(USTimeZone):
3265 def fromutc(self, dt):
3266 return dt + self.stdoffset
3267 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3268
3269 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3270 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3271 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3272
3273 # Check around DST start.
3274 start = self.dston.replace(hour=4, tzinfo=Eastern)
3275 fstart = start.replace(tzinfo=FEastern)
3276 for wall in 23, 0, 1, 3, 4, 5:
3277 expected = start.replace(hour=wall)
3278 if wall == 23:
3279 expected -= timedelta(days=1)
3280 got = Eastern.fromutc(start)
3281 self.assertEqual(expected, got)
3282
3283 expected = fstart + FEastern.stdoffset
3284 got = FEastern.fromutc(fstart)
3285 self.assertEqual(expected, got)
3286
3287 # Ensure astimezone() calls fromutc() too.
3288 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3289 self.assertEqual(expected, got)
3290
3291 start += HOUR
3292 fstart += HOUR
3293
3294 # Check around DST end.
3295 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3296 fstart = start.replace(tzinfo=FEastern)
3297 for wall in 0, 1, 1, 2, 3, 4:
3298 expected = start.replace(hour=wall)
3299 got = Eastern.fromutc(start)
3300 self.assertEqual(expected, got)
3301
3302 expected = fstart + FEastern.stdoffset
3303 got = FEastern.fromutc(fstart)
3304 self.assertEqual(expected, got)
3305
3306 # Ensure astimezone() calls fromutc() too.
3307 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3308 self.assertEqual(expected, got)
3309
3310 start += HOUR
3311 fstart += HOUR
3312
Tim Peters710fb152003-01-02 19:35:54 +00003313
Tim Peters528ca532004-09-16 01:30:50 +00003314#############################################################################
3315# oddballs
3316
3317class Oddballs(unittest.TestCase):
3318
3319 def test_bug_1028306(self):
3320 # Trying to compare a date to a datetime should act like a mixed-
3321 # type comparison, despite that datetime is a subclass of date.
3322 as_date = date.today()
3323 as_datetime = datetime.combine(as_date, time())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003324 self.assertTrue(as_date != as_datetime)
3325 self.assertTrue(as_datetime != as_date)
3326 self.assertTrue(not as_date == as_datetime)
3327 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003328 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3329 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3330 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3331 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3332 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3333 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3334 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3335 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3336
3337 # Neverthelss, comparison should work with the base-class (date)
3338 # projection if use of a date method is forced.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003339 self.assertTrue(as_date.__eq__(as_datetime))
Tim Peters528ca532004-09-16 01:30:50 +00003340 different_day = (as_date.day + 1) % 20 + 1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003341 self.assertTrue(not as_date.__eq__(as_datetime.replace(day=
Tim Peters528ca532004-09-16 01:30:50 +00003342 different_day)))
3343
3344 # And date should compare with other subclasses of date. If a
3345 # subclass wants to stop this, it's up to the subclass to do so.
3346 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3347 self.assertEqual(as_date, date_sc)
3348 self.assertEqual(date_sc, as_date)
3349
3350 # Ditto for datetimes.
3351 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3352 as_date.day, 0, 0, 0)
3353 self.assertEqual(as_datetime, datetime_sc)
3354 self.assertEqual(datetime_sc, as_datetime)
3355
Tim Peters2a799bf2002-12-16 20:18:38 +00003356def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003357 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003358
3359if __name__ == "__main__":
3360 test_main()