blob: f9bb464774b0ca768cf00c452fb4a993d7baca49 [file] [log] [blame]
Tim Peters0bf60bd2003-01-08 20:40:01 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
Mark Dickinson7000e9e2010-05-09 09:30:06 +00005from __future__ import division
Alexander Belopolskya26cf462010-05-26 19:43:16 +00006import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
8import cPickle
Tim Peters2a799bf2002-12-16 20:18:38 +00009import unittest
10
11from test import test_support
12
13from datetime import MINYEAR, MAXYEAR
14from datetime import timedelta
15from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000016from datetime import time
17from datetime import date, datetime
18
Tim Peters35ad6412003-02-05 04:08:07 +000019pickle_choices = [(pickler, unpickler, proto)
20 for pickler in pickle, cPickle
21 for unpickler in pickle, cPickle
22 for proto in range(3)]
23assert len(pickle_choices) == 2*2*3
Guido van Rossum177e41a2003-01-30 22:06:23 +000024
Tim Peters68124bb2003-02-08 03:46:31 +000025# An arbitrary collection of objects of non-datetime types, for testing
26# mixed-type comparisons.
27OTHERSTUFF = (10, 10L, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000028
Tim Peters2a799bf2002-12-16 20:18:38 +000029
30#############################################################################
31# module tests
32
33class TestModule(unittest.TestCase):
34
35 def test_constants(self):
36 import datetime
37 self.assertEqual(datetime.MINYEAR, 1)
38 self.assertEqual(datetime.MAXYEAR, 9999)
39
40#############################################################################
41# tzinfo tests
42
43class FixedOffset(tzinfo):
44 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000045 if isinstance(offset, int):
46 offset = timedelta(minutes=offset)
47 if isinstance(dstoffset, int):
48 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000049 self.__offset = offset
50 self.__name = name
51 self.__dstoffset = dstoffset
52 def __repr__(self):
53 return self.__name.lower()
54 def utcoffset(self, dt):
55 return self.__offset
56 def tzname(self, dt):
57 return self.__name
58 def dst(self, dt):
59 return self.__dstoffset
60
Tim Petersfb8472c2002-12-21 05:04:42 +000061class PicklableFixedOffset(FixedOffset):
62 def __init__(self, offset=None, name=None, dstoffset=None):
63 FixedOffset.__init__(self, offset, name, dstoffset)
64
Tim Peters2a799bf2002-12-16 20:18:38 +000065class TestTZInfo(unittest.TestCase):
66
67 def test_non_abstractness(self):
68 # In order to allow subclasses to get pickled, the C implementation
69 # wasn't able to get away with having __init__ raise
70 # NotImplementedError.
71 useless = tzinfo()
72 dt = datetime.max
73 self.assertRaises(NotImplementedError, useless.tzname, dt)
74 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
75 self.assertRaises(NotImplementedError, useless.dst, dt)
76
77 def test_subclass_must_override(self):
78 class NotEnough(tzinfo):
79 def __init__(self, offset, name):
80 self.__offset = offset
81 self.__name = name
Benjamin Peterson5c8da862009-06-30 22:57:08 +000082 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000083 ne = NotEnough(3, "NotByALongShot")
Ezio Melottib0f5adc2010-01-24 16:58:36 +000084 self.assertIsInstance(ne, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000085
86 dt = datetime.now()
87 self.assertRaises(NotImplementedError, ne.tzname, dt)
88 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
89 self.assertRaises(NotImplementedError, ne.dst, dt)
90
91 def test_normal(self):
92 fo = FixedOffset(3, "Three")
Ezio Melottib0f5adc2010-01-24 16:58:36 +000093 self.assertIsInstance(fo, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000094 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +000095 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +000096 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +000097 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +000098
99 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000100 # There's no point to pickling tzinfo objects on their own (they
101 # carry no data), but they need to be picklable anyway else
102 # concrete subclasses can't be pickled.
103 orig = tzinfo.__new__(tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000104 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000105 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000106 green = pickler.dumps(orig, proto)
107 derived = unpickler.loads(green)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000108 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000109
110 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000111 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000112 offset = timedelta(minutes=-300)
113 orig = PicklableFixedOffset(offset, 'cookie')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000114 self.assertIsInstance(orig, tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000115 self.assertTrue(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000116 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000117 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000118 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000119 green = pickler.dumps(orig, proto)
120 derived = unpickler.loads(green)
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000121 self.assertIsInstance(derived, tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000122 self.assertTrue(type(derived) is PicklableFixedOffset)
Tim Petersf2715e02003-02-19 02:35:07 +0000123 self.assertEqual(derived.utcoffset(None), offset)
124 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000125
126#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000127# Base clase for testing a particular aspect of timedelta, time, date and
128# datetime comparisons.
129
Collin Winterc2898c52007-04-25 17:29:52 +0000130class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000131 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
132
133 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
134 # legit constructor.
135
136 def test_harmless_mixed_comparison(self):
137 me = self.theclass(1, 1, 1)
138
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000139 self.assertFalse(me == ())
140 self.assertTrue(me != ())
141 self.assertFalse(() == me)
142 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000143
Ezio Melottiaa980582010-01-23 23:04:36 +0000144 self.assertIn(me, [1, 20L, [], me])
145 self.assertIn([], [me, 1, 20L, []])
Tim Peters07534a62003-02-07 22:50:28 +0000146
147 def test_harmful_mixed_comparison(self):
148 me = self.theclass(1, 1, 1)
149
150 self.assertRaises(TypeError, lambda: me < ())
151 self.assertRaises(TypeError, lambda: me <= ())
152 self.assertRaises(TypeError, lambda: me > ())
153 self.assertRaises(TypeError, lambda: me >= ())
154
155 self.assertRaises(TypeError, lambda: () < me)
156 self.assertRaises(TypeError, lambda: () <= me)
157 self.assertRaises(TypeError, lambda: () > me)
158 self.assertRaises(TypeError, lambda: () >= me)
159
160 self.assertRaises(TypeError, cmp, (), me)
161 self.assertRaises(TypeError, cmp, me, ())
162
163#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000164# timedelta tests
165
Collin Winterc2898c52007-04-25 17:29:52 +0000166class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000167
168 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000169
170 def test_constructor(self):
171 eq = self.assertEqual
172 td = timedelta
173
174 # Check keyword args to constructor
175 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
176 milliseconds=0, microseconds=0))
177 eq(td(1), td(days=1))
178 eq(td(0, 1), td(seconds=1))
179 eq(td(0, 0, 1), td(microseconds=1))
180 eq(td(weeks=1), td(days=7))
181 eq(td(days=1), td(hours=24))
182 eq(td(hours=1), td(minutes=60))
183 eq(td(minutes=1), td(seconds=60))
184 eq(td(seconds=1), td(milliseconds=1000))
185 eq(td(milliseconds=1), td(microseconds=1000))
186
187 # Check float args to constructor
188 eq(td(weeks=1.0/7), td(days=1))
189 eq(td(days=1.0/24), td(hours=1))
190 eq(td(hours=1.0/60), td(minutes=1))
191 eq(td(minutes=1.0/60), td(seconds=1))
192 eq(td(seconds=0.001), td(milliseconds=1))
193 eq(td(milliseconds=0.001), td(microseconds=1))
194
195 def test_computations(self):
196 eq = self.assertEqual
197 td = timedelta
198
199 a = td(7) # One week
200 b = td(0, 60) # One minute
201 c = td(0, 0, 1000) # One millisecond
202 eq(a+b+c, td(7, 60, 1000))
203 eq(a-b, td(6, 24*3600 - 60))
204 eq(-a, td(-7))
205 eq(+a, td(7))
206 eq(-b, td(-1, 24*3600 - 60))
207 eq(-c, td(-1, 24*3600 - 1, 999000))
208 eq(abs(a), a)
209 eq(abs(-a), a)
210 eq(td(6, 24*3600), a)
211 eq(td(0, 0, 60*1000000), b)
212 eq(a*10, td(70))
213 eq(a*10, 10*a)
214 eq(a*10L, 10*a)
215 eq(b*10, td(0, 600))
216 eq(10*b, td(0, 600))
217 eq(b*10L, td(0, 600))
218 eq(c*10, td(0, 0, 10000))
219 eq(10*c, td(0, 0, 10000))
220 eq(c*10L, td(0, 0, 10000))
221 eq(a*-1, -a)
222 eq(b*-2, -b-b)
223 eq(c*-2, -c+-c)
224 eq(b*(60*24), (b*60)*24)
225 eq(b*(60*24), (60*b)*24)
226 eq(c*1000, td(0, 1))
227 eq(1000*c, td(0, 1))
228 eq(a//7, td(1))
229 eq(b//10, td(0, 6))
230 eq(c//1000, td(0, 0, 1))
231 eq(a//10, td(0, 7*24*360))
232 eq(a//3600000, td(0, 0, 7*24*1000))
233
234 def test_disallowed_computations(self):
235 a = timedelta(42)
236
237 # Add/sub ints, longs, floats should be illegal
238 for i in 1, 1L, 1.0:
239 self.assertRaises(TypeError, lambda: a+i)
240 self.assertRaises(TypeError, lambda: a-i)
241 self.assertRaises(TypeError, lambda: i+a)
242 self.assertRaises(TypeError, lambda: i-a)
243
244 # Mul/div by float isn't supported.
245 x = 2.3
246 self.assertRaises(TypeError, lambda: a*x)
247 self.assertRaises(TypeError, lambda: x*a)
248 self.assertRaises(TypeError, lambda: a/x)
249 self.assertRaises(TypeError, lambda: x/a)
250 self.assertRaises(TypeError, lambda: a // x)
251 self.assertRaises(TypeError, lambda: x // a)
252
Andrew M. Kuchling081bb452008-10-03 16:42:52 +0000253 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000254 # Division by zero doesn't make sense.
255 for zero in 0, 0L:
256 self.assertRaises(TypeError, lambda: zero // a)
257 self.assertRaises(ZeroDivisionError, lambda: a // zero)
258
259 def test_basic_attributes(self):
260 days, seconds, us = 1, 7, 31
261 td = timedelta(days, seconds, us)
262 self.assertEqual(td.days, days)
263 self.assertEqual(td.seconds, seconds)
264 self.assertEqual(td.microseconds, us)
265
Antoine Pitroubcfaf802009-11-25 22:59:36 +0000266 def test_total_seconds(self):
267 td = timedelta(days=365)
268 self.assertEqual(td.total_seconds(), 31536000.0)
269 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
270 td = timedelta(seconds=total_seconds)
271 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson7000e9e2010-05-09 09:30:06 +0000272 # Issue8644: Test that td.total_seconds() has the same
273 # accuracy as td / timedelta(seconds=1).
274 for ms in [-1, -2, -123]:
275 td = timedelta(microseconds=ms)
276 self.assertEqual(td.total_seconds(),
277 ((24*3600*td.days + td.seconds)*10**6
278 + td.microseconds)/10**6)
Antoine Pitroubcfaf802009-11-25 22:59:36 +0000279
Tim Peters2a799bf2002-12-16 20:18:38 +0000280 def test_carries(self):
281 t1 = timedelta(days=100,
282 weeks=-7,
283 hours=-24*(100-49),
284 minutes=-3,
285 seconds=12,
286 microseconds=(3*60 - 12) * 1e6 + 1)
287 t2 = timedelta(microseconds=1)
288 self.assertEqual(t1, t2)
289
290 def test_hash_equality(self):
291 t1 = timedelta(days=100,
292 weeks=-7,
293 hours=-24*(100-49),
294 minutes=-3,
295 seconds=12,
296 microseconds=(3*60 - 12) * 1000000)
297 t2 = timedelta()
298 self.assertEqual(hash(t1), hash(t2))
299
300 t1 += timedelta(weeks=7)
301 t2 += timedelta(days=7*7)
302 self.assertEqual(t1, t2)
303 self.assertEqual(hash(t1), hash(t2))
304
305 d = {t1: 1}
306 d[t2] = 2
307 self.assertEqual(len(d), 1)
308 self.assertEqual(d[t1], 2)
309
310 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000311 args = 12, 34, 56
312 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000313 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000314 green = pickler.dumps(orig, proto)
315 derived = unpickler.loads(green)
316 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000317
318 def test_compare(self):
319 t1 = timedelta(2, 3, 4)
320 t2 = timedelta(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000321 self.assertTrue(t1 == t2)
322 self.assertTrue(t1 <= t2)
323 self.assertTrue(t1 >= t2)
324 self.assertTrue(not t1 != t2)
325 self.assertTrue(not t1 < t2)
326 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000327 self.assertEqual(cmp(t1, t2), 0)
328 self.assertEqual(cmp(t2, t1), 0)
329
330 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
331 t2 = timedelta(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000332 self.assertTrue(t1 < t2)
333 self.assertTrue(t2 > t1)
334 self.assertTrue(t1 <= t2)
335 self.assertTrue(t2 >= t1)
336 self.assertTrue(t1 != t2)
337 self.assertTrue(t2 != t1)
338 self.assertTrue(not t1 == t2)
339 self.assertTrue(not t2 == t1)
340 self.assertTrue(not t1 > t2)
341 self.assertTrue(not t2 < t1)
342 self.assertTrue(not t1 >= t2)
343 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000344 self.assertEqual(cmp(t1, t2), -1)
345 self.assertEqual(cmp(t2, t1), 1)
346
Tim Peters68124bb2003-02-08 03:46:31 +0000347 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000348 self.assertEqual(t1 == badarg, False)
349 self.assertEqual(t1 != badarg, True)
350 self.assertEqual(badarg == t1, False)
351 self.assertEqual(badarg != t1, True)
352
Tim Peters2a799bf2002-12-16 20:18:38 +0000353 self.assertRaises(TypeError, lambda: t1 <= badarg)
354 self.assertRaises(TypeError, lambda: t1 < badarg)
355 self.assertRaises(TypeError, lambda: t1 > badarg)
356 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000357 self.assertRaises(TypeError, lambda: badarg <= t1)
358 self.assertRaises(TypeError, lambda: badarg < t1)
359 self.assertRaises(TypeError, lambda: badarg > t1)
360 self.assertRaises(TypeError, lambda: badarg >= t1)
361
362 def test_str(self):
363 td = timedelta
364 eq = self.assertEqual
365
366 eq(str(td(1)), "1 day, 0:00:00")
367 eq(str(td(-1)), "-1 day, 0:00:00")
368 eq(str(td(2)), "2 days, 0:00:00")
369 eq(str(td(-2)), "-2 days, 0:00:00")
370
371 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
372 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
373 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
374 "-210 days, 23:12:34")
375
376 eq(str(td(milliseconds=1)), "0:00:00.001000")
377 eq(str(td(microseconds=3)), "0:00:00.000003")
378
379 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
380 microseconds=999999)),
381 "999999999 days, 23:59:59.999999")
382
383 def test_roundtrip(self):
384 for td in (timedelta(days=999999999, hours=23, minutes=59,
385 seconds=59, microseconds=999999),
386 timedelta(days=-999999999),
387 timedelta(days=1, seconds=2, microseconds=3)):
388
389 # Verify td -> string -> td identity.
390 s = repr(td)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000391 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000392 s = s[9:]
393 td2 = eval(s)
394 self.assertEqual(td, td2)
395
396 # Verify identity via reconstructing from pieces.
397 td2 = timedelta(td.days, td.seconds, td.microseconds)
398 self.assertEqual(td, td2)
399
400 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000401 self.assertIsInstance(timedelta.min, timedelta)
402 self.assertIsInstance(timedelta.max, timedelta)
403 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000404 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000405 self.assertEqual(timedelta.min, timedelta(-999999999))
406 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
407 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
408
409 def test_overflow(self):
410 tiny = timedelta.resolution
411
412 td = timedelta.min + tiny
413 td -= tiny # no problem
414 self.assertRaises(OverflowError, td.__sub__, tiny)
415 self.assertRaises(OverflowError, td.__add__, -tiny)
416
417 td = timedelta.max - tiny
418 td += tiny # no problem
419 self.assertRaises(OverflowError, td.__add__, tiny)
420 self.assertRaises(OverflowError, td.__sub__, -tiny)
421
422 self.assertRaises(OverflowError, lambda: -timedelta.max)
423
424 def test_microsecond_rounding(self):
425 td = timedelta
426 eq = self.assertEqual
427
428 # Single-field rounding.
429 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
430 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
431 eq(td(milliseconds=0.6/1000), td(microseconds=1))
432 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
433
434 # Rounding due to contributions from more than one field.
435 us_per_hour = 3600e6
436 us_per_day = us_per_hour * 24
437 eq(td(days=.4/us_per_day), td(0))
438 eq(td(hours=.2/us_per_hour), td(0))
439 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
440
441 eq(td(days=-.4/us_per_day), td(0))
442 eq(td(hours=-.2/us_per_hour), td(0))
443 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
444
445 def test_massive_normalization(self):
446 td = timedelta(microseconds=-1)
447 self.assertEqual((td.days, td.seconds, td.microseconds),
448 (-1, 24*3600-1, 999999))
449
450 def test_bool(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000451 self.assertTrue(timedelta(1))
452 self.assertTrue(timedelta(0, 1))
453 self.assertTrue(timedelta(0, 0, 1))
454 self.assertTrue(timedelta(microseconds=1))
455 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000456
Tim Petersb0c854d2003-05-17 15:57:00 +0000457 def test_subclass_timedelta(self):
458
459 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000460 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000461 def from_td(td):
462 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000463
464 def as_hours(self):
465 sum = (self.days * 24 +
466 self.seconds / 3600.0 +
467 self.microseconds / 3600e6)
468 return round(sum)
469
470 t1 = T(days=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000471 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000472 self.assertEqual(t1.as_hours(), 24)
473
474 t2 = T(days=-1, seconds=-3600)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000475 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000476 self.assertEqual(t2.as_hours(), -25)
477
478 t3 = t1 + t2
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000479 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000480 t4 = T.from_td(t3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000481 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000482 self.assertEqual(t3.days, t4.days)
483 self.assertEqual(t3.seconds, t4.seconds)
484 self.assertEqual(t3.microseconds, t4.microseconds)
485 self.assertEqual(str(t3), str(t4))
486 self.assertEqual(t4.as_hours(), -1)
487
Tim Peters2a799bf2002-12-16 20:18:38 +0000488#############################################################################
489# date tests
490
491class TestDateOnly(unittest.TestCase):
492 # Tests here won't pass if also run on datetime objects, so don't
493 # subclass this to test datetimes too.
494
495 def test_delta_non_days_ignored(self):
496 dt = date(2000, 1, 2)
497 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
498 microseconds=5)
499 days = timedelta(delta.days)
500 self.assertEqual(days, timedelta(1))
501
502 dt2 = dt + delta
503 self.assertEqual(dt2, dt + days)
504
505 dt2 = delta + dt
506 self.assertEqual(dt2, dt + days)
507
508 dt2 = dt - delta
509 self.assertEqual(dt2, dt - days)
510
511 delta = -delta
512 days = timedelta(delta.days)
513 self.assertEqual(days, timedelta(-2))
514
515 dt2 = dt + delta
516 self.assertEqual(dt2, dt + days)
517
518 dt2 = delta + dt
519 self.assertEqual(dt2, dt + days)
520
521 dt2 = dt - delta
522 self.assertEqual(dt2, dt - days)
523
Tim Peters604c0132004-06-07 23:04:33 +0000524class SubclassDate(date):
525 sub_var = 1
526
Collin Winterc2898c52007-04-25 17:29:52 +0000527class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000528 # Tests here should pass for both dates and datetimes, except for a
529 # few tests that TestDateTime overrides.
530
531 theclass = date
532
533 def test_basic_attributes(self):
534 dt = self.theclass(2002, 3, 1)
535 self.assertEqual(dt.year, 2002)
536 self.assertEqual(dt.month, 3)
537 self.assertEqual(dt.day, 1)
538
539 def test_roundtrip(self):
540 for dt in (self.theclass(1, 2, 3),
541 self.theclass.today()):
542 # Verify dt -> string -> date identity.
543 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000544 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000545 s = s[9:]
546 dt2 = eval(s)
547 self.assertEqual(dt, dt2)
548
549 # Verify identity via reconstructing from pieces.
550 dt2 = self.theclass(dt.year, dt.month, dt.day)
551 self.assertEqual(dt, dt2)
552
553 def test_ordinal_conversions(self):
554 # Check some fixed values.
555 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
556 (1, 12, 31, 365),
557 (2, 1, 1, 366),
558 # first example from "Calendrical Calculations"
559 (1945, 11, 12, 710347)]:
560 d = self.theclass(y, m, d)
561 self.assertEqual(n, d.toordinal())
562 fromord = self.theclass.fromordinal(n)
563 self.assertEqual(d, fromord)
564 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000565 # if we're checking something fancier than a date, verify
566 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000567 self.assertEqual(fromord.hour, 0)
568 self.assertEqual(fromord.minute, 0)
569 self.assertEqual(fromord.second, 0)
570 self.assertEqual(fromord.microsecond, 0)
571
Tim Peters0bf60bd2003-01-08 20:40:01 +0000572 # Check first and last days of year spottily across the whole
573 # range of years supported.
574 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000575 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
576 d = self.theclass(year, 1, 1)
577 n = d.toordinal()
578 d2 = self.theclass.fromordinal(n)
579 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000580 # Verify that moving back a day gets to the end of year-1.
581 if year > 1:
582 d = self.theclass.fromordinal(n-1)
583 d2 = self.theclass(year-1, 12, 31)
584 self.assertEqual(d, d2)
585 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000586
587 # Test every day in a leap-year and a non-leap year.
588 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
589 for year, isleap in (2000, True), (2002, False):
590 n = self.theclass(year, 1, 1).toordinal()
591 for month, maxday in zip(range(1, 13), dim):
592 if month == 2 and isleap:
593 maxday += 1
594 for day in range(1, maxday+1):
595 d = self.theclass(year, month, day)
596 self.assertEqual(d.toordinal(), n)
597 self.assertEqual(d, self.theclass.fromordinal(n))
598 n += 1
599
600 def test_extreme_ordinals(self):
601 a = self.theclass.min
602 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
603 aord = a.toordinal()
604 b = a.fromordinal(aord)
605 self.assertEqual(a, b)
606
607 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
608
609 b = a + timedelta(days=1)
610 self.assertEqual(b.toordinal(), aord + 1)
611 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
612
613 a = self.theclass.max
614 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
615 aord = a.toordinal()
616 b = a.fromordinal(aord)
617 self.assertEqual(a, b)
618
619 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
620
621 b = a - timedelta(days=1)
622 self.assertEqual(b.toordinal(), aord - 1)
623 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
624
625 def test_bad_constructor_arguments(self):
626 # bad years
627 self.theclass(MINYEAR, 1, 1) # no exception
628 self.theclass(MAXYEAR, 1, 1) # no exception
629 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
630 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
631 # bad months
632 self.theclass(2000, 1, 1) # no exception
633 self.theclass(2000, 12, 1) # no exception
634 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
635 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
636 # bad days
637 self.theclass(2000, 2, 29) # no exception
638 self.theclass(2004, 2, 29) # no exception
639 self.theclass(2400, 2, 29) # no exception
640 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
641 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
642 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
643 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
644 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
645 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
646
647 def test_hash_equality(self):
648 d = self.theclass(2000, 12, 31)
649 # same thing
650 e = self.theclass(2000, 12, 31)
651 self.assertEqual(d, e)
652 self.assertEqual(hash(d), hash(e))
653
654 dic = {d: 1}
655 dic[e] = 2
656 self.assertEqual(len(dic), 1)
657 self.assertEqual(dic[d], 2)
658 self.assertEqual(dic[e], 2)
659
660 d = self.theclass(2001, 1, 1)
661 # same thing
662 e = self.theclass(2001, 1, 1)
663 self.assertEqual(d, e)
664 self.assertEqual(hash(d), hash(e))
665
666 dic = {d: 1}
667 dic[e] = 2
668 self.assertEqual(len(dic), 1)
669 self.assertEqual(dic[d], 2)
670 self.assertEqual(dic[e], 2)
671
672 def test_computations(self):
673 a = self.theclass(2002, 1, 31)
674 b = self.theclass(1956, 1, 31)
675
676 diff = a-b
677 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
678 self.assertEqual(diff.seconds, 0)
679 self.assertEqual(diff.microseconds, 0)
680
681 day = timedelta(1)
682 week = timedelta(7)
683 a = self.theclass(2002, 3, 2)
684 self.assertEqual(a + day, self.theclass(2002, 3, 3))
685 self.assertEqual(day + a, self.theclass(2002, 3, 3))
686 self.assertEqual(a - day, self.theclass(2002, 3, 1))
687 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
688 self.assertEqual(a + week, self.theclass(2002, 3, 9))
689 self.assertEqual(a - week, self.theclass(2002, 2, 23))
690 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
691 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
692 self.assertEqual((a + week) - a, week)
693 self.assertEqual((a + day) - a, day)
694 self.assertEqual((a - week) - a, -week)
695 self.assertEqual((a - day) - a, -day)
696 self.assertEqual(a - (a + week), -week)
697 self.assertEqual(a - (a + day), -day)
698 self.assertEqual(a - (a - week), week)
699 self.assertEqual(a - (a - day), day)
700
701 # Add/sub ints, longs, floats should be illegal
702 for i in 1, 1L, 1.0:
703 self.assertRaises(TypeError, lambda: a+i)
704 self.assertRaises(TypeError, lambda: a-i)
705 self.assertRaises(TypeError, lambda: i+a)
706 self.assertRaises(TypeError, lambda: i-a)
707
708 # delta - date is senseless.
709 self.assertRaises(TypeError, lambda: day - a)
710 # mixing date and (delta or date) via * or // is senseless
711 self.assertRaises(TypeError, lambda: day * a)
712 self.assertRaises(TypeError, lambda: a * day)
713 self.assertRaises(TypeError, lambda: day // a)
714 self.assertRaises(TypeError, lambda: a // day)
715 self.assertRaises(TypeError, lambda: a * a)
716 self.assertRaises(TypeError, lambda: a // a)
717 # date + date is senseless
718 self.assertRaises(TypeError, lambda: a + a)
719
720 def test_overflow(self):
721 tiny = self.theclass.resolution
722
723 dt = self.theclass.min + tiny
724 dt -= tiny # no problem
725 self.assertRaises(OverflowError, dt.__sub__, tiny)
726 self.assertRaises(OverflowError, dt.__add__, -tiny)
727
728 dt = self.theclass.max - tiny
729 dt += tiny # no problem
730 self.assertRaises(OverflowError, dt.__add__, tiny)
731 self.assertRaises(OverflowError, dt.__sub__, -tiny)
732
733 def test_fromtimestamp(self):
734 import time
735
736 # Try an arbitrary fixed value.
737 year, month, day = 1999, 9, 19
738 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
739 d = self.theclass.fromtimestamp(ts)
740 self.assertEqual(d.year, year)
741 self.assertEqual(d.month, month)
742 self.assertEqual(d.day, day)
743
Tim Peters1b6f7a92004-06-20 02:50:16 +0000744 def test_insane_fromtimestamp(self):
745 # It's possible that some platform maps time_t to double,
746 # and that this test will fail there. This test should
747 # exempt such platforms (provided they return reasonable
748 # results!).
749 for insane in -1e200, 1e200:
750 self.assertRaises(ValueError, self.theclass.fromtimestamp,
751 insane)
752
Tim Peters2a799bf2002-12-16 20:18:38 +0000753 def test_today(self):
754 import time
755
756 # We claim that today() is like fromtimestamp(time.time()), so
757 # prove it.
758 for dummy in range(3):
759 today = self.theclass.today()
760 ts = time.time()
761 todayagain = self.theclass.fromtimestamp(ts)
762 if today == todayagain:
763 break
764 # There are several legit reasons that could fail:
765 # 1. It recently became midnight, between the today() and the
766 # time() calls.
767 # 2. The platform time() has such fine resolution that we'll
768 # never get the same value twice.
769 # 3. The platform time() has poor resolution, and we just
770 # happened to call today() right before a resolution quantum
771 # boundary.
772 # 4. The system clock got fiddled between calls.
773 # In any case, wait a little while and try again.
774 time.sleep(0.1)
775
776 # It worked or it didn't. If it didn't, assume it's reason #2, and
777 # let the test pass if they're within half a second of each other.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000778 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000779 abs(todayagain - today) < timedelta(seconds=0.5))
780
781 def test_weekday(self):
782 for i in range(7):
783 # March 4, 2002 is a Monday
784 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
785 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
786 # January 2, 1956 is a Monday
787 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
788 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
789
790 def test_isocalendar(self):
791 # Check examples from
792 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
793 for i in range(7):
794 d = self.theclass(2003, 12, 22+i)
795 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
796 d = self.theclass(2003, 12, 29) + timedelta(i)
797 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
798 d = self.theclass(2004, 1, 5+i)
799 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
800 d = self.theclass(2009, 12, 21+i)
801 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
802 d = self.theclass(2009, 12, 28) + timedelta(i)
803 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
804 d = self.theclass(2010, 1, 4+i)
805 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
806
807 def test_iso_long_years(self):
808 # Calculate long ISO years and compare to table from
809 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
810 ISO_LONG_YEARS_TABLE = """
811 4 32 60 88
812 9 37 65 93
813 15 43 71 99
814 20 48 76
815 26 54 82
816
817 105 133 161 189
818 111 139 167 195
819 116 144 172
820 122 150 178
821 128 156 184
822
823 201 229 257 285
824 207 235 263 291
825 212 240 268 296
826 218 246 274
827 224 252 280
828
829 303 331 359 387
830 308 336 364 392
831 314 342 370 398
832 320 348 376
833 325 353 381
834 """
835 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
836 iso_long_years.sort()
837 L = []
838 for i in range(400):
839 d = self.theclass(2000+i, 12, 31)
840 d1 = self.theclass(1600+i, 12, 31)
841 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
842 if d.isocalendar()[1] == 53:
843 L.append(i)
844 self.assertEqual(L, iso_long_years)
845
846 def test_isoformat(self):
847 t = self.theclass(2, 3, 2)
848 self.assertEqual(t.isoformat(), "0002-03-02")
849
850 def test_ctime(self):
851 t = self.theclass(2002, 3, 2)
852 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
853
854 def test_strftime(self):
855 t = self.theclass(2005, 3, 2)
856 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000857 self.assertEqual(t.strftime(""), "") # SF bug #761337
Georg Brandl4ddfcd32006-09-30 11:17:34 +0000858 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000859
860 self.assertRaises(TypeError, t.strftime) # needs an arg
861 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
862 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
863
Gregory P. Smith137d8242008-06-02 04:05:52 +0000864 # test that unicode input is allowed (issue 2782)
865 self.assertEqual(t.strftime(u"%m"), "03")
866
Tim Peters2a799bf2002-12-16 20:18:38 +0000867 # A naive object replaces %z and %Z w/ empty strings.
868 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
869
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000870 #make sure that invalid format specifiers are handled correctly
Kristján Valur Jónsson8adc0b52009-01-15 09:09:13 +0000871 #self.assertRaises(ValueError, t.strftime, "%e")
872 #self.assertRaises(ValueError, t.strftime, "%")
873 #self.assertRaises(ValueError, t.strftime, "%#")
874
875 #oh well, some systems just ignore those invalid ones.
876 #at least, excercise them to make sure that no crashes
877 #are generated
878 for f in ["%e", "%", "%#"]:
879 try:
880 t.strftime(f)
881 except ValueError:
882 pass
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000883
884 #check that this standard extension works
885 t.strftime("%f")
886
Gregory P. Smith137d8242008-06-02 04:05:52 +0000887
Eric Smitha9f7d622008-02-17 19:46:49 +0000888 def test_format(self):
889 dt = self.theclass(2007, 9, 10)
890 self.assertEqual(dt.__format__(''), str(dt))
891
892 # check that a derived class's __str__() gets called
893 class A(self.theclass):
894 def __str__(self):
895 return 'A'
896 a = A(2007, 9, 10)
897 self.assertEqual(a.__format__(''), 'A')
898
899 # check that a derived class's strftime gets called
900 class B(self.theclass):
901 def strftime(self, format_spec):
902 return 'B'
903 b = B(2007, 9, 10)
904 self.assertEqual(b.__format__(''), str(dt))
905
906 for fmt in ["m:%m d:%d y:%y",
907 "m:%m d:%d y:%y H:%H M:%M S:%S",
908 "%z %Z",
909 ]:
910 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
911 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
912 self.assertEqual(b.__format__(fmt), 'B')
913
Tim Peters2a799bf2002-12-16 20:18:38 +0000914 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000915 self.assertIsInstance(self.theclass.min, self.theclass)
916 self.assertIsInstance(self.theclass.max, self.theclass)
917 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000918 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000919
920 def test_extreme_timedelta(self):
921 big = self.theclass.max - self.theclass.min
922 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
923 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
924 # n == 315537897599999999 ~= 2**58.13
925 justasbig = timedelta(0, 0, n)
926 self.assertEqual(big, justasbig)
927 self.assertEqual(self.theclass.min + big, self.theclass.max)
928 self.assertEqual(self.theclass.max - big, self.theclass.min)
929
930 def test_timetuple(self):
931 for i in range(7):
932 # January 2, 1956 is a Monday (0)
933 d = self.theclass(1956, 1, 2+i)
934 t = d.timetuple()
935 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
936 # February 1, 1956 is a Wednesday (2)
937 d = self.theclass(1956, 2, 1+i)
938 t = d.timetuple()
939 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
940 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
941 # of the year.
942 d = self.theclass(1956, 3, 1+i)
943 t = d.timetuple()
944 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
945 self.assertEqual(t.tm_year, 1956)
946 self.assertEqual(t.tm_mon, 3)
947 self.assertEqual(t.tm_mday, 1+i)
948 self.assertEqual(t.tm_hour, 0)
949 self.assertEqual(t.tm_min, 0)
950 self.assertEqual(t.tm_sec, 0)
951 self.assertEqual(t.tm_wday, (3+i)%7)
952 self.assertEqual(t.tm_yday, 61+i)
953 self.assertEqual(t.tm_isdst, -1)
954
955 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000956 args = 6, 7, 23
957 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000958 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000959 green = pickler.dumps(orig, proto)
960 derived = unpickler.loads(green)
961 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000962
963 def test_compare(self):
964 t1 = self.theclass(2, 3, 4)
965 t2 = self.theclass(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000966 self.assertTrue(t1 == t2)
967 self.assertTrue(t1 <= t2)
968 self.assertTrue(t1 >= t2)
969 self.assertTrue(not t1 != t2)
970 self.assertTrue(not t1 < t2)
971 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000972 self.assertEqual(cmp(t1, t2), 0)
973 self.assertEqual(cmp(t2, t1), 0)
974
975 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
976 t2 = self.theclass(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000977 self.assertTrue(t1 < t2)
978 self.assertTrue(t2 > t1)
979 self.assertTrue(t1 <= t2)
980 self.assertTrue(t2 >= t1)
981 self.assertTrue(t1 != t2)
982 self.assertTrue(t2 != t1)
983 self.assertTrue(not t1 == t2)
984 self.assertTrue(not t2 == t1)
985 self.assertTrue(not t1 > t2)
986 self.assertTrue(not t2 < t1)
987 self.assertTrue(not t1 >= t2)
988 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000989 self.assertEqual(cmp(t1, t2), -1)
990 self.assertEqual(cmp(t2, t1), 1)
991
Tim Peters68124bb2003-02-08 03:46:31 +0000992 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000993 self.assertEqual(t1 == badarg, False)
994 self.assertEqual(t1 != badarg, True)
995 self.assertEqual(badarg == t1, False)
996 self.assertEqual(badarg != t1, True)
997
Tim Peters2a799bf2002-12-16 20:18:38 +0000998 self.assertRaises(TypeError, lambda: t1 < badarg)
999 self.assertRaises(TypeError, lambda: t1 > badarg)
1000 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001001 self.assertRaises(TypeError, lambda: badarg <= t1)
1002 self.assertRaises(TypeError, lambda: badarg < t1)
1003 self.assertRaises(TypeError, lambda: badarg > t1)
1004 self.assertRaises(TypeError, lambda: badarg >= t1)
1005
Tim Peters8d81a012003-01-24 22:36:34 +00001006 def test_mixed_compare(self):
1007 our = self.theclass(2000, 4, 5)
1008 self.assertRaises(TypeError, cmp, our, 1)
1009 self.assertRaises(TypeError, cmp, 1, our)
1010
1011 class AnotherDateTimeClass(object):
1012 def __cmp__(self, other):
1013 # Return "equal" so calling this can't be confused with
1014 # compare-by-address (which never says "equal" for distinct
1015 # objects).
1016 return 0
Nick Coghlan48361f52008-08-11 15:45:58 +00001017 __hash__ = None # Silence Py3k warning
Tim Peters8d81a012003-01-24 22:36:34 +00001018
1019 # This still errors, because date and datetime comparison raise
1020 # TypeError instead of NotImplemented when they don't know what to
1021 # do, in order to stop comparison from falling back to the default
1022 # compare-by-address.
1023 their = AnotherDateTimeClass()
1024 self.assertRaises(TypeError, cmp, our, their)
1025 # Oops: The next stab raises TypeError in the C implementation,
1026 # but not in the Python implementation of datetime. The difference
1027 # is due to that the Python implementation defines __cmp__ but
1028 # the C implementation defines tp_richcompare. This is more pain
1029 # to fix than it's worth, so commenting out the test.
1030 # self.assertEqual(cmp(their, our), 0)
1031
1032 # But date and datetime comparison return NotImplemented instead if the
1033 # other object has a timetuple attr. This gives the other object a
1034 # chance to do the comparison.
1035 class Comparable(AnotherDateTimeClass):
1036 def timetuple(self):
1037 return ()
1038
1039 their = Comparable()
1040 self.assertEqual(cmp(our, their), 0)
1041 self.assertEqual(cmp(their, our), 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001042 self.assertTrue(our == their)
1043 self.assertTrue(their == our)
Tim Peters8d81a012003-01-24 22:36:34 +00001044
Tim Peters2a799bf2002-12-16 20:18:38 +00001045 def test_bool(self):
1046 # All dates are considered true.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001047 self.assertTrue(self.theclass.min)
1048 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001049
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001050 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001051 # For nasty technical reasons, we can't handle years before 1900.
1052 cls = self.theclass
1053 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1054 for y in 1, 49, 51, 99, 100, 1000, 1899:
1055 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001056
1057 def test_replace(self):
1058 cls = self.theclass
1059 args = [1, 2, 3]
1060 base = cls(*args)
1061 self.assertEqual(base, base.replace())
1062
1063 i = 0
1064 for name, newval in (("year", 2),
1065 ("month", 3),
1066 ("day", 4)):
1067 newargs = args[:]
1068 newargs[i] = newval
1069 expected = cls(*newargs)
1070 got = base.replace(**{name: newval})
1071 self.assertEqual(expected, got)
1072 i += 1
1073
1074 # Out of bounds.
1075 base = cls(2000, 2, 29)
1076 self.assertRaises(ValueError, base.replace, year=2001)
1077
Tim Petersa98924a2003-05-17 05:55:19 +00001078 def test_subclass_date(self):
1079
1080 class C(self.theclass):
1081 theAnswer = 42
1082
1083 def __new__(cls, *args, **kws):
1084 temp = kws.copy()
1085 extra = temp.pop('extra')
1086 result = self.theclass.__new__(cls, *args, **temp)
1087 result.extra = extra
1088 return result
1089
1090 def newmeth(self, start):
1091 return start + self.year + self.month
1092
1093 args = 2003, 4, 14
1094
1095 dt1 = self.theclass(*args)
1096 dt2 = C(*args, **{'extra': 7})
1097
1098 self.assertEqual(dt2.__class__, C)
1099 self.assertEqual(dt2.theAnswer, 42)
1100 self.assertEqual(dt2.extra, 7)
1101 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1102 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1103
Tim Peters604c0132004-06-07 23:04:33 +00001104 def test_pickling_subclass_date(self):
1105
1106 args = 6, 7, 23
1107 orig = SubclassDate(*args)
1108 for pickler, unpickler, proto in pickle_choices:
1109 green = pickler.dumps(orig, proto)
1110 derived = unpickler.loads(green)
1111 self.assertEqual(orig, derived)
1112
Tim Peters3f606292004-03-21 23:38:41 +00001113 def test_backdoor_resistance(self):
1114 # For fast unpickling, the constructor accepts a pickle string.
1115 # This is a low-overhead backdoor. A user can (by intent or
1116 # mistake) pass a string directly, which (if it's the right length)
1117 # will get treated like a pickle, and bypass the normal sanity
1118 # checks in the constructor. This can create insane objects.
1119 # The constructor doesn't want to burn the time to validate all
1120 # fields, but does check the month field. This stops, e.g.,
1121 # datetime.datetime('1995-03-25') from yielding an insane object.
1122 base = '1995-03-25'
1123 if not issubclass(self.theclass, datetime):
1124 base = base[:4]
1125 for month_byte in '9', chr(0), chr(13), '\xff':
1126 self.assertRaises(TypeError, self.theclass,
1127 base[:2] + month_byte + base[3:])
1128 for ord_byte in range(1, 13):
1129 # This shouldn't blow up because of the month byte alone. If
1130 # the implementation changes to do more-careful checking, it may
1131 # blow up because other fields are insane.
1132 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001133
Tim Peters2a799bf2002-12-16 20:18:38 +00001134#############################################################################
1135# datetime tests
1136
Tim Peters604c0132004-06-07 23:04:33 +00001137class SubclassDatetime(datetime):
1138 sub_var = 1
1139
Tim Peters2a799bf2002-12-16 20:18:38 +00001140class TestDateTime(TestDate):
1141
1142 theclass = datetime
1143
1144 def test_basic_attributes(self):
1145 dt = self.theclass(2002, 3, 1, 12, 0)
1146 self.assertEqual(dt.year, 2002)
1147 self.assertEqual(dt.month, 3)
1148 self.assertEqual(dt.day, 1)
1149 self.assertEqual(dt.hour, 12)
1150 self.assertEqual(dt.minute, 0)
1151 self.assertEqual(dt.second, 0)
1152 self.assertEqual(dt.microsecond, 0)
1153
1154 def test_basic_attributes_nonzero(self):
1155 # Make sure all attributes are non-zero so bugs in
1156 # bit-shifting access show up.
1157 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1158 self.assertEqual(dt.year, 2002)
1159 self.assertEqual(dt.month, 3)
1160 self.assertEqual(dt.day, 1)
1161 self.assertEqual(dt.hour, 12)
1162 self.assertEqual(dt.minute, 59)
1163 self.assertEqual(dt.second, 59)
1164 self.assertEqual(dt.microsecond, 8000)
1165
1166 def test_roundtrip(self):
1167 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1168 self.theclass.now()):
1169 # Verify dt -> string -> datetime identity.
1170 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001171 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001172 s = s[9:]
1173 dt2 = eval(s)
1174 self.assertEqual(dt, dt2)
1175
1176 # Verify identity via reconstructing from pieces.
1177 dt2 = self.theclass(dt.year, dt.month, dt.day,
1178 dt.hour, dt.minute, dt.second,
1179 dt.microsecond)
1180 self.assertEqual(dt, dt2)
1181
1182 def test_isoformat(self):
1183 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1184 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1185 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1186 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arc8645a5c2009-12-29 22:03:38 +00001187 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001188 # str is ISO format with the separator forced to a blank.
1189 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1190
1191 t = self.theclass(2, 3, 2)
1192 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1193 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1194 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1195 # str is ISO format with the separator forced to a blank.
1196 self.assertEqual(str(t), "0002-03-02 00:00:00")
1197
Eric Smitha9f7d622008-02-17 19:46:49 +00001198 def test_format(self):
1199 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1200 self.assertEqual(dt.__format__(''), str(dt))
1201
1202 # check that a derived class's __str__() gets called
1203 class A(self.theclass):
1204 def __str__(self):
1205 return 'A'
1206 a = A(2007, 9, 10, 4, 5, 1, 123)
1207 self.assertEqual(a.__format__(''), 'A')
1208
1209 # check that a derived class's strftime gets called
1210 class B(self.theclass):
1211 def strftime(self, format_spec):
1212 return 'B'
1213 b = B(2007, 9, 10, 4, 5, 1, 123)
1214 self.assertEqual(b.__format__(''), str(dt))
1215
1216 for fmt in ["m:%m d:%d y:%y",
1217 "m:%m d:%d y:%y H:%H M:%M S:%S",
1218 "%z %Z",
1219 ]:
1220 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1221 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1222 self.assertEqual(b.__format__(fmt), 'B')
1223
Tim Peters2a799bf2002-12-16 20:18:38 +00001224 def test_more_ctime(self):
1225 # Test fields that TestDate doesn't touch.
1226 import time
1227
1228 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1229 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1230 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1231 # out. The difference is that t.ctime() produces " 2" for the day,
1232 # but platform ctime() produces "02" for the day. According to
1233 # C99, t.ctime() is correct here.
1234 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1235
1236 # So test a case where that difference doesn't matter.
1237 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1238 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1239
1240 def test_tz_independent_comparing(self):
1241 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1242 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1243 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1244 self.assertEqual(dt1, dt3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001245 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001246
1247 # Make sure comparison doesn't forget microseconds, and isn't done
1248 # via comparing a float timestamp (an IEEE double doesn't have enough
1249 # precision to span microsecond resolution across years 1 thru 9999,
1250 # so comparing via timestamp necessarily calls some distinct values
1251 # equal).
1252 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1253 us = timedelta(microseconds=1)
1254 dt2 = dt1 + us
1255 self.assertEqual(dt2 - dt1, us)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001256 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001257
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001258 def test_strftime_with_bad_tzname_replace(self):
1259 # verify ok if tzinfo.tzname().replace() returns a non-string
1260 class MyTzInfo(FixedOffset):
1261 def tzname(self, dt):
1262 class MyStr(str):
1263 def replace(self, *args):
1264 return None
1265 return MyStr('name')
1266 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1267 self.assertRaises(TypeError, t.strftime, '%Z')
1268
Tim Peters2a799bf2002-12-16 20:18:38 +00001269 def test_bad_constructor_arguments(self):
1270 # bad years
1271 self.theclass(MINYEAR, 1, 1) # no exception
1272 self.theclass(MAXYEAR, 1, 1) # no exception
1273 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1274 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1275 # bad months
1276 self.theclass(2000, 1, 1) # no exception
1277 self.theclass(2000, 12, 1) # no exception
1278 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1279 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1280 # bad days
1281 self.theclass(2000, 2, 29) # no exception
1282 self.theclass(2004, 2, 29) # no exception
1283 self.theclass(2400, 2, 29) # no exception
1284 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1285 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1286 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1287 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1288 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1289 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1290 # bad hours
1291 self.theclass(2000, 1, 31, 0) # no exception
1292 self.theclass(2000, 1, 31, 23) # no exception
1293 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1294 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1295 # bad minutes
1296 self.theclass(2000, 1, 31, 23, 0) # no exception
1297 self.theclass(2000, 1, 31, 23, 59) # no exception
1298 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1299 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1300 # bad seconds
1301 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1302 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1303 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1304 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1305 # bad microseconds
1306 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1307 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1308 self.assertRaises(ValueError, self.theclass,
1309 2000, 1, 31, 23, 59, 59, -1)
1310 self.assertRaises(ValueError, self.theclass,
1311 2000, 1, 31, 23, 59, 59,
1312 1000000)
1313
1314 def test_hash_equality(self):
1315 d = self.theclass(2000, 12, 31, 23, 30, 17)
1316 e = self.theclass(2000, 12, 31, 23, 30, 17)
1317 self.assertEqual(d, e)
1318 self.assertEqual(hash(d), hash(e))
1319
1320 dic = {d: 1}
1321 dic[e] = 2
1322 self.assertEqual(len(dic), 1)
1323 self.assertEqual(dic[d], 2)
1324 self.assertEqual(dic[e], 2)
1325
1326 d = self.theclass(2001, 1, 1, 0, 5, 17)
1327 e = self.theclass(2001, 1, 1, 0, 5, 17)
1328 self.assertEqual(d, e)
1329 self.assertEqual(hash(d), hash(e))
1330
1331 dic = {d: 1}
1332 dic[e] = 2
1333 self.assertEqual(len(dic), 1)
1334 self.assertEqual(dic[d], 2)
1335 self.assertEqual(dic[e], 2)
1336
1337 def test_computations(self):
1338 a = self.theclass(2002, 1, 31)
1339 b = self.theclass(1956, 1, 31)
1340 diff = a-b
1341 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1342 self.assertEqual(diff.seconds, 0)
1343 self.assertEqual(diff.microseconds, 0)
1344 a = self.theclass(2002, 3, 2, 17, 6)
1345 millisec = timedelta(0, 0, 1000)
1346 hour = timedelta(0, 3600)
1347 day = timedelta(1)
1348 week = timedelta(7)
1349 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1350 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1351 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1352 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1353 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1354 self.assertEqual(a - hour, a + -hour)
1355 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1356 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1357 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1358 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1359 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1360 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1361 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1362 self.assertEqual((a + week) - a, week)
1363 self.assertEqual((a + day) - a, day)
1364 self.assertEqual((a + hour) - a, hour)
1365 self.assertEqual((a + millisec) - a, millisec)
1366 self.assertEqual((a - week) - a, -week)
1367 self.assertEqual((a - day) - a, -day)
1368 self.assertEqual((a - hour) - a, -hour)
1369 self.assertEqual((a - millisec) - a, -millisec)
1370 self.assertEqual(a - (a + week), -week)
1371 self.assertEqual(a - (a + day), -day)
1372 self.assertEqual(a - (a + hour), -hour)
1373 self.assertEqual(a - (a + millisec), -millisec)
1374 self.assertEqual(a - (a - week), week)
1375 self.assertEqual(a - (a - day), day)
1376 self.assertEqual(a - (a - hour), hour)
1377 self.assertEqual(a - (a - millisec), millisec)
1378 self.assertEqual(a + (week + day + hour + millisec),
1379 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1380 self.assertEqual(a + (week + day + hour + millisec),
1381 (((a + week) + day) + hour) + millisec)
1382 self.assertEqual(a - (week + day + hour + millisec),
1383 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1384 self.assertEqual(a - (week + day + hour + millisec),
1385 (((a - week) - day) - hour) - millisec)
1386 # Add/sub ints, longs, floats should be illegal
1387 for i in 1, 1L, 1.0:
1388 self.assertRaises(TypeError, lambda: a+i)
1389 self.assertRaises(TypeError, lambda: a-i)
1390 self.assertRaises(TypeError, lambda: i+a)
1391 self.assertRaises(TypeError, lambda: i-a)
1392
1393 # delta - datetime is senseless.
1394 self.assertRaises(TypeError, lambda: day - a)
1395 # mixing datetime and (delta or datetime) via * or // is senseless
1396 self.assertRaises(TypeError, lambda: day * a)
1397 self.assertRaises(TypeError, lambda: a * day)
1398 self.assertRaises(TypeError, lambda: day // a)
1399 self.assertRaises(TypeError, lambda: a // day)
1400 self.assertRaises(TypeError, lambda: a * a)
1401 self.assertRaises(TypeError, lambda: a // a)
1402 # datetime + datetime is senseless
1403 self.assertRaises(TypeError, lambda: a + a)
1404
1405 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001406 args = 6, 7, 23, 20, 59, 1, 64**2
1407 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001408 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001409 green = pickler.dumps(orig, proto)
1410 derived = unpickler.loads(green)
1411 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001412
Guido van Rossum275666f2003-02-07 21:49:01 +00001413 def test_more_pickling(self):
1414 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1415 s = pickle.dumps(a)
1416 b = pickle.loads(s)
1417 self.assertEqual(b.year, 2003)
1418 self.assertEqual(b.month, 2)
1419 self.assertEqual(b.day, 7)
1420
Tim Peters604c0132004-06-07 23:04:33 +00001421 def test_pickling_subclass_datetime(self):
1422 args = 6, 7, 23, 20, 59, 1, 64**2
1423 orig = SubclassDatetime(*args)
1424 for pickler, unpickler, proto in pickle_choices:
1425 green = pickler.dumps(orig, proto)
1426 derived = unpickler.loads(green)
1427 self.assertEqual(orig, derived)
1428
Tim Peters2a799bf2002-12-16 20:18:38 +00001429 def test_more_compare(self):
1430 # The test_compare() inherited from TestDate covers the error cases.
1431 # We just want to test lexicographic ordering on the members datetime
1432 # has that date lacks.
1433 args = [2000, 11, 29, 20, 58, 16, 999998]
1434 t1 = self.theclass(*args)
1435 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001436 self.assertTrue(t1 == t2)
1437 self.assertTrue(t1 <= t2)
1438 self.assertTrue(t1 >= t2)
1439 self.assertTrue(not t1 != t2)
1440 self.assertTrue(not t1 < t2)
1441 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001442 self.assertEqual(cmp(t1, t2), 0)
1443 self.assertEqual(cmp(t2, t1), 0)
1444
1445 for i in range(len(args)):
1446 newargs = args[:]
1447 newargs[i] = args[i] + 1
1448 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001449 self.assertTrue(t1 < t2)
1450 self.assertTrue(t2 > t1)
1451 self.assertTrue(t1 <= t2)
1452 self.assertTrue(t2 >= t1)
1453 self.assertTrue(t1 != t2)
1454 self.assertTrue(t2 != t1)
1455 self.assertTrue(not t1 == t2)
1456 self.assertTrue(not t2 == t1)
1457 self.assertTrue(not t1 > t2)
1458 self.assertTrue(not t2 < t1)
1459 self.assertTrue(not t1 >= t2)
1460 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001461 self.assertEqual(cmp(t1, t2), -1)
1462 self.assertEqual(cmp(t2, t1), 1)
1463
1464
1465 # A helper for timestamp constructor tests.
1466 def verify_field_equality(self, expected, got):
1467 self.assertEqual(expected.tm_year, got.year)
1468 self.assertEqual(expected.tm_mon, got.month)
1469 self.assertEqual(expected.tm_mday, got.day)
1470 self.assertEqual(expected.tm_hour, got.hour)
1471 self.assertEqual(expected.tm_min, got.minute)
1472 self.assertEqual(expected.tm_sec, got.second)
1473
1474 def test_fromtimestamp(self):
1475 import time
1476
1477 ts = time.time()
1478 expected = time.localtime(ts)
1479 got = self.theclass.fromtimestamp(ts)
1480 self.verify_field_equality(expected, got)
1481
1482 def test_utcfromtimestamp(self):
1483 import time
1484
1485 ts = time.time()
1486 expected = time.gmtime(ts)
1487 got = self.theclass.utcfromtimestamp(ts)
1488 self.verify_field_equality(expected, got)
1489
Georg Brandl6d78a582006-04-28 19:09:24 +00001490 def test_microsecond_rounding(self):
1491 # Test whether fromtimestamp "rounds up" floats that are less
1492 # than one microsecond smaller than an integer.
1493 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1494 self.theclass.fromtimestamp(1))
1495
Tim Peters1b6f7a92004-06-20 02:50:16 +00001496 def test_insane_fromtimestamp(self):
1497 # It's possible that some platform maps time_t to double,
1498 # and that this test will fail there. This test should
1499 # exempt such platforms (provided they return reasonable
1500 # results!).
1501 for insane in -1e200, 1e200:
1502 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1503 insane)
1504
1505 def test_insane_utcfromtimestamp(self):
1506 # It's possible that some platform maps time_t to double,
1507 # and that this test will fail there. This test should
1508 # exempt such platforms (provided they return reasonable
1509 # results!).
1510 for insane in -1e200, 1e200:
1511 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1512 insane)
1513
Guido van Rossum2054ee92007-03-06 15:50:01 +00001514 def test_negative_float_fromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001515 # Windows doesn't accept negative timestamps
Alexander Belopolskya26cf462010-05-26 19:43:16 +00001516 if sys.platform == "win32":
Guido van Rossumf1200f82007-03-07 15:16:29 +00001517 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001518 # The result is tz-dependent; at least test that this doesn't
1519 # fail (like it did before bug 1646728 was fixed).
1520 self.theclass.fromtimestamp(-1.05)
1521
1522 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001523 # Windows doesn't accept negative timestamps
Alexander Belopolskya26cf462010-05-26 19:43:16 +00001524 if sys.platform == "win32":
Guido van Rossumf1200f82007-03-07 15:16:29 +00001525 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001526 d = self.theclass.utcfromtimestamp(-1.05)
1527 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1528
Tim Peters2a799bf2002-12-16 20:18:38 +00001529 def test_utcnow(self):
1530 import time
1531
1532 # Call it a success if utcnow() and utcfromtimestamp() are within
1533 # a second of each other.
1534 tolerance = timedelta(seconds=1)
1535 for dummy in range(3):
1536 from_now = self.theclass.utcnow()
1537 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1538 if abs(from_timestamp - from_now) <= tolerance:
1539 break
1540 # Else try again a few times.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001541 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001542
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001543 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001544 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001545
Skip Montanarofc070d22008-03-15 16:04:45 +00001546 string = '2004-12-01 13:02:47.197'
1547 format = '%Y-%m-%d %H:%M:%S.%f'
1548 result, frac = _strptime._strptime(string, format)
1549 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001550 got = self.theclass.strptime(string, format)
1551 self.assertEqual(expected, got)
1552
Tim Peters2a799bf2002-12-16 20:18:38 +00001553 def test_more_timetuple(self):
1554 # This tests fields beyond those tested by the TestDate.test_timetuple.
1555 t = self.theclass(2004, 12, 31, 6, 22, 33)
1556 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1557 self.assertEqual(t.timetuple(),
1558 (t.year, t.month, t.day,
1559 t.hour, t.minute, t.second,
1560 t.weekday(),
1561 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1562 -1))
1563 tt = t.timetuple()
1564 self.assertEqual(tt.tm_year, t.year)
1565 self.assertEqual(tt.tm_mon, t.month)
1566 self.assertEqual(tt.tm_mday, t.day)
1567 self.assertEqual(tt.tm_hour, t.hour)
1568 self.assertEqual(tt.tm_min, t.minute)
1569 self.assertEqual(tt.tm_sec, t.second)
1570 self.assertEqual(tt.tm_wday, t.weekday())
1571 self.assertEqual(tt.tm_yday, t.toordinal() -
1572 date(t.year, 1, 1).toordinal() + 1)
1573 self.assertEqual(tt.tm_isdst, -1)
1574
1575 def test_more_strftime(self):
1576 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001577 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1578 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1579 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001580
1581 def test_extract(self):
1582 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1583 self.assertEqual(dt.date(), date(2002, 3, 4))
1584 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1585
1586 def test_combine(self):
1587 d = date(2002, 3, 4)
1588 t = time(18, 45, 3, 1234)
1589 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1590 combine = self.theclass.combine
1591 dt = combine(d, t)
1592 self.assertEqual(dt, expected)
1593
1594 dt = combine(time=t, date=d)
1595 self.assertEqual(dt, expected)
1596
1597 self.assertEqual(d, dt.date())
1598 self.assertEqual(t, dt.time())
1599 self.assertEqual(dt, combine(dt.date(), dt.time()))
1600
1601 self.assertRaises(TypeError, combine) # need an arg
1602 self.assertRaises(TypeError, combine, d) # need two args
1603 self.assertRaises(TypeError, combine, t, d) # args reversed
1604 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1605 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1606
Tim Peters12bf3392002-12-24 05:41:27 +00001607 def test_replace(self):
1608 cls = self.theclass
1609 args = [1, 2, 3, 4, 5, 6, 7]
1610 base = cls(*args)
1611 self.assertEqual(base, base.replace())
1612
1613 i = 0
1614 for name, newval in (("year", 2),
1615 ("month", 3),
1616 ("day", 4),
1617 ("hour", 5),
1618 ("minute", 6),
1619 ("second", 7),
1620 ("microsecond", 8)):
1621 newargs = args[:]
1622 newargs[i] = newval
1623 expected = cls(*newargs)
1624 got = base.replace(**{name: newval})
1625 self.assertEqual(expected, got)
1626 i += 1
1627
1628 # Out of bounds.
1629 base = cls(2000, 2, 29)
1630 self.assertRaises(ValueError, base.replace, year=2001)
1631
Tim Peters80475bb2002-12-25 07:40:55 +00001632 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001633 # Pretty boring! The TZ test is more interesting here. astimezone()
1634 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001635 dt = self.theclass.now()
1636 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001637 self.assertRaises(TypeError, dt.astimezone) # not enough args
1638 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1639 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001640 self.assertRaises(ValueError, dt.astimezone, f) # naive
1641 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001642
Tim Peters52dcce22003-01-23 16:36:11 +00001643 class Bogus(tzinfo):
1644 def utcoffset(self, dt): return None
1645 def dst(self, dt): return timedelta(0)
1646 bog = Bogus()
1647 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1648
1649 class AlsoBogus(tzinfo):
1650 def utcoffset(self, dt): return timedelta(0)
1651 def dst(self, dt): return None
1652 alsobog = AlsoBogus()
1653 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001654
Tim Petersa98924a2003-05-17 05:55:19 +00001655 def test_subclass_datetime(self):
1656
1657 class C(self.theclass):
1658 theAnswer = 42
1659
1660 def __new__(cls, *args, **kws):
1661 temp = kws.copy()
1662 extra = temp.pop('extra')
1663 result = self.theclass.__new__(cls, *args, **temp)
1664 result.extra = extra
1665 return result
1666
1667 def newmeth(self, start):
1668 return start + self.year + self.month + self.second
1669
1670 args = 2003, 4, 14, 12, 13, 41
1671
1672 dt1 = self.theclass(*args)
1673 dt2 = C(*args, **{'extra': 7})
1674
1675 self.assertEqual(dt2.__class__, C)
1676 self.assertEqual(dt2.theAnswer, 42)
1677 self.assertEqual(dt2.extra, 7)
1678 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1679 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1680 dt1.second - 7)
1681
Tim Peters604c0132004-06-07 23:04:33 +00001682class SubclassTime(time):
1683 sub_var = 1
1684
Collin Winterc2898c52007-04-25 17:29:52 +00001685class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001686
1687 theclass = time
1688
1689 def test_basic_attributes(self):
1690 t = self.theclass(12, 0)
1691 self.assertEqual(t.hour, 12)
1692 self.assertEqual(t.minute, 0)
1693 self.assertEqual(t.second, 0)
1694 self.assertEqual(t.microsecond, 0)
1695
1696 def test_basic_attributes_nonzero(self):
1697 # Make sure all attributes are non-zero so bugs in
1698 # bit-shifting access show up.
1699 t = self.theclass(12, 59, 59, 8000)
1700 self.assertEqual(t.hour, 12)
1701 self.assertEqual(t.minute, 59)
1702 self.assertEqual(t.second, 59)
1703 self.assertEqual(t.microsecond, 8000)
1704
1705 def test_roundtrip(self):
1706 t = self.theclass(1, 2, 3, 4)
1707
1708 # Verify t -> string -> time identity.
1709 s = repr(t)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001710 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001711 s = s[9:]
1712 t2 = eval(s)
1713 self.assertEqual(t, t2)
1714
1715 # Verify identity via reconstructing from pieces.
1716 t2 = self.theclass(t.hour, t.minute, t.second,
1717 t.microsecond)
1718 self.assertEqual(t, t2)
1719
1720 def test_comparing(self):
1721 args = [1, 2, 3, 4]
1722 t1 = self.theclass(*args)
1723 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001724 self.assertTrue(t1 == t2)
1725 self.assertTrue(t1 <= t2)
1726 self.assertTrue(t1 >= t2)
1727 self.assertTrue(not t1 != t2)
1728 self.assertTrue(not t1 < t2)
1729 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001730 self.assertEqual(cmp(t1, t2), 0)
1731 self.assertEqual(cmp(t2, t1), 0)
1732
1733 for i in range(len(args)):
1734 newargs = args[:]
1735 newargs[i] = args[i] + 1
1736 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001737 self.assertTrue(t1 < t2)
1738 self.assertTrue(t2 > t1)
1739 self.assertTrue(t1 <= t2)
1740 self.assertTrue(t2 >= t1)
1741 self.assertTrue(t1 != t2)
1742 self.assertTrue(t2 != t1)
1743 self.assertTrue(not t1 == t2)
1744 self.assertTrue(not t2 == t1)
1745 self.assertTrue(not t1 > t2)
1746 self.assertTrue(not t2 < t1)
1747 self.assertTrue(not t1 >= t2)
1748 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001749 self.assertEqual(cmp(t1, t2), -1)
1750 self.assertEqual(cmp(t2, t1), 1)
1751
Tim Peters68124bb2003-02-08 03:46:31 +00001752 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001753 self.assertEqual(t1 == badarg, False)
1754 self.assertEqual(t1 != badarg, True)
1755 self.assertEqual(badarg == t1, False)
1756 self.assertEqual(badarg != t1, True)
1757
Tim Peters2a799bf2002-12-16 20:18:38 +00001758 self.assertRaises(TypeError, lambda: t1 <= badarg)
1759 self.assertRaises(TypeError, lambda: t1 < badarg)
1760 self.assertRaises(TypeError, lambda: t1 > badarg)
1761 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001762 self.assertRaises(TypeError, lambda: badarg <= t1)
1763 self.assertRaises(TypeError, lambda: badarg < t1)
1764 self.assertRaises(TypeError, lambda: badarg > t1)
1765 self.assertRaises(TypeError, lambda: badarg >= t1)
1766
1767 def test_bad_constructor_arguments(self):
1768 # bad hours
1769 self.theclass(0, 0) # no exception
1770 self.theclass(23, 0) # no exception
1771 self.assertRaises(ValueError, self.theclass, -1, 0)
1772 self.assertRaises(ValueError, self.theclass, 24, 0)
1773 # bad minutes
1774 self.theclass(23, 0) # no exception
1775 self.theclass(23, 59) # no exception
1776 self.assertRaises(ValueError, self.theclass, 23, -1)
1777 self.assertRaises(ValueError, self.theclass, 23, 60)
1778 # bad seconds
1779 self.theclass(23, 59, 0) # no exception
1780 self.theclass(23, 59, 59) # no exception
1781 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1782 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1783 # bad microseconds
1784 self.theclass(23, 59, 59, 0) # no exception
1785 self.theclass(23, 59, 59, 999999) # no exception
1786 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1787 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1788
1789 def test_hash_equality(self):
1790 d = self.theclass(23, 30, 17)
1791 e = self.theclass(23, 30, 17)
1792 self.assertEqual(d, e)
1793 self.assertEqual(hash(d), hash(e))
1794
1795 dic = {d: 1}
1796 dic[e] = 2
1797 self.assertEqual(len(dic), 1)
1798 self.assertEqual(dic[d], 2)
1799 self.assertEqual(dic[e], 2)
1800
1801 d = self.theclass(0, 5, 17)
1802 e = self.theclass(0, 5, 17)
1803 self.assertEqual(d, e)
1804 self.assertEqual(hash(d), hash(e))
1805
1806 dic = {d: 1}
1807 dic[e] = 2
1808 self.assertEqual(len(dic), 1)
1809 self.assertEqual(dic[d], 2)
1810 self.assertEqual(dic[e], 2)
1811
1812 def test_isoformat(self):
1813 t = self.theclass(4, 5, 1, 123)
1814 self.assertEqual(t.isoformat(), "04:05:01.000123")
1815 self.assertEqual(t.isoformat(), str(t))
1816
1817 t = self.theclass()
1818 self.assertEqual(t.isoformat(), "00:00:00")
1819 self.assertEqual(t.isoformat(), str(t))
1820
1821 t = self.theclass(microsecond=1)
1822 self.assertEqual(t.isoformat(), "00:00:00.000001")
1823 self.assertEqual(t.isoformat(), str(t))
1824
1825 t = self.theclass(microsecond=10)
1826 self.assertEqual(t.isoformat(), "00:00:00.000010")
1827 self.assertEqual(t.isoformat(), str(t))
1828
1829 t = self.theclass(microsecond=100)
1830 self.assertEqual(t.isoformat(), "00:00:00.000100")
1831 self.assertEqual(t.isoformat(), str(t))
1832
1833 t = self.theclass(microsecond=1000)
1834 self.assertEqual(t.isoformat(), "00:00:00.001000")
1835 self.assertEqual(t.isoformat(), str(t))
1836
1837 t = self.theclass(microsecond=10000)
1838 self.assertEqual(t.isoformat(), "00:00:00.010000")
1839 self.assertEqual(t.isoformat(), str(t))
1840
1841 t = self.theclass(microsecond=100000)
1842 self.assertEqual(t.isoformat(), "00:00:00.100000")
1843 self.assertEqual(t.isoformat(), str(t))
1844
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001845 def test_1653736(self):
1846 # verify it doesn't accept extra keyword arguments
1847 t = self.theclass(second=1)
1848 self.assertRaises(TypeError, t.isoformat, foo=3)
1849
Tim Peters2a799bf2002-12-16 20:18:38 +00001850 def test_strftime(self):
1851 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001852 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001853 # A naive object replaces %z and %Z with empty strings.
1854 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1855
Eric Smitha9f7d622008-02-17 19:46:49 +00001856 def test_format(self):
1857 t = self.theclass(1, 2, 3, 4)
1858 self.assertEqual(t.__format__(''), str(t))
1859
1860 # check that a derived class's __str__() gets called
1861 class A(self.theclass):
1862 def __str__(self):
1863 return 'A'
1864 a = A(1, 2, 3, 4)
1865 self.assertEqual(a.__format__(''), 'A')
1866
1867 # check that a derived class's strftime gets called
1868 class B(self.theclass):
1869 def strftime(self, format_spec):
1870 return 'B'
1871 b = B(1, 2, 3, 4)
1872 self.assertEqual(b.__format__(''), str(t))
1873
1874 for fmt in ['%H %M %S',
1875 ]:
1876 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1877 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1878 self.assertEqual(b.__format__(fmt), 'B')
1879
Tim Peters2a799bf2002-12-16 20:18:38 +00001880 def test_str(self):
1881 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1882 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1883 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1884 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1885 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1886
1887 def test_repr(self):
1888 name = 'datetime.' + self.theclass.__name__
1889 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1890 "%s(1, 2, 3, 4)" % name)
1891 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1892 "%s(10, 2, 3, 4000)" % name)
1893 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1894 "%s(0, 2, 3, 400000)" % name)
1895 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1896 "%s(12, 2, 3)" % name)
1897 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1898 "%s(23, 15)" % name)
1899
1900 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +00001901 self.assertIsInstance(self.theclass.min, self.theclass)
1902 self.assertIsInstance(self.theclass.max, self.theclass)
1903 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001904 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001905
1906 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001907 args = 20, 59, 16, 64**2
1908 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001909 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001910 green = pickler.dumps(orig, proto)
1911 derived = unpickler.loads(green)
1912 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001913
Tim Peters604c0132004-06-07 23:04:33 +00001914 def test_pickling_subclass_time(self):
1915 args = 20, 59, 16, 64**2
1916 orig = SubclassTime(*args)
1917 for pickler, unpickler, proto in pickle_choices:
1918 green = pickler.dumps(orig, proto)
1919 derived = unpickler.loads(green)
1920 self.assertEqual(orig, derived)
1921
Tim Peters2a799bf2002-12-16 20:18:38 +00001922 def test_bool(self):
1923 cls = self.theclass
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001924 self.assertTrue(cls(1))
1925 self.assertTrue(cls(0, 1))
1926 self.assertTrue(cls(0, 0, 1))
1927 self.assertTrue(cls(0, 0, 0, 1))
1928 self.assertTrue(not cls(0))
1929 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001930
Tim Peters12bf3392002-12-24 05:41:27 +00001931 def test_replace(self):
1932 cls = self.theclass
1933 args = [1, 2, 3, 4]
1934 base = cls(*args)
1935 self.assertEqual(base, base.replace())
1936
1937 i = 0
1938 for name, newval in (("hour", 5),
1939 ("minute", 6),
1940 ("second", 7),
1941 ("microsecond", 8)):
1942 newargs = args[:]
1943 newargs[i] = newval
1944 expected = cls(*newargs)
1945 got = base.replace(**{name: newval})
1946 self.assertEqual(expected, got)
1947 i += 1
1948
1949 # Out of bounds.
1950 base = cls(1)
1951 self.assertRaises(ValueError, base.replace, hour=24)
1952 self.assertRaises(ValueError, base.replace, minute=-1)
1953 self.assertRaises(ValueError, base.replace, second=100)
1954 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1955
Tim Petersa98924a2003-05-17 05:55:19 +00001956 def test_subclass_time(self):
1957
1958 class C(self.theclass):
1959 theAnswer = 42
1960
1961 def __new__(cls, *args, **kws):
1962 temp = kws.copy()
1963 extra = temp.pop('extra')
1964 result = self.theclass.__new__(cls, *args, **temp)
1965 result.extra = extra
1966 return result
1967
1968 def newmeth(self, start):
1969 return start + self.hour + self.second
1970
1971 args = 4, 5, 6
1972
1973 dt1 = self.theclass(*args)
1974 dt2 = C(*args, **{'extra': 7})
1975
1976 self.assertEqual(dt2.__class__, C)
1977 self.assertEqual(dt2.theAnswer, 42)
1978 self.assertEqual(dt2.extra, 7)
1979 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1980 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1981
Armin Rigof4afb212005-11-07 07:15:48 +00001982 def test_backdoor_resistance(self):
1983 # see TestDate.test_backdoor_resistance().
1984 base = '2:59.0'
1985 for hour_byte in ' ', '9', chr(24), '\xff':
1986 self.assertRaises(TypeError, self.theclass,
1987 hour_byte + base[1:])
1988
Tim Peters855fe882002-12-22 03:43:39 +00001989# A mixin for classes with a tzinfo= argument. Subclasses must define
1990# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001991# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001992class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001993
Tim Petersbad8ff02002-12-30 20:52:32 +00001994 def test_argument_passing(self):
1995 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001996 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001997 class introspective(tzinfo):
1998 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001999 def utcoffset(self, dt):
2000 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002001 dst = utcoffset
2002
2003 obj = cls(1, 2, 3, tzinfo=introspective())
2004
Tim Peters0bf60bd2003-01-08 20:40:01 +00002005 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002006 self.assertEqual(obj.tzname(), expected)
2007
Tim Peters0bf60bd2003-01-08 20:40:01 +00002008 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002009 self.assertEqual(obj.utcoffset(), expected)
2010 self.assertEqual(obj.dst(), expected)
2011
Tim Peters855fe882002-12-22 03:43:39 +00002012 def test_bad_tzinfo_classes(self):
2013 cls = self.theclass
2014 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002015
Tim Peters855fe882002-12-22 03:43:39 +00002016 class NiceTry(object):
2017 def __init__(self): pass
2018 def utcoffset(self, dt): pass
2019 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2020
2021 class BetterTry(tzinfo):
2022 def __init__(self): pass
2023 def utcoffset(self, dt): pass
2024 b = BetterTry()
2025 t = cls(1, 1, 1, tzinfo=b)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002026 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002027
2028 def test_utc_offset_out_of_bounds(self):
2029 class Edgy(tzinfo):
2030 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002031 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002032 def utcoffset(self, dt):
2033 return self.offset
2034
2035 cls = self.theclass
2036 for offset, legit in ((-1440, False),
2037 (-1439, True),
2038 (1439, True),
2039 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002040 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002041 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002042 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002043 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002044 else:
2045 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002046 if legit:
2047 aofs = abs(offset)
2048 h, m = divmod(aofs, 60)
2049 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002050 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002051 t = t.timetz()
2052 self.assertEqual(str(t), "01:02:03" + tag)
2053 else:
2054 self.assertRaises(ValueError, str, t)
2055
2056 def test_tzinfo_classes(self):
2057 cls = self.theclass
2058 class C1(tzinfo):
2059 def utcoffset(self, dt): return None
2060 def dst(self, dt): return None
2061 def tzname(self, dt): return None
2062 for t in (cls(1, 1, 1),
2063 cls(1, 1, 1, tzinfo=None),
2064 cls(1, 1, 1, tzinfo=C1())):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002065 self.assertTrue(t.utcoffset() is None)
2066 self.assertTrue(t.dst() is None)
2067 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002068
Tim Peters855fe882002-12-22 03:43:39 +00002069 class C3(tzinfo):
2070 def utcoffset(self, dt): return timedelta(minutes=-1439)
2071 def dst(self, dt): return timedelta(minutes=1439)
2072 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002073 t = cls(1, 1, 1, tzinfo=C3())
2074 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2075 self.assertEqual(t.dst(), timedelta(minutes=1439))
2076 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002077
2078 # Wrong types.
2079 class C4(tzinfo):
2080 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002081 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002082 def tzname(self, dt): return 0
2083 t = cls(1, 1, 1, tzinfo=C4())
2084 self.assertRaises(TypeError, t.utcoffset)
2085 self.assertRaises(TypeError, t.dst)
2086 self.assertRaises(TypeError, t.tzname)
2087
2088 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002089 class C6(tzinfo):
2090 def utcoffset(self, dt): return timedelta(hours=-24)
2091 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002092 t = cls(1, 1, 1, tzinfo=C6())
2093 self.assertRaises(ValueError, t.utcoffset)
2094 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002095
2096 # Not a whole number of minutes.
2097 class C7(tzinfo):
2098 def utcoffset(self, dt): return timedelta(seconds=61)
2099 def dst(self, dt): return timedelta(microseconds=-81)
2100 t = cls(1, 1, 1, tzinfo=C7())
2101 self.assertRaises(ValueError, t.utcoffset)
2102 self.assertRaises(ValueError, t.dst)
2103
Tim Peters4c0db782002-12-26 05:01:19 +00002104 def test_aware_compare(self):
2105 cls = self.theclass
2106
Tim Peters60c76e42002-12-27 00:41:11 +00002107 # Ensure that utcoffset() gets ignored if the comparands have
2108 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002109 class OperandDependentOffset(tzinfo):
2110 def utcoffset(self, t):
2111 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002112 # d0 and d1 equal after adjustment
2113 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002114 else:
Tim Peters397301e2003-01-02 21:28:08 +00002115 # d2 off in the weeds
2116 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002117
2118 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2119 d0 = base.replace(minute=3)
2120 d1 = base.replace(minute=9)
2121 d2 = base.replace(minute=11)
2122 for x in d0, d1, d2:
2123 for y in d0, d1, d2:
2124 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002125 expected = cmp(x.minute, y.minute)
2126 self.assertEqual(got, expected)
2127
2128 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002129 # Note that a time can't actually have an operand-depedent offset,
2130 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2131 # so skip this test for time.
2132 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002133 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2134 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2135 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2136 for x in d0, d1, d2:
2137 for y in d0, d1, d2:
2138 got = cmp(x, y)
2139 if (x is d0 or x is d1) and (y is d0 or y is d1):
2140 expected = 0
2141 elif x is y is d2:
2142 expected = 0
2143 elif x is d2:
2144 expected = -1
2145 else:
2146 assert y is d2
2147 expected = 1
2148 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002149
Tim Peters855fe882002-12-22 03:43:39 +00002150
Tim Peters0bf60bd2003-01-08 20:40:01 +00002151# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002152class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002153 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002154
2155 def test_empty(self):
2156 t = self.theclass()
2157 self.assertEqual(t.hour, 0)
2158 self.assertEqual(t.minute, 0)
2159 self.assertEqual(t.second, 0)
2160 self.assertEqual(t.microsecond, 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002161 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002162
Tim Peters2a799bf2002-12-16 20:18:38 +00002163 def test_zones(self):
2164 est = FixedOffset(-300, "EST", 1)
2165 utc = FixedOffset(0, "UTC", -2)
2166 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002167 t1 = time( 7, 47, tzinfo=est)
2168 t2 = time(12, 47, tzinfo=utc)
2169 t3 = time(13, 47, tzinfo=met)
2170 t4 = time(microsecond=40)
2171 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002172
2173 self.assertEqual(t1.tzinfo, est)
2174 self.assertEqual(t2.tzinfo, utc)
2175 self.assertEqual(t3.tzinfo, met)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002176 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002177 self.assertEqual(t5.tzinfo, utc)
2178
Tim Peters855fe882002-12-22 03:43:39 +00002179 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2180 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2181 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002182 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002183 self.assertRaises(TypeError, t1.utcoffset, "no args")
2184
2185 self.assertEqual(t1.tzname(), "EST")
2186 self.assertEqual(t2.tzname(), "UTC")
2187 self.assertEqual(t3.tzname(), "MET")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002188 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002189 self.assertRaises(TypeError, t1.tzname, "no args")
2190
Tim Peters855fe882002-12-22 03:43:39 +00002191 self.assertEqual(t1.dst(), timedelta(minutes=1))
2192 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2193 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002194 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002195 self.assertRaises(TypeError, t1.dst, "no args")
2196
2197 self.assertEqual(hash(t1), hash(t2))
2198 self.assertEqual(hash(t1), hash(t3))
2199 self.assertEqual(hash(t2), hash(t3))
2200
2201 self.assertEqual(t1, t2)
2202 self.assertEqual(t1, t3)
2203 self.assertEqual(t2, t3)
2204 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2205 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2206 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2207
2208 self.assertEqual(str(t1), "07:47:00-05:00")
2209 self.assertEqual(str(t2), "12:47:00+00:00")
2210 self.assertEqual(str(t3), "13:47:00+01:00")
2211 self.assertEqual(str(t4), "00:00:00.000040")
2212 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2213
2214 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2215 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2216 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2217 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2218 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2219
Tim Peters0bf60bd2003-01-08 20:40:01 +00002220 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002221 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2222 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2223 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2224 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2225 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2226
2227 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2228 "07:47:00 %Z=EST %z=-0500")
2229 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2230 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2231
2232 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002233 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002234 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2235 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2236
Tim Petersb92bb712002-12-21 17:44:07 +00002237 # Check that an invalid tzname result raises an exception.
2238 class Badtzname(tzinfo):
2239 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002240 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002241 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2242 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002243
2244 def test_hash_edge_cases(self):
2245 # Offsets that overflow a basic time.
2246 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2247 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2248 self.assertEqual(hash(t1), hash(t2))
2249
2250 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2251 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2252 self.assertEqual(hash(t1), hash(t2))
2253
Tim Peters2a799bf2002-12-16 20:18:38 +00002254 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002255 # Try one without a tzinfo.
2256 args = 20, 59, 16, 64**2
2257 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002258 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002259 green = pickler.dumps(orig, proto)
2260 derived = unpickler.loads(green)
2261 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002262
2263 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002264 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002265 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002266 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002267 green = pickler.dumps(orig, proto)
2268 derived = unpickler.loads(green)
2269 self.assertEqual(orig, derived)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002270 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002271 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2272 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002273
2274 def test_more_bool(self):
2275 # Test cases with non-None tzinfo.
2276 cls = self.theclass
2277
2278 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002279 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002280
2281 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002282 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002283
2284 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002285 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002286
2287 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002288 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002289
2290 # Mostly ensuring this doesn't overflow internally.
2291 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002292 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002293
2294 # But this should yield a value error -- the utcoffset is bogus.
2295 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2296 self.assertRaises(ValueError, lambda: bool(t))
2297
2298 # Likewise.
2299 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2300 self.assertRaises(ValueError, lambda: bool(t))
2301
Tim Peters12bf3392002-12-24 05:41:27 +00002302 def test_replace(self):
2303 cls = self.theclass
2304 z100 = FixedOffset(100, "+100")
2305 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2306 args = [1, 2, 3, 4, z100]
2307 base = cls(*args)
2308 self.assertEqual(base, base.replace())
2309
2310 i = 0
2311 for name, newval in (("hour", 5),
2312 ("minute", 6),
2313 ("second", 7),
2314 ("microsecond", 8),
2315 ("tzinfo", zm200)):
2316 newargs = args[:]
2317 newargs[i] = newval
2318 expected = cls(*newargs)
2319 got = base.replace(**{name: newval})
2320 self.assertEqual(expected, got)
2321 i += 1
2322
2323 # Ensure we can get rid of a tzinfo.
2324 self.assertEqual(base.tzname(), "+100")
2325 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002326 self.assertTrue(base2.tzinfo is None)
2327 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002328
2329 # Ensure we can add one.
2330 base3 = base2.replace(tzinfo=z100)
2331 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002332 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002333
2334 # Out of bounds.
2335 base = cls(1)
2336 self.assertRaises(ValueError, base.replace, hour=24)
2337 self.assertRaises(ValueError, base.replace, minute=-1)
2338 self.assertRaises(ValueError, base.replace, second=100)
2339 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2340
Tim Peters60c76e42002-12-27 00:41:11 +00002341 def test_mixed_compare(self):
2342 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002343 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002344 self.assertEqual(t1, t2)
2345 t2 = t2.replace(tzinfo=None)
2346 self.assertEqual(t1, t2)
2347 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2348 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002349 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2350 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002351
Tim Peters0bf60bd2003-01-08 20:40:01 +00002352 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002353 class Varies(tzinfo):
2354 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002355 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002356 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002357 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002358 return self.offset
2359
2360 v = Varies()
2361 t1 = t2.replace(tzinfo=v)
2362 t2 = t2.replace(tzinfo=v)
2363 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2364 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2365 self.assertEqual(t1, t2)
2366
2367 # But if they're not identical, it isn't ignored.
2368 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002369 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002370
Tim Petersa98924a2003-05-17 05:55:19 +00002371 def test_subclass_timetz(self):
2372
2373 class C(self.theclass):
2374 theAnswer = 42
2375
2376 def __new__(cls, *args, **kws):
2377 temp = kws.copy()
2378 extra = temp.pop('extra')
2379 result = self.theclass.__new__(cls, *args, **temp)
2380 result.extra = extra
2381 return result
2382
2383 def newmeth(self, start):
2384 return start + self.hour + self.second
2385
2386 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2387
2388 dt1 = self.theclass(*args)
2389 dt2 = C(*args, **{'extra': 7})
2390
2391 self.assertEqual(dt2.__class__, C)
2392 self.assertEqual(dt2.theAnswer, 42)
2393 self.assertEqual(dt2.extra, 7)
2394 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2395 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2396
Tim Peters4c0db782002-12-26 05:01:19 +00002397
Tim Peters0bf60bd2003-01-08 20:40:01 +00002398# Testing datetime objects with a non-None tzinfo.
2399
Collin Winterc2898c52007-04-25 17:29:52 +00002400class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002401 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002402
2403 def test_trivial(self):
2404 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2405 self.assertEqual(dt.year, 1)
2406 self.assertEqual(dt.month, 2)
2407 self.assertEqual(dt.day, 3)
2408 self.assertEqual(dt.hour, 4)
2409 self.assertEqual(dt.minute, 5)
2410 self.assertEqual(dt.second, 6)
2411 self.assertEqual(dt.microsecond, 7)
2412 self.assertEqual(dt.tzinfo, None)
2413
2414 def test_even_more_compare(self):
2415 # The test_compare() and test_more_compare() inherited from TestDate
2416 # and TestDateTime covered non-tzinfo cases.
2417
2418 # Smallest possible after UTC adjustment.
2419 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2420 # Largest possible after UTC adjustment.
2421 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2422 tzinfo=FixedOffset(-1439, ""))
2423
2424 # Make sure those compare correctly, and w/o overflow.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002425 self.assertTrue(t1 < t2)
2426 self.assertTrue(t1 != t2)
2427 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002428
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002429 self.assertTrue(t1 == t1)
2430 self.assertTrue(t2 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002431
2432 # Equal afer adjustment.
2433 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2434 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2435 self.assertEqual(t1, t2)
2436
2437 # Change t1 not to subtract a minute, and t1 should be larger.
2438 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002439 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002440
2441 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2442 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002443 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002444
2445 # Back to the original t1, but make seconds resolve it.
2446 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2447 second=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002448 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002449
2450 # Likewise, but make microseconds resolve it.
2451 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2452 microsecond=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002453 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002454
2455 # Make t2 naive and it should fail.
2456 t2 = self.theclass.min
2457 self.assertRaises(TypeError, lambda: t1 == t2)
2458 self.assertEqual(t2, t2)
2459
2460 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2461 class Naive(tzinfo):
2462 def utcoffset(self, dt): return None
2463 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2464 self.assertRaises(TypeError, lambda: t1 == t2)
2465 self.assertEqual(t2, t2)
2466
2467 # OTOH, it's OK to compare two of these mixing the two ways of being
2468 # naive.
2469 t1 = self.theclass(5, 6, 7)
2470 self.assertEqual(t1, t2)
2471
2472 # Try a bogus uctoffset.
2473 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002474 def utcoffset(self, dt):
2475 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002476 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2477 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002478 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002479
Tim Peters2a799bf2002-12-16 20:18:38 +00002480 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002481 # Try one without a tzinfo.
2482 args = 6, 7, 23, 20, 59, 1, 64**2
2483 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002484 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002485 green = pickler.dumps(orig, proto)
2486 derived = unpickler.loads(green)
2487 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002488
2489 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002490 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002491 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002492 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002493 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002494 green = pickler.dumps(orig, proto)
2495 derived = unpickler.loads(green)
2496 self.assertEqual(orig, derived)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002497 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002498 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2499 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002500
2501 def test_extreme_hashes(self):
2502 # If an attempt is made to hash these via subtracting the offset
2503 # then hashing a datetime object, OverflowError results. The
2504 # Python implementation used to blow up here.
2505 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2506 hash(t)
2507 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2508 tzinfo=FixedOffset(-1439, ""))
2509 hash(t)
2510
2511 # OTOH, an OOB offset should blow up.
2512 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2513 self.assertRaises(ValueError, hash, t)
2514
2515 def test_zones(self):
2516 est = FixedOffset(-300, "EST")
2517 utc = FixedOffset(0, "UTC")
2518 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002519 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2520 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2521 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002522 self.assertEqual(t1.tzinfo, est)
2523 self.assertEqual(t2.tzinfo, utc)
2524 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002525 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2526 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2527 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002528 self.assertEqual(t1.tzname(), "EST")
2529 self.assertEqual(t2.tzname(), "UTC")
2530 self.assertEqual(t3.tzname(), "MET")
2531 self.assertEqual(hash(t1), hash(t2))
2532 self.assertEqual(hash(t1), hash(t3))
2533 self.assertEqual(hash(t2), hash(t3))
2534 self.assertEqual(t1, t2)
2535 self.assertEqual(t1, t3)
2536 self.assertEqual(t2, t3)
2537 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2538 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2539 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002540 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002541 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2542 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2543 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2544
2545 def test_combine(self):
2546 met = FixedOffset(60, "MET")
2547 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002548 tz = time(18, 45, 3, 1234, tzinfo=met)
2549 dt = datetime.combine(d, tz)
2550 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002551 tzinfo=met))
2552
2553 def test_extract(self):
2554 met = FixedOffset(60, "MET")
2555 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2556 self.assertEqual(dt.date(), date(2002, 3, 4))
2557 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002558 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002559
2560 def test_tz_aware_arithmetic(self):
2561 import random
2562
2563 now = self.theclass.now()
2564 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002565 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002566 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002567 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002568 self.assertEqual(nowaware.timetz(), timeaware)
2569
2570 # Can't mix aware and non-aware.
2571 self.assertRaises(TypeError, lambda: now - nowaware)
2572 self.assertRaises(TypeError, lambda: nowaware - now)
2573
Tim Peters0bf60bd2003-01-08 20:40:01 +00002574 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002575 self.assertRaises(TypeError, lambda: now + nowaware)
2576 self.assertRaises(TypeError, lambda: nowaware + now)
2577 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2578
2579 # Subtracting should yield 0.
2580 self.assertEqual(now - now, timedelta(0))
2581 self.assertEqual(nowaware - nowaware, timedelta(0))
2582
2583 # Adding a delta should preserve tzinfo.
2584 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2585 nowawareplus = nowaware + delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002586 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002587 nowawareplus2 = delta + nowaware
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002588 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002589 self.assertEqual(nowawareplus, nowawareplus2)
2590
2591 # that - delta should be what we started with, and that - what we
2592 # started with should be delta.
2593 diff = nowawareplus - delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002594 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002595 self.assertEqual(nowaware, diff)
2596 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2597 self.assertEqual(nowawareplus - nowaware, delta)
2598
2599 # Make up a random timezone.
2600 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002601 # Attach it to nowawareplus.
2602 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002603 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002604 # Make sure the difference takes the timezone adjustments into account.
2605 got = nowaware - nowawareplus
2606 # Expected: (nowaware base - nowaware offset) -
2607 # (nowawareplus base - nowawareplus offset) =
2608 # (nowaware base - nowawareplus base) +
2609 # (nowawareplus offset - nowaware offset) =
2610 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002611 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002612 self.assertEqual(got, expected)
2613
2614 # Try max possible difference.
2615 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2616 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2617 tzinfo=FixedOffset(-1439, "max"))
2618 maxdiff = max - min
2619 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2620 timedelta(minutes=2*1439))
2621
2622 def test_tzinfo_now(self):
2623 meth = self.theclass.now
2624 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2625 base = meth()
2626 # Try with and without naming the keyword.
2627 off42 = FixedOffset(42, "42")
2628 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002629 again = meth(tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002630 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002631 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002632 # Bad argument with and w/o naming the keyword.
2633 self.assertRaises(TypeError, meth, 16)
2634 self.assertRaises(TypeError, meth, tzinfo=16)
2635 # Bad keyword name.
2636 self.assertRaises(TypeError, meth, tinfo=off42)
2637 # Too many args.
2638 self.assertRaises(TypeError, meth, off42, off42)
2639
Tim Peters10cadce2003-01-23 19:58:02 +00002640 # We don't know which time zone we're in, and don't have a tzinfo
2641 # class to represent it, so seeing whether a tz argument actually
2642 # does a conversion is tricky.
2643 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2644 utc = FixedOffset(0, "utc", 0)
2645 for dummy in range(3):
2646 now = datetime.now(weirdtz)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002647 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002648 utcnow = datetime.utcnow().replace(tzinfo=utc)
2649 now2 = utcnow.astimezone(weirdtz)
2650 if abs(now - now2) < timedelta(seconds=30):
2651 break
2652 # Else the code is broken, or more than 30 seconds passed between
2653 # calls; assuming the latter, just try again.
2654 else:
2655 # Three strikes and we're out.
2656 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2657
Tim Peters2a799bf2002-12-16 20:18:38 +00002658 def test_tzinfo_fromtimestamp(self):
2659 import time
2660 meth = self.theclass.fromtimestamp
2661 ts = time.time()
2662 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2663 base = meth(ts)
2664 # Try with and without naming the keyword.
2665 off42 = FixedOffset(42, "42")
2666 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002667 again = meth(ts, tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002668 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002669 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002670 # Bad argument with and w/o naming the keyword.
2671 self.assertRaises(TypeError, meth, ts, 16)
2672 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2673 # Bad keyword name.
2674 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2675 # Too many args.
2676 self.assertRaises(TypeError, meth, ts, off42, off42)
2677 # Too few args.
2678 self.assertRaises(TypeError, meth)
2679
Tim Peters2a44a8d2003-01-23 20:53:10 +00002680 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002681 timestamp = 1000000000
2682 utcdatetime = datetime.utcfromtimestamp(timestamp)
2683 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2684 # But on some flavor of Mac, it's nowhere near that. So we can't have
2685 # any idea here what time that actually is, we can only test that
2686 # relative changes match.
2687 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2688 tz = FixedOffset(utcoffset, "tz", 0)
2689 expected = utcdatetime + utcoffset
2690 got = datetime.fromtimestamp(timestamp, tz)
2691 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002692
Tim Peters2a799bf2002-12-16 20:18:38 +00002693 def test_tzinfo_utcnow(self):
2694 meth = self.theclass.utcnow
2695 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2696 base = meth()
2697 # Try with and without naming the keyword; for whatever reason,
2698 # utcnow() doesn't accept a tzinfo argument.
2699 off42 = FixedOffset(42, "42")
2700 self.assertRaises(TypeError, meth, off42)
2701 self.assertRaises(TypeError, meth, tzinfo=off42)
2702
2703 def test_tzinfo_utcfromtimestamp(self):
2704 import time
2705 meth = self.theclass.utcfromtimestamp
2706 ts = time.time()
2707 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2708 base = meth(ts)
2709 # Try with and without naming the keyword; for whatever reason,
2710 # utcfromtimestamp() doesn't accept a tzinfo argument.
2711 off42 = FixedOffset(42, "42")
2712 self.assertRaises(TypeError, meth, ts, off42)
2713 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2714
2715 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002716 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002717 # DST flag.
2718 class DST(tzinfo):
2719 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002720 if isinstance(dstvalue, int):
2721 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002722 self.dstvalue = dstvalue
2723 def dst(self, dt):
2724 return self.dstvalue
2725
2726 cls = self.theclass
2727 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2728 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2729 t = d.timetuple()
2730 self.assertEqual(1, t.tm_year)
2731 self.assertEqual(1, t.tm_mon)
2732 self.assertEqual(1, t.tm_mday)
2733 self.assertEqual(10, t.tm_hour)
2734 self.assertEqual(20, t.tm_min)
2735 self.assertEqual(30, t.tm_sec)
2736 self.assertEqual(0, t.tm_wday)
2737 self.assertEqual(1, t.tm_yday)
2738 self.assertEqual(flag, t.tm_isdst)
2739
2740 # dst() returns wrong type.
2741 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2742
2743 # dst() at the edge.
2744 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2745 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2746
2747 # dst() out of range.
2748 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2749 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2750
2751 def test_utctimetuple(self):
2752 class DST(tzinfo):
2753 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002754 if isinstance(dstvalue, int):
2755 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002756 self.dstvalue = dstvalue
2757 def dst(self, dt):
2758 return self.dstvalue
2759
2760 cls = self.theclass
2761 # This can't work: DST didn't implement utcoffset.
2762 self.assertRaises(NotImplementedError,
2763 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2764
2765 class UOFS(DST):
2766 def __init__(self, uofs, dofs=None):
2767 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002768 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002769 def utcoffset(self, dt):
2770 return self.uofs
2771
2772 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2773 # in effect for a UTC time.
2774 for dstvalue in -33, 33, 0, None:
2775 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2776 t = d.utctimetuple()
2777 self.assertEqual(d.year, t.tm_year)
2778 self.assertEqual(d.month, t.tm_mon)
2779 self.assertEqual(d.day, t.tm_mday)
2780 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2781 self.assertEqual(13, t.tm_min)
2782 self.assertEqual(d.second, t.tm_sec)
2783 self.assertEqual(d.weekday(), t.tm_wday)
2784 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2785 t.tm_yday)
2786 self.assertEqual(0, t.tm_isdst)
2787
2788 # At the edges, UTC adjustment can normalize into years out-of-range
2789 # for a datetime object. Ensure that a correct timetuple is
2790 # created anyway.
2791 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2792 # That goes back 1 minute less than a full day.
2793 t = tiny.utctimetuple()
2794 self.assertEqual(t.tm_year, MINYEAR-1)
2795 self.assertEqual(t.tm_mon, 12)
2796 self.assertEqual(t.tm_mday, 31)
2797 self.assertEqual(t.tm_hour, 0)
2798 self.assertEqual(t.tm_min, 1)
2799 self.assertEqual(t.tm_sec, 37)
2800 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2801 self.assertEqual(t.tm_isdst, 0)
2802
2803 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2804 # That goes forward 1 minute less than a full day.
2805 t = huge.utctimetuple()
2806 self.assertEqual(t.tm_year, MAXYEAR+1)
2807 self.assertEqual(t.tm_mon, 1)
2808 self.assertEqual(t.tm_mday, 1)
2809 self.assertEqual(t.tm_hour, 23)
2810 self.assertEqual(t.tm_min, 58)
2811 self.assertEqual(t.tm_sec, 37)
2812 self.assertEqual(t.tm_yday, 1)
2813 self.assertEqual(t.tm_isdst, 0)
2814
2815 def test_tzinfo_isoformat(self):
2816 zero = FixedOffset(0, "+00:00")
2817 plus = FixedOffset(220, "+03:40")
2818 minus = FixedOffset(-231, "-03:51")
2819 unknown = FixedOffset(None, "")
2820
2821 cls = self.theclass
2822 datestr = '0001-02-03'
2823 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002824 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002825 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2826 timestr = '04:05:59' + (us and '.987001' or '')
2827 ofsstr = ofs is not None and d.tzname() or ''
2828 tailstr = timestr + ofsstr
2829 iso = d.isoformat()
2830 self.assertEqual(iso, datestr + 'T' + tailstr)
2831 self.assertEqual(iso, d.isoformat('T'))
2832 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2833 self.assertEqual(str(d), datestr + ' ' + tailstr)
2834
Tim Peters12bf3392002-12-24 05:41:27 +00002835 def test_replace(self):
2836 cls = self.theclass
2837 z100 = FixedOffset(100, "+100")
2838 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2839 args = [1, 2, 3, 4, 5, 6, 7, z100]
2840 base = cls(*args)
2841 self.assertEqual(base, base.replace())
2842
2843 i = 0
2844 for name, newval in (("year", 2),
2845 ("month", 3),
2846 ("day", 4),
2847 ("hour", 5),
2848 ("minute", 6),
2849 ("second", 7),
2850 ("microsecond", 8),
2851 ("tzinfo", zm200)):
2852 newargs = args[:]
2853 newargs[i] = newval
2854 expected = cls(*newargs)
2855 got = base.replace(**{name: newval})
2856 self.assertEqual(expected, got)
2857 i += 1
2858
2859 # Ensure we can get rid of a tzinfo.
2860 self.assertEqual(base.tzname(), "+100")
2861 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002862 self.assertTrue(base2.tzinfo is None)
2863 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002864
2865 # Ensure we can add one.
2866 base3 = base2.replace(tzinfo=z100)
2867 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002868 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002869
2870 # Out of bounds.
2871 base = cls(2000, 2, 29)
2872 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002873
Tim Peters80475bb2002-12-25 07:40:55 +00002874 def test_more_astimezone(self):
2875 # The inherited test_astimezone covered some trivial and error cases.
2876 fnone = FixedOffset(None, "None")
2877 f44m = FixedOffset(44, "44")
2878 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2879
Tim Peters10cadce2003-01-23 19:58:02 +00002880 dt = self.theclass.now(tz=f44m)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002881 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002882 # Replacing with degenerate tzinfo raises an exception.
2883 self.assertRaises(ValueError, dt.astimezone, fnone)
2884 # Ditto with None tz.
2885 self.assertRaises(TypeError, dt.astimezone, None)
2886 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002887 x = dt.astimezone(dt.tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002888 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002889 self.assertEqual(x.date(), dt.date())
2890 self.assertEqual(x.time(), dt.time())
2891
2892 # Replacing with different tzinfo does adjust.
2893 got = dt.astimezone(fm5h)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002894 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002895 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2896 expected = dt - dt.utcoffset() # in effect, convert to UTC
2897 expected += fm5h.utcoffset(dt) # and from there to local time
2898 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2899 self.assertEqual(got.date(), expected.date())
2900 self.assertEqual(got.time(), expected.time())
2901 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002902 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002903 self.assertEqual(got, expected)
2904
Tim Peters4c0db782002-12-26 05:01:19 +00002905 def test_aware_subtract(self):
2906 cls = self.theclass
2907
Tim Peters60c76e42002-12-27 00:41:11 +00002908 # Ensure that utcoffset() is ignored when the operands have the
2909 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002910 class OperandDependentOffset(tzinfo):
2911 def utcoffset(self, t):
2912 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002913 # d0 and d1 equal after adjustment
2914 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002915 else:
Tim Peters397301e2003-01-02 21:28:08 +00002916 # d2 off in the weeds
2917 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002918
2919 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2920 d0 = base.replace(minute=3)
2921 d1 = base.replace(minute=9)
2922 d2 = base.replace(minute=11)
2923 for x in d0, d1, d2:
2924 for y in d0, d1, d2:
2925 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002926 expected = timedelta(minutes=x.minute - y.minute)
2927 self.assertEqual(got, expected)
2928
2929 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2930 # ignored.
2931 base = cls(8, 9, 10, 11, 12, 13, 14)
2932 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2933 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2934 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2935 for x in d0, d1, d2:
2936 for y in d0, d1, d2:
2937 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002938 if (x is d0 or x is d1) and (y is d0 or y is d1):
2939 expected = timedelta(0)
2940 elif x is y is d2:
2941 expected = timedelta(0)
2942 elif x is d2:
2943 expected = timedelta(minutes=(11-59)-0)
2944 else:
2945 assert y is d2
2946 expected = timedelta(minutes=0-(11-59))
2947 self.assertEqual(got, expected)
2948
Tim Peters60c76e42002-12-27 00:41:11 +00002949 def test_mixed_compare(self):
2950 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002951 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002952 self.assertEqual(t1, t2)
2953 t2 = t2.replace(tzinfo=None)
2954 self.assertEqual(t1, t2)
2955 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2956 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002957 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2958 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002959
Tim Peters0bf60bd2003-01-08 20:40:01 +00002960 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002961 class Varies(tzinfo):
2962 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002963 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002964 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002965 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002966 return self.offset
2967
2968 v = Varies()
2969 t1 = t2.replace(tzinfo=v)
2970 t2 = t2.replace(tzinfo=v)
2971 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2972 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2973 self.assertEqual(t1, t2)
2974
2975 # But if they're not identical, it isn't ignored.
2976 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002977 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002978
Tim Petersa98924a2003-05-17 05:55:19 +00002979 def test_subclass_datetimetz(self):
2980
2981 class C(self.theclass):
2982 theAnswer = 42
2983
2984 def __new__(cls, *args, **kws):
2985 temp = kws.copy()
2986 extra = temp.pop('extra')
2987 result = self.theclass.__new__(cls, *args, **temp)
2988 result.extra = extra
2989 return result
2990
2991 def newmeth(self, start):
2992 return start + self.hour + self.year
2993
2994 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2995
2996 dt1 = self.theclass(*args)
2997 dt2 = C(*args, **{'extra': 7})
2998
2999 self.assertEqual(dt2.__class__, C)
3000 self.assertEqual(dt2.theAnswer, 42)
3001 self.assertEqual(dt2.extra, 7)
3002 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3003 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3004
Tim Peters621818b2002-12-29 23:44:49 +00003005# Pain to set up DST-aware tzinfo classes.
3006
3007def first_sunday_on_or_after(dt):
3008 days_to_go = 6 - dt.weekday()
3009 if days_to_go:
3010 dt += timedelta(days_to_go)
3011 return dt
3012
3013ZERO = timedelta(0)
3014HOUR = timedelta(hours=1)
3015DAY = timedelta(days=1)
3016# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3017DSTSTART = datetime(1, 4, 1, 2)
3018# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003019# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3020# being standard time on that day, there is no spelling in local time of
3021# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3022DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003023
3024class USTimeZone(tzinfo):
3025
3026 def __init__(self, hours, reprname, stdname, dstname):
3027 self.stdoffset = timedelta(hours=hours)
3028 self.reprname = reprname
3029 self.stdname = stdname
3030 self.dstname = dstname
3031
3032 def __repr__(self):
3033 return self.reprname
3034
3035 def tzname(self, dt):
3036 if self.dst(dt):
3037 return self.dstname
3038 else:
3039 return self.stdname
3040
3041 def utcoffset(self, dt):
3042 return self.stdoffset + self.dst(dt)
3043
3044 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003045 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003046 # An exception instead may be sensible here, in one or more of
3047 # the cases.
3048 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003049 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003050
3051 # Find first Sunday in April.
3052 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3053 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3054
3055 # Find last Sunday in October.
3056 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3057 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3058
Tim Peters621818b2002-12-29 23:44:49 +00003059 # Can't compare naive to aware objects, so strip the timezone from
3060 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003061 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003062 return HOUR
3063 else:
3064 return ZERO
3065
Tim Peters521fc152002-12-31 17:36:56 +00003066Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3067Central = USTimeZone(-6, "Central", "CST", "CDT")
3068Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3069Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003070utc_real = FixedOffset(0, "UTC", 0)
3071# For better test coverage, we want another flavor of UTC that's west of
3072# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003073utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003074
3075class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003076 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003077 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003078 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003079
Tim Peters0bf60bd2003-01-08 20:40:01 +00003080 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003081
Tim Peters521fc152002-12-31 17:36:56 +00003082 # Check a time that's inside DST.
3083 def checkinside(self, dt, tz, utc, dston, dstoff):
3084 self.assertEqual(dt.dst(), HOUR)
3085
3086 # Conversion to our own timezone is always an identity.
3087 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003088
3089 asutc = dt.astimezone(utc)
3090 there_and_back = asutc.astimezone(tz)
3091
3092 # Conversion to UTC and back isn't always an identity here,
3093 # because there are redundant spellings (in local time) of
3094 # UTC time when DST begins: the clock jumps from 1:59:59
3095 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3096 # make sense then. The classes above treat 2:MM:SS as
3097 # daylight time then (it's "after 2am"), really an alias
3098 # for 1:MM:SS standard time. The latter form is what
3099 # conversion back from UTC produces.
3100 if dt.date() == dston.date() and dt.hour == 2:
3101 # We're in the redundant hour, and coming back from
3102 # UTC gives the 1:MM:SS standard-time spelling.
3103 self.assertEqual(there_and_back + HOUR, dt)
3104 # Although during was considered to be in daylight
3105 # time, there_and_back is not.
3106 self.assertEqual(there_and_back.dst(), ZERO)
3107 # They're the same times in UTC.
3108 self.assertEqual(there_and_back.astimezone(utc),
3109 dt.astimezone(utc))
3110 else:
3111 # We're not in the redundant hour.
3112 self.assertEqual(dt, there_and_back)
3113
Tim Peters327098a2003-01-20 22:54:38 +00003114 # Because we have a redundant spelling when DST begins, there is
3115 # (unforunately) an hour when DST ends that can't be spelled at all in
3116 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3117 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3118 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3119 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3120 # expressed in local time. Nevertheless, we want conversion back
3121 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003122 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003123 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003124 if dt.date() == dstoff.date() and dt.hour == 0:
3125 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003126 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003127 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3128 nexthour_utc += HOUR
3129 nexthour_tz = nexthour_utc.astimezone(tz)
3130 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003131 else:
Tim Peters327098a2003-01-20 22:54:38 +00003132 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003133
3134 # Check a time that's outside DST.
3135 def checkoutside(self, dt, tz, utc):
3136 self.assertEqual(dt.dst(), ZERO)
3137
3138 # Conversion to our own timezone is always an identity.
3139 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003140
3141 # Converting to UTC and back is an identity too.
3142 asutc = dt.astimezone(utc)
3143 there_and_back = asutc.astimezone(tz)
3144 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003145
Tim Peters1024bf82002-12-30 17:09:40 +00003146 def convert_between_tz_and_utc(self, tz, utc):
3147 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003148 # Because 1:MM on the day DST ends is taken as being standard time,
3149 # there is no spelling in tz for the last hour of daylight time.
3150 # For purposes of the test, the last hour of DST is 0:MM, which is
3151 # taken as being daylight time (and 1:MM is taken as being standard
3152 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003153 dstoff = self.dstoff.replace(tzinfo=tz)
3154 for delta in (timedelta(weeks=13),
3155 DAY,
3156 HOUR,
3157 timedelta(minutes=1),
3158 timedelta(microseconds=1)):
3159
Tim Peters521fc152002-12-31 17:36:56 +00003160 self.checkinside(dston, tz, utc, dston, dstoff)
3161 for during in dston + delta, dstoff - delta:
3162 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003163
Tim Peters521fc152002-12-31 17:36:56 +00003164 self.checkoutside(dstoff, tz, utc)
3165 for outside in dston - delta, dstoff + delta:
3166 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003167
Tim Peters621818b2002-12-29 23:44:49 +00003168 def test_easy(self):
3169 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003170 self.convert_between_tz_and_utc(Eastern, utc_real)
3171 self.convert_between_tz_and_utc(Pacific, utc_real)
3172 self.convert_between_tz_and_utc(Eastern, utc_fake)
3173 self.convert_between_tz_and_utc(Pacific, utc_fake)
3174 # The next is really dancing near the edge. It works because
3175 # Pacific and Eastern are far enough apart that their "problem
3176 # hours" don't overlap.
3177 self.convert_between_tz_and_utc(Eastern, Pacific)
3178 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003179 # OTOH, these fail! Don't enable them. The difficulty is that
3180 # the edge case tests assume that every hour is representable in
3181 # the "utc" class. This is always true for a fixed-offset tzinfo
3182 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3183 # For these adjacent DST-aware time zones, the range of time offsets
3184 # tested ends up creating hours in the one that aren't representable
3185 # in the other. For the same reason, we would see failures in the
3186 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3187 # offset deltas in convert_between_tz_and_utc().
3188 #
3189 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3190 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003191
Tim Petersf3615152003-01-01 21:51:37 +00003192 def test_tricky(self):
3193 # 22:00 on day before daylight starts.
3194 fourback = self.dston - timedelta(hours=4)
3195 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003196 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003197 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3198 # 2", we should get the 3 spelling.
3199 # If we plug 22:00 the day before into Eastern, it "looks like std
3200 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3201 # to 22:00 lands on 2:00, which makes no sense in local time (the
3202 # local clock jumps from 1 to 3). The point here is to make sure we
3203 # get the 3 spelling.
3204 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003205 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003206 self.assertEqual(expected, got)
3207
3208 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3209 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003210 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003211 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3212 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3213 # spelling.
3214 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003215 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003216 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003217
Tim Petersadf64202003-01-04 06:03:15 +00003218 # Now on the day DST ends, we want "repeat an hour" behavior.
3219 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3220 # EST 23:MM 0:MM 1:MM 2:MM
3221 # EDT 0:MM 1:MM 2:MM 3:MM
3222 # wall 0:MM 1:MM 1:MM 2:MM against these
3223 for utc in utc_real, utc_fake:
3224 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003225 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003226 # Convert that to UTC.
3227 first_std_hour -= tz.utcoffset(None)
3228 # Adjust for possibly fake UTC.
3229 asutc = first_std_hour + utc.utcoffset(None)
3230 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3231 # tz=Eastern.
3232 asutcbase = asutc.replace(tzinfo=utc)
3233 for tzhour in (0, 1, 1, 2):
3234 expectedbase = self.dstoff.replace(hour=tzhour)
3235 for minute in 0, 30, 59:
3236 expected = expectedbase.replace(minute=minute)
3237 asutc = asutcbase.replace(minute=minute)
3238 astz = asutc.astimezone(tz)
3239 self.assertEqual(astz.replace(tzinfo=None), expected)
3240 asutcbase += HOUR
3241
3242
Tim Peters710fb152003-01-02 19:35:54 +00003243 def test_bogus_dst(self):
3244 class ok(tzinfo):
3245 def utcoffset(self, dt): return HOUR
3246 def dst(self, dt): return HOUR
3247
3248 now = self.theclass.now().replace(tzinfo=utc_real)
3249 # Doesn't blow up.
3250 now.astimezone(ok())
3251
3252 # Does blow up.
3253 class notok(ok):
3254 def dst(self, dt): return None
3255 self.assertRaises(ValueError, now.astimezone, notok())
3256
Tim Peters52dcce22003-01-23 16:36:11 +00003257 def test_fromutc(self):
3258 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3259 now = datetime.utcnow().replace(tzinfo=utc_real)
3260 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3261 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3262 enow = Eastern.fromutc(now) # doesn't blow up
3263 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3264 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3265 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3266
3267 # Always converts UTC to standard time.
3268 class FauxUSTimeZone(USTimeZone):
3269 def fromutc(self, dt):
3270 return dt + self.stdoffset
3271 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3272
3273 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3274 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3275 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3276
3277 # Check around DST start.
3278 start = self.dston.replace(hour=4, tzinfo=Eastern)
3279 fstart = start.replace(tzinfo=FEastern)
3280 for wall in 23, 0, 1, 3, 4, 5:
3281 expected = start.replace(hour=wall)
3282 if wall == 23:
3283 expected -= timedelta(days=1)
3284 got = Eastern.fromutc(start)
3285 self.assertEqual(expected, got)
3286
3287 expected = fstart + FEastern.stdoffset
3288 got = FEastern.fromutc(fstart)
3289 self.assertEqual(expected, got)
3290
3291 # Ensure astimezone() calls fromutc() too.
3292 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3293 self.assertEqual(expected, got)
3294
3295 start += HOUR
3296 fstart += HOUR
3297
3298 # Check around DST end.
3299 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3300 fstart = start.replace(tzinfo=FEastern)
3301 for wall in 0, 1, 1, 2, 3, 4:
3302 expected = start.replace(hour=wall)
3303 got = Eastern.fromutc(start)
3304 self.assertEqual(expected, got)
3305
3306 expected = fstart + FEastern.stdoffset
3307 got = FEastern.fromutc(fstart)
3308 self.assertEqual(expected, got)
3309
3310 # Ensure astimezone() calls fromutc() too.
3311 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3312 self.assertEqual(expected, got)
3313
3314 start += HOUR
3315 fstart += HOUR
3316
Tim Peters710fb152003-01-02 19:35:54 +00003317
Tim Peters528ca532004-09-16 01:30:50 +00003318#############################################################################
3319# oddballs
3320
3321class Oddballs(unittest.TestCase):
3322
3323 def test_bug_1028306(self):
3324 # Trying to compare a date to a datetime should act like a mixed-
3325 # type comparison, despite that datetime is a subclass of date.
3326 as_date = date.today()
3327 as_datetime = datetime.combine(as_date, time())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003328 self.assertTrue(as_date != as_datetime)
3329 self.assertTrue(as_datetime != as_date)
3330 self.assertTrue(not as_date == as_datetime)
3331 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003332 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 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3337 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3338 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3339 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3340
3341 # Neverthelss, comparison should work with the base-class (date)
3342 # projection if use of a date method is forced.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003343 self.assertTrue(as_date.__eq__(as_datetime))
Tim Peters528ca532004-09-16 01:30:50 +00003344 different_day = (as_date.day + 1) % 20 + 1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003345 self.assertTrue(not as_date.__eq__(as_datetime.replace(day=
Tim Peters528ca532004-09-16 01:30:50 +00003346 different_day)))
3347
3348 # And date should compare with other subclasses of date. If a
3349 # subclass wants to stop this, it's up to the subclass to do so.
3350 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3351 self.assertEqual(as_date, date_sc)
3352 self.assertEqual(date_sc, as_date)
3353
3354 # Ditto for datetimes.
3355 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3356 as_date.day, 0, 0, 0)
3357 self.assertEqual(as_datetime, datetime_sc)
3358 self.assertEqual(datetime_sc, as_datetime)
3359
Tim Peters2a799bf2002-12-16 20:18:38 +00003360def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003361 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003362
3363if __name__ == "__main__":
3364 test_main()