blob: 52387a09e93184a4ca1c2c7f8b402342fbc2df57 [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"""
Tim Peters2a799bf2002-12-16 20:18:38 +00005
Guido van Rossumf1200f82007-03-07 15:16:29 +00006import os
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)
272
Tim Peters2a799bf2002-12-16 20:18:38 +0000273 def test_carries(self):
274 t1 = timedelta(days=100,
275 weeks=-7,
276 hours=-24*(100-49),
277 minutes=-3,
278 seconds=12,
279 microseconds=(3*60 - 12) * 1e6 + 1)
280 t2 = timedelta(microseconds=1)
281 self.assertEqual(t1, t2)
282
283 def test_hash_equality(self):
284 t1 = timedelta(days=100,
285 weeks=-7,
286 hours=-24*(100-49),
287 minutes=-3,
288 seconds=12,
289 microseconds=(3*60 - 12) * 1000000)
290 t2 = timedelta()
291 self.assertEqual(hash(t1), hash(t2))
292
293 t1 += timedelta(weeks=7)
294 t2 += timedelta(days=7*7)
295 self.assertEqual(t1, t2)
296 self.assertEqual(hash(t1), hash(t2))
297
298 d = {t1: 1}
299 d[t2] = 2
300 self.assertEqual(len(d), 1)
301 self.assertEqual(d[t1], 2)
302
303 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000304 args = 12, 34, 56
305 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000306 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000307 green = pickler.dumps(orig, proto)
308 derived = unpickler.loads(green)
309 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000310
311 def test_compare(self):
312 t1 = timedelta(2, 3, 4)
313 t2 = timedelta(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000314 self.assertTrue(t1 == t2)
315 self.assertTrue(t1 <= t2)
316 self.assertTrue(t1 >= t2)
317 self.assertTrue(not t1 != t2)
318 self.assertTrue(not t1 < t2)
319 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000320 self.assertEqual(cmp(t1, t2), 0)
321 self.assertEqual(cmp(t2, t1), 0)
322
323 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
324 t2 = timedelta(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000325 self.assertTrue(t1 < t2)
326 self.assertTrue(t2 > t1)
327 self.assertTrue(t1 <= t2)
328 self.assertTrue(t2 >= t1)
329 self.assertTrue(t1 != t2)
330 self.assertTrue(t2 != t1)
331 self.assertTrue(not t1 == t2)
332 self.assertTrue(not t2 == t1)
333 self.assertTrue(not t1 > t2)
334 self.assertTrue(not t2 < t1)
335 self.assertTrue(not t1 >= t2)
336 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000337 self.assertEqual(cmp(t1, t2), -1)
338 self.assertEqual(cmp(t2, t1), 1)
339
Tim Peters68124bb2003-02-08 03:46:31 +0000340 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000341 self.assertEqual(t1 == badarg, False)
342 self.assertEqual(t1 != badarg, True)
343 self.assertEqual(badarg == t1, False)
344 self.assertEqual(badarg != t1, True)
345
Tim Peters2a799bf2002-12-16 20:18:38 +0000346 self.assertRaises(TypeError, lambda: t1 <= badarg)
347 self.assertRaises(TypeError, lambda: t1 < badarg)
348 self.assertRaises(TypeError, lambda: t1 > badarg)
349 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000350 self.assertRaises(TypeError, lambda: badarg <= t1)
351 self.assertRaises(TypeError, lambda: badarg < t1)
352 self.assertRaises(TypeError, lambda: badarg > t1)
353 self.assertRaises(TypeError, lambda: badarg >= t1)
354
355 def test_str(self):
356 td = timedelta
357 eq = self.assertEqual
358
359 eq(str(td(1)), "1 day, 0:00:00")
360 eq(str(td(-1)), "-1 day, 0:00:00")
361 eq(str(td(2)), "2 days, 0:00:00")
362 eq(str(td(-2)), "-2 days, 0:00:00")
363
364 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
365 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
366 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
367 "-210 days, 23:12:34")
368
369 eq(str(td(milliseconds=1)), "0:00:00.001000")
370 eq(str(td(microseconds=3)), "0:00:00.000003")
371
372 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
373 microseconds=999999)),
374 "999999999 days, 23:59:59.999999")
375
376 def test_roundtrip(self):
377 for td in (timedelta(days=999999999, hours=23, minutes=59,
378 seconds=59, microseconds=999999),
379 timedelta(days=-999999999),
380 timedelta(days=1, seconds=2, microseconds=3)):
381
382 # Verify td -> string -> td identity.
383 s = repr(td)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000384 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000385 s = s[9:]
386 td2 = eval(s)
387 self.assertEqual(td, td2)
388
389 # Verify identity via reconstructing from pieces.
390 td2 = timedelta(td.days, td.seconds, td.microseconds)
391 self.assertEqual(td, td2)
392
393 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000394 self.assertIsInstance(timedelta.min, timedelta)
395 self.assertIsInstance(timedelta.max, timedelta)
396 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000397 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000398 self.assertEqual(timedelta.min, timedelta(-999999999))
399 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
400 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
401
402 def test_overflow(self):
403 tiny = timedelta.resolution
404
405 td = timedelta.min + tiny
406 td -= tiny # no problem
407 self.assertRaises(OverflowError, td.__sub__, tiny)
408 self.assertRaises(OverflowError, td.__add__, -tiny)
409
410 td = timedelta.max - tiny
411 td += tiny # no problem
412 self.assertRaises(OverflowError, td.__add__, tiny)
413 self.assertRaises(OverflowError, td.__sub__, -tiny)
414
415 self.assertRaises(OverflowError, lambda: -timedelta.max)
416
417 def test_microsecond_rounding(self):
418 td = timedelta
419 eq = self.assertEqual
420
421 # Single-field rounding.
422 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
423 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
424 eq(td(milliseconds=0.6/1000), td(microseconds=1))
425 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
426
427 # Rounding due to contributions from more than one field.
428 us_per_hour = 3600e6
429 us_per_day = us_per_hour * 24
430 eq(td(days=.4/us_per_day), td(0))
431 eq(td(hours=.2/us_per_hour), td(0))
432 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
433
434 eq(td(days=-.4/us_per_day), td(0))
435 eq(td(hours=-.2/us_per_hour), td(0))
436 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
437
438 def test_massive_normalization(self):
439 td = timedelta(microseconds=-1)
440 self.assertEqual((td.days, td.seconds, td.microseconds),
441 (-1, 24*3600-1, 999999))
442
443 def test_bool(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000444 self.assertTrue(timedelta(1))
445 self.assertTrue(timedelta(0, 1))
446 self.assertTrue(timedelta(0, 0, 1))
447 self.assertTrue(timedelta(microseconds=1))
448 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000449
Tim Petersb0c854d2003-05-17 15:57:00 +0000450 def test_subclass_timedelta(self):
451
452 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000453 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000454 def from_td(td):
455 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000456
457 def as_hours(self):
458 sum = (self.days * 24 +
459 self.seconds / 3600.0 +
460 self.microseconds / 3600e6)
461 return round(sum)
462
463 t1 = T(days=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000464 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000465 self.assertEqual(t1.as_hours(), 24)
466
467 t2 = T(days=-1, seconds=-3600)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000468 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000469 self.assertEqual(t2.as_hours(), -25)
470
471 t3 = t1 + t2
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000472 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000473 t4 = T.from_td(t3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000474 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000475 self.assertEqual(t3.days, t4.days)
476 self.assertEqual(t3.seconds, t4.seconds)
477 self.assertEqual(t3.microseconds, t4.microseconds)
478 self.assertEqual(str(t3), str(t4))
479 self.assertEqual(t4.as_hours(), -1)
480
Tim Peters2a799bf2002-12-16 20:18:38 +0000481#############################################################################
482# date tests
483
484class TestDateOnly(unittest.TestCase):
485 # Tests here won't pass if also run on datetime objects, so don't
486 # subclass this to test datetimes too.
487
488 def test_delta_non_days_ignored(self):
489 dt = date(2000, 1, 2)
490 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
491 microseconds=5)
492 days = timedelta(delta.days)
493 self.assertEqual(days, timedelta(1))
494
495 dt2 = dt + delta
496 self.assertEqual(dt2, dt + days)
497
498 dt2 = delta + dt
499 self.assertEqual(dt2, dt + days)
500
501 dt2 = dt - delta
502 self.assertEqual(dt2, dt - days)
503
504 delta = -delta
505 days = timedelta(delta.days)
506 self.assertEqual(days, timedelta(-2))
507
508 dt2 = dt + delta
509 self.assertEqual(dt2, dt + days)
510
511 dt2 = delta + dt
512 self.assertEqual(dt2, dt + days)
513
514 dt2 = dt - delta
515 self.assertEqual(dt2, dt - days)
516
Tim Peters604c0132004-06-07 23:04:33 +0000517class SubclassDate(date):
518 sub_var = 1
519
Collin Winterc2898c52007-04-25 17:29:52 +0000520class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000521 # Tests here should pass for both dates and datetimes, except for a
522 # few tests that TestDateTime overrides.
523
524 theclass = date
525
526 def test_basic_attributes(self):
527 dt = self.theclass(2002, 3, 1)
528 self.assertEqual(dt.year, 2002)
529 self.assertEqual(dt.month, 3)
530 self.assertEqual(dt.day, 1)
531
532 def test_roundtrip(self):
533 for dt in (self.theclass(1, 2, 3),
534 self.theclass.today()):
535 # Verify dt -> string -> date identity.
536 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000537 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000538 s = s[9:]
539 dt2 = eval(s)
540 self.assertEqual(dt, dt2)
541
542 # Verify identity via reconstructing from pieces.
543 dt2 = self.theclass(dt.year, dt.month, dt.day)
544 self.assertEqual(dt, dt2)
545
546 def test_ordinal_conversions(self):
547 # Check some fixed values.
548 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
549 (1, 12, 31, 365),
550 (2, 1, 1, 366),
551 # first example from "Calendrical Calculations"
552 (1945, 11, 12, 710347)]:
553 d = self.theclass(y, m, d)
554 self.assertEqual(n, d.toordinal())
555 fromord = self.theclass.fromordinal(n)
556 self.assertEqual(d, fromord)
557 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000558 # if we're checking something fancier than a date, verify
559 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000560 self.assertEqual(fromord.hour, 0)
561 self.assertEqual(fromord.minute, 0)
562 self.assertEqual(fromord.second, 0)
563 self.assertEqual(fromord.microsecond, 0)
564
Tim Peters0bf60bd2003-01-08 20:40:01 +0000565 # Check first and last days of year spottily across the whole
566 # range of years supported.
567 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000568 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
569 d = self.theclass(year, 1, 1)
570 n = d.toordinal()
571 d2 = self.theclass.fromordinal(n)
572 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000573 # Verify that moving back a day gets to the end of year-1.
574 if year > 1:
575 d = self.theclass.fromordinal(n-1)
576 d2 = self.theclass(year-1, 12, 31)
577 self.assertEqual(d, d2)
578 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000579
580 # Test every day in a leap-year and a non-leap year.
581 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
582 for year, isleap in (2000, True), (2002, False):
583 n = self.theclass(year, 1, 1).toordinal()
584 for month, maxday in zip(range(1, 13), dim):
585 if month == 2 and isleap:
586 maxday += 1
587 for day in range(1, maxday+1):
588 d = self.theclass(year, month, day)
589 self.assertEqual(d.toordinal(), n)
590 self.assertEqual(d, self.theclass.fromordinal(n))
591 n += 1
592
593 def test_extreme_ordinals(self):
594 a = self.theclass.min
595 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
596 aord = a.toordinal()
597 b = a.fromordinal(aord)
598 self.assertEqual(a, b)
599
600 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
601
602 b = a + timedelta(days=1)
603 self.assertEqual(b.toordinal(), aord + 1)
604 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
605
606 a = self.theclass.max
607 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
608 aord = a.toordinal()
609 b = a.fromordinal(aord)
610 self.assertEqual(a, b)
611
612 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
613
614 b = a - timedelta(days=1)
615 self.assertEqual(b.toordinal(), aord - 1)
616 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
617
618 def test_bad_constructor_arguments(self):
619 # bad years
620 self.theclass(MINYEAR, 1, 1) # no exception
621 self.theclass(MAXYEAR, 1, 1) # no exception
622 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
623 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
624 # bad months
625 self.theclass(2000, 1, 1) # no exception
626 self.theclass(2000, 12, 1) # no exception
627 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
628 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
629 # bad days
630 self.theclass(2000, 2, 29) # no exception
631 self.theclass(2004, 2, 29) # no exception
632 self.theclass(2400, 2, 29) # no exception
633 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
634 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
635 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
636 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
637 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
638 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
639
640 def test_hash_equality(self):
641 d = self.theclass(2000, 12, 31)
642 # same thing
643 e = self.theclass(2000, 12, 31)
644 self.assertEqual(d, e)
645 self.assertEqual(hash(d), hash(e))
646
647 dic = {d: 1}
648 dic[e] = 2
649 self.assertEqual(len(dic), 1)
650 self.assertEqual(dic[d], 2)
651 self.assertEqual(dic[e], 2)
652
653 d = self.theclass(2001, 1, 1)
654 # same thing
655 e = self.theclass(2001, 1, 1)
656 self.assertEqual(d, e)
657 self.assertEqual(hash(d), hash(e))
658
659 dic = {d: 1}
660 dic[e] = 2
661 self.assertEqual(len(dic), 1)
662 self.assertEqual(dic[d], 2)
663 self.assertEqual(dic[e], 2)
664
665 def test_computations(self):
666 a = self.theclass(2002, 1, 31)
667 b = self.theclass(1956, 1, 31)
668
669 diff = a-b
670 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
671 self.assertEqual(diff.seconds, 0)
672 self.assertEqual(diff.microseconds, 0)
673
674 day = timedelta(1)
675 week = timedelta(7)
676 a = self.theclass(2002, 3, 2)
677 self.assertEqual(a + day, self.theclass(2002, 3, 3))
678 self.assertEqual(day + a, self.theclass(2002, 3, 3))
679 self.assertEqual(a - day, self.theclass(2002, 3, 1))
680 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
681 self.assertEqual(a + week, self.theclass(2002, 3, 9))
682 self.assertEqual(a - week, self.theclass(2002, 2, 23))
683 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
684 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
685 self.assertEqual((a + week) - a, week)
686 self.assertEqual((a + day) - a, day)
687 self.assertEqual((a - week) - a, -week)
688 self.assertEqual((a - day) - a, -day)
689 self.assertEqual(a - (a + week), -week)
690 self.assertEqual(a - (a + day), -day)
691 self.assertEqual(a - (a - week), week)
692 self.assertEqual(a - (a - day), day)
693
694 # Add/sub ints, longs, floats should be illegal
695 for i in 1, 1L, 1.0:
696 self.assertRaises(TypeError, lambda: a+i)
697 self.assertRaises(TypeError, lambda: a-i)
698 self.assertRaises(TypeError, lambda: i+a)
699 self.assertRaises(TypeError, lambda: i-a)
700
701 # delta - date is senseless.
702 self.assertRaises(TypeError, lambda: day - a)
703 # mixing date and (delta or date) via * or // is senseless
704 self.assertRaises(TypeError, lambda: day * a)
705 self.assertRaises(TypeError, lambda: a * day)
706 self.assertRaises(TypeError, lambda: day // a)
707 self.assertRaises(TypeError, lambda: a // day)
708 self.assertRaises(TypeError, lambda: a * a)
709 self.assertRaises(TypeError, lambda: a // a)
710 # date + date is senseless
711 self.assertRaises(TypeError, lambda: a + a)
712
713 def test_overflow(self):
714 tiny = self.theclass.resolution
715
716 dt = self.theclass.min + tiny
717 dt -= tiny # no problem
718 self.assertRaises(OverflowError, dt.__sub__, tiny)
719 self.assertRaises(OverflowError, dt.__add__, -tiny)
720
721 dt = self.theclass.max - tiny
722 dt += tiny # no problem
723 self.assertRaises(OverflowError, dt.__add__, tiny)
724 self.assertRaises(OverflowError, dt.__sub__, -tiny)
725
726 def test_fromtimestamp(self):
727 import time
728
729 # Try an arbitrary fixed value.
730 year, month, day = 1999, 9, 19
731 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
732 d = self.theclass.fromtimestamp(ts)
733 self.assertEqual(d.year, year)
734 self.assertEqual(d.month, month)
735 self.assertEqual(d.day, day)
736
Tim Peters1b6f7a92004-06-20 02:50:16 +0000737 def test_insane_fromtimestamp(self):
738 # It's possible that some platform maps time_t to double,
739 # and that this test will fail there. This test should
740 # exempt such platforms (provided they return reasonable
741 # results!).
742 for insane in -1e200, 1e200:
743 self.assertRaises(ValueError, self.theclass.fromtimestamp,
744 insane)
745
Tim Peters2a799bf2002-12-16 20:18:38 +0000746 def test_today(self):
747 import time
748
749 # We claim that today() is like fromtimestamp(time.time()), so
750 # prove it.
751 for dummy in range(3):
752 today = self.theclass.today()
753 ts = time.time()
754 todayagain = self.theclass.fromtimestamp(ts)
755 if today == todayagain:
756 break
757 # There are several legit reasons that could fail:
758 # 1. It recently became midnight, between the today() and the
759 # time() calls.
760 # 2. The platform time() has such fine resolution that we'll
761 # never get the same value twice.
762 # 3. The platform time() has poor resolution, and we just
763 # happened to call today() right before a resolution quantum
764 # boundary.
765 # 4. The system clock got fiddled between calls.
766 # In any case, wait a little while and try again.
767 time.sleep(0.1)
768
769 # It worked or it didn't. If it didn't, assume it's reason #2, and
770 # let the test pass if they're within half a second of each other.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000771 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000772 abs(todayagain - today) < timedelta(seconds=0.5))
773
774 def test_weekday(self):
775 for i in range(7):
776 # March 4, 2002 is a Monday
777 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
778 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
779 # January 2, 1956 is a Monday
780 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
781 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
782
783 def test_isocalendar(self):
784 # Check examples from
785 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
786 for i in range(7):
787 d = self.theclass(2003, 12, 22+i)
788 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
789 d = self.theclass(2003, 12, 29) + timedelta(i)
790 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
791 d = self.theclass(2004, 1, 5+i)
792 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
793 d = self.theclass(2009, 12, 21+i)
794 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
795 d = self.theclass(2009, 12, 28) + timedelta(i)
796 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
797 d = self.theclass(2010, 1, 4+i)
798 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
799
800 def test_iso_long_years(self):
801 # Calculate long ISO years and compare to table from
802 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
803 ISO_LONG_YEARS_TABLE = """
804 4 32 60 88
805 9 37 65 93
806 15 43 71 99
807 20 48 76
808 26 54 82
809
810 105 133 161 189
811 111 139 167 195
812 116 144 172
813 122 150 178
814 128 156 184
815
816 201 229 257 285
817 207 235 263 291
818 212 240 268 296
819 218 246 274
820 224 252 280
821
822 303 331 359 387
823 308 336 364 392
824 314 342 370 398
825 320 348 376
826 325 353 381
827 """
828 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
829 iso_long_years.sort()
830 L = []
831 for i in range(400):
832 d = self.theclass(2000+i, 12, 31)
833 d1 = self.theclass(1600+i, 12, 31)
834 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
835 if d.isocalendar()[1] == 53:
836 L.append(i)
837 self.assertEqual(L, iso_long_years)
838
839 def test_isoformat(self):
840 t = self.theclass(2, 3, 2)
841 self.assertEqual(t.isoformat(), "0002-03-02")
842
843 def test_ctime(self):
844 t = self.theclass(2002, 3, 2)
845 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
846
847 def test_strftime(self):
848 t = self.theclass(2005, 3, 2)
849 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000850 self.assertEqual(t.strftime(""), "") # SF bug #761337
Georg Brandl4ddfcd32006-09-30 11:17:34 +0000851 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000852
853 self.assertRaises(TypeError, t.strftime) # needs an arg
854 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
855 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
856
Gregory P. Smith137d8242008-06-02 04:05:52 +0000857 # test that unicode input is allowed (issue 2782)
858 self.assertEqual(t.strftime(u"%m"), "03")
859
Tim Peters2a799bf2002-12-16 20:18:38 +0000860 # A naive object replaces %z and %Z w/ empty strings.
861 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
862
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000863 #make sure that invalid format specifiers are handled correctly
Kristján Valur Jónsson8adc0b52009-01-15 09:09:13 +0000864 #self.assertRaises(ValueError, t.strftime, "%e")
865 #self.assertRaises(ValueError, t.strftime, "%")
866 #self.assertRaises(ValueError, t.strftime, "%#")
867
868 #oh well, some systems just ignore those invalid ones.
869 #at least, excercise them to make sure that no crashes
870 #are generated
871 for f in ["%e", "%", "%#"]:
872 try:
873 t.strftime(f)
874 except ValueError:
875 pass
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000876
877 #check that this standard extension works
878 t.strftime("%f")
879
Gregory P. Smith137d8242008-06-02 04:05:52 +0000880
Eric Smitha9f7d622008-02-17 19:46:49 +0000881 def test_format(self):
882 dt = self.theclass(2007, 9, 10)
883 self.assertEqual(dt.__format__(''), str(dt))
884
885 # check that a derived class's __str__() gets called
886 class A(self.theclass):
887 def __str__(self):
888 return 'A'
889 a = A(2007, 9, 10)
890 self.assertEqual(a.__format__(''), 'A')
891
892 # check that a derived class's strftime gets called
893 class B(self.theclass):
894 def strftime(self, format_spec):
895 return 'B'
896 b = B(2007, 9, 10)
897 self.assertEqual(b.__format__(''), str(dt))
898
899 for fmt in ["m:%m d:%d y:%y",
900 "m:%m d:%d y:%y H:%H M:%M S:%S",
901 "%z %Z",
902 ]:
903 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
904 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
905 self.assertEqual(b.__format__(fmt), 'B')
906
Tim Peters2a799bf2002-12-16 20:18:38 +0000907 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000908 self.assertIsInstance(self.theclass.min, self.theclass)
909 self.assertIsInstance(self.theclass.max, self.theclass)
910 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000911 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000912
913 def test_extreme_timedelta(self):
914 big = self.theclass.max - self.theclass.min
915 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
916 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
917 # n == 315537897599999999 ~= 2**58.13
918 justasbig = timedelta(0, 0, n)
919 self.assertEqual(big, justasbig)
920 self.assertEqual(self.theclass.min + big, self.theclass.max)
921 self.assertEqual(self.theclass.max - big, self.theclass.min)
922
923 def test_timetuple(self):
924 for i in range(7):
925 # January 2, 1956 is a Monday (0)
926 d = self.theclass(1956, 1, 2+i)
927 t = d.timetuple()
928 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
929 # February 1, 1956 is a Wednesday (2)
930 d = self.theclass(1956, 2, 1+i)
931 t = d.timetuple()
932 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
933 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
934 # of the year.
935 d = self.theclass(1956, 3, 1+i)
936 t = d.timetuple()
937 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
938 self.assertEqual(t.tm_year, 1956)
939 self.assertEqual(t.tm_mon, 3)
940 self.assertEqual(t.tm_mday, 1+i)
941 self.assertEqual(t.tm_hour, 0)
942 self.assertEqual(t.tm_min, 0)
943 self.assertEqual(t.tm_sec, 0)
944 self.assertEqual(t.tm_wday, (3+i)%7)
945 self.assertEqual(t.tm_yday, 61+i)
946 self.assertEqual(t.tm_isdst, -1)
947
948 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000949 args = 6, 7, 23
950 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000951 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000952 green = pickler.dumps(orig, proto)
953 derived = unpickler.loads(green)
954 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000955
956 def test_compare(self):
957 t1 = self.theclass(2, 3, 4)
958 t2 = self.theclass(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000959 self.assertTrue(t1 == t2)
960 self.assertTrue(t1 <= t2)
961 self.assertTrue(t1 >= t2)
962 self.assertTrue(not t1 != t2)
963 self.assertTrue(not t1 < t2)
964 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000965 self.assertEqual(cmp(t1, t2), 0)
966 self.assertEqual(cmp(t2, t1), 0)
967
968 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
969 t2 = self.theclass(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000970 self.assertTrue(t1 < t2)
971 self.assertTrue(t2 > t1)
972 self.assertTrue(t1 <= t2)
973 self.assertTrue(t2 >= t1)
974 self.assertTrue(t1 != t2)
975 self.assertTrue(t2 != t1)
976 self.assertTrue(not t1 == t2)
977 self.assertTrue(not t2 == t1)
978 self.assertTrue(not t1 > t2)
979 self.assertTrue(not t2 < t1)
980 self.assertTrue(not t1 >= t2)
981 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000982 self.assertEqual(cmp(t1, t2), -1)
983 self.assertEqual(cmp(t2, t1), 1)
984
Tim Peters68124bb2003-02-08 03:46:31 +0000985 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000986 self.assertEqual(t1 == badarg, False)
987 self.assertEqual(t1 != badarg, True)
988 self.assertEqual(badarg == t1, False)
989 self.assertEqual(badarg != t1, True)
990
Tim Peters2a799bf2002-12-16 20:18:38 +0000991 self.assertRaises(TypeError, lambda: t1 < badarg)
992 self.assertRaises(TypeError, lambda: t1 > badarg)
993 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000994 self.assertRaises(TypeError, lambda: badarg <= t1)
995 self.assertRaises(TypeError, lambda: badarg < t1)
996 self.assertRaises(TypeError, lambda: badarg > t1)
997 self.assertRaises(TypeError, lambda: badarg >= t1)
998
Tim Peters8d81a012003-01-24 22:36:34 +0000999 def test_mixed_compare(self):
1000 our = self.theclass(2000, 4, 5)
1001 self.assertRaises(TypeError, cmp, our, 1)
1002 self.assertRaises(TypeError, cmp, 1, our)
1003
1004 class AnotherDateTimeClass(object):
1005 def __cmp__(self, other):
1006 # Return "equal" so calling this can't be confused with
1007 # compare-by-address (which never says "equal" for distinct
1008 # objects).
1009 return 0
Nick Coghlan48361f52008-08-11 15:45:58 +00001010 __hash__ = None # Silence Py3k warning
Tim Peters8d81a012003-01-24 22:36:34 +00001011
1012 # This still errors, because date and datetime comparison raise
1013 # TypeError instead of NotImplemented when they don't know what to
1014 # do, in order to stop comparison from falling back to the default
1015 # compare-by-address.
1016 their = AnotherDateTimeClass()
1017 self.assertRaises(TypeError, cmp, our, their)
1018 # Oops: The next stab raises TypeError in the C implementation,
1019 # but not in the Python implementation of datetime. The difference
1020 # is due to that the Python implementation defines __cmp__ but
1021 # the C implementation defines tp_richcompare. This is more pain
1022 # to fix than it's worth, so commenting out the test.
1023 # self.assertEqual(cmp(their, our), 0)
1024
1025 # But date and datetime comparison return NotImplemented instead if the
1026 # other object has a timetuple attr. This gives the other object a
1027 # chance to do the comparison.
1028 class Comparable(AnotherDateTimeClass):
1029 def timetuple(self):
1030 return ()
1031
1032 their = Comparable()
1033 self.assertEqual(cmp(our, their), 0)
1034 self.assertEqual(cmp(their, our), 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001035 self.assertTrue(our == their)
1036 self.assertTrue(their == our)
Tim Peters8d81a012003-01-24 22:36:34 +00001037
Tim Peters2a799bf2002-12-16 20:18:38 +00001038 def test_bool(self):
1039 # All dates are considered true.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001040 self.assertTrue(self.theclass.min)
1041 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001042
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001043 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001044 # For nasty technical reasons, we can't handle years before 1900.
1045 cls = self.theclass
1046 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1047 for y in 1, 49, 51, 99, 100, 1000, 1899:
1048 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001049
1050 def test_replace(self):
1051 cls = self.theclass
1052 args = [1, 2, 3]
1053 base = cls(*args)
1054 self.assertEqual(base, base.replace())
1055
1056 i = 0
1057 for name, newval in (("year", 2),
1058 ("month", 3),
1059 ("day", 4)):
1060 newargs = args[:]
1061 newargs[i] = newval
1062 expected = cls(*newargs)
1063 got = base.replace(**{name: newval})
1064 self.assertEqual(expected, got)
1065 i += 1
1066
1067 # Out of bounds.
1068 base = cls(2000, 2, 29)
1069 self.assertRaises(ValueError, base.replace, year=2001)
1070
Tim Petersa98924a2003-05-17 05:55:19 +00001071 def test_subclass_date(self):
1072
1073 class C(self.theclass):
1074 theAnswer = 42
1075
1076 def __new__(cls, *args, **kws):
1077 temp = kws.copy()
1078 extra = temp.pop('extra')
1079 result = self.theclass.__new__(cls, *args, **temp)
1080 result.extra = extra
1081 return result
1082
1083 def newmeth(self, start):
1084 return start + self.year + self.month
1085
1086 args = 2003, 4, 14
1087
1088 dt1 = self.theclass(*args)
1089 dt2 = C(*args, **{'extra': 7})
1090
1091 self.assertEqual(dt2.__class__, C)
1092 self.assertEqual(dt2.theAnswer, 42)
1093 self.assertEqual(dt2.extra, 7)
1094 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1095 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1096
Tim Peters604c0132004-06-07 23:04:33 +00001097 def test_pickling_subclass_date(self):
1098
1099 args = 6, 7, 23
1100 orig = SubclassDate(*args)
1101 for pickler, unpickler, proto in pickle_choices:
1102 green = pickler.dumps(orig, proto)
1103 derived = unpickler.loads(green)
1104 self.assertEqual(orig, derived)
1105
Tim Peters3f606292004-03-21 23:38:41 +00001106 def test_backdoor_resistance(self):
1107 # For fast unpickling, the constructor accepts a pickle string.
1108 # This is a low-overhead backdoor. A user can (by intent or
1109 # mistake) pass a string directly, which (if it's the right length)
1110 # will get treated like a pickle, and bypass the normal sanity
1111 # checks in the constructor. This can create insane objects.
1112 # The constructor doesn't want to burn the time to validate all
1113 # fields, but does check the month field. This stops, e.g.,
1114 # datetime.datetime('1995-03-25') from yielding an insane object.
1115 base = '1995-03-25'
1116 if not issubclass(self.theclass, datetime):
1117 base = base[:4]
1118 for month_byte in '9', chr(0), chr(13), '\xff':
1119 self.assertRaises(TypeError, self.theclass,
1120 base[:2] + month_byte + base[3:])
1121 for ord_byte in range(1, 13):
1122 # This shouldn't blow up because of the month byte alone. If
1123 # the implementation changes to do more-careful checking, it may
1124 # blow up because other fields are insane.
1125 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001126
Tim Peters2a799bf2002-12-16 20:18:38 +00001127#############################################################################
1128# datetime tests
1129
Tim Peters604c0132004-06-07 23:04:33 +00001130class SubclassDatetime(datetime):
1131 sub_var = 1
1132
Tim Peters2a799bf2002-12-16 20:18:38 +00001133class TestDateTime(TestDate):
1134
1135 theclass = datetime
1136
1137 def test_basic_attributes(self):
1138 dt = self.theclass(2002, 3, 1, 12, 0)
1139 self.assertEqual(dt.year, 2002)
1140 self.assertEqual(dt.month, 3)
1141 self.assertEqual(dt.day, 1)
1142 self.assertEqual(dt.hour, 12)
1143 self.assertEqual(dt.minute, 0)
1144 self.assertEqual(dt.second, 0)
1145 self.assertEqual(dt.microsecond, 0)
1146
1147 def test_basic_attributes_nonzero(self):
1148 # Make sure all attributes are non-zero so bugs in
1149 # bit-shifting access show up.
1150 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1151 self.assertEqual(dt.year, 2002)
1152 self.assertEqual(dt.month, 3)
1153 self.assertEqual(dt.day, 1)
1154 self.assertEqual(dt.hour, 12)
1155 self.assertEqual(dt.minute, 59)
1156 self.assertEqual(dt.second, 59)
1157 self.assertEqual(dt.microsecond, 8000)
1158
1159 def test_roundtrip(self):
1160 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1161 self.theclass.now()):
1162 # Verify dt -> string -> datetime identity.
1163 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001164 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001165 s = s[9:]
1166 dt2 = eval(s)
1167 self.assertEqual(dt, dt2)
1168
1169 # Verify identity via reconstructing from pieces.
1170 dt2 = self.theclass(dt.year, dt.month, dt.day,
1171 dt.hour, dt.minute, dt.second,
1172 dt.microsecond)
1173 self.assertEqual(dt, dt2)
1174
1175 def test_isoformat(self):
1176 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1177 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1178 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1179 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arc8645a5c2009-12-29 22:03:38 +00001180 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001181 # str is ISO format with the separator forced to a blank.
1182 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1183
1184 t = self.theclass(2, 3, 2)
1185 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1186 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1187 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1188 # str is ISO format with the separator forced to a blank.
1189 self.assertEqual(str(t), "0002-03-02 00:00:00")
1190
Eric Smitha9f7d622008-02-17 19:46:49 +00001191 def test_format(self):
1192 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1193 self.assertEqual(dt.__format__(''), str(dt))
1194
1195 # check that a derived class's __str__() gets called
1196 class A(self.theclass):
1197 def __str__(self):
1198 return 'A'
1199 a = A(2007, 9, 10, 4, 5, 1, 123)
1200 self.assertEqual(a.__format__(''), 'A')
1201
1202 # check that a derived class's strftime gets called
1203 class B(self.theclass):
1204 def strftime(self, format_spec):
1205 return 'B'
1206 b = B(2007, 9, 10, 4, 5, 1, 123)
1207 self.assertEqual(b.__format__(''), str(dt))
1208
1209 for fmt in ["m:%m d:%d y:%y",
1210 "m:%m d:%d y:%y H:%H M:%M S:%S",
1211 "%z %Z",
1212 ]:
1213 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1214 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1215 self.assertEqual(b.__format__(fmt), 'B')
1216
Tim Peters2a799bf2002-12-16 20:18:38 +00001217 def test_more_ctime(self):
1218 # Test fields that TestDate doesn't touch.
1219 import time
1220
1221 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1222 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1223 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1224 # out. The difference is that t.ctime() produces " 2" for the day,
1225 # but platform ctime() produces "02" for the day. According to
1226 # C99, t.ctime() is correct here.
1227 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1228
1229 # So test a case where that difference doesn't matter.
1230 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1231 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1232
1233 def test_tz_independent_comparing(self):
1234 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1235 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1236 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1237 self.assertEqual(dt1, dt3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001238 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001239
1240 # Make sure comparison doesn't forget microseconds, and isn't done
1241 # via comparing a float timestamp (an IEEE double doesn't have enough
1242 # precision to span microsecond resolution across years 1 thru 9999,
1243 # so comparing via timestamp necessarily calls some distinct values
1244 # equal).
1245 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1246 us = timedelta(microseconds=1)
1247 dt2 = dt1 + us
1248 self.assertEqual(dt2 - dt1, us)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001249 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001250
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001251 def test_strftime_with_bad_tzname_replace(self):
1252 # verify ok if tzinfo.tzname().replace() returns a non-string
1253 class MyTzInfo(FixedOffset):
1254 def tzname(self, dt):
1255 class MyStr(str):
1256 def replace(self, *args):
1257 return None
1258 return MyStr('name')
1259 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1260 self.assertRaises(TypeError, t.strftime, '%Z')
1261
Tim Peters2a799bf2002-12-16 20:18:38 +00001262 def test_bad_constructor_arguments(self):
1263 # bad years
1264 self.theclass(MINYEAR, 1, 1) # no exception
1265 self.theclass(MAXYEAR, 1, 1) # no exception
1266 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1267 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1268 # bad months
1269 self.theclass(2000, 1, 1) # no exception
1270 self.theclass(2000, 12, 1) # no exception
1271 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1272 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1273 # bad days
1274 self.theclass(2000, 2, 29) # no exception
1275 self.theclass(2004, 2, 29) # no exception
1276 self.theclass(2400, 2, 29) # no exception
1277 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1278 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1279 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1280 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1281 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1282 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1283 # bad hours
1284 self.theclass(2000, 1, 31, 0) # no exception
1285 self.theclass(2000, 1, 31, 23) # no exception
1286 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1287 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1288 # bad minutes
1289 self.theclass(2000, 1, 31, 23, 0) # no exception
1290 self.theclass(2000, 1, 31, 23, 59) # no exception
1291 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1292 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1293 # bad seconds
1294 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1295 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1296 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1297 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1298 # bad microseconds
1299 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1300 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1301 self.assertRaises(ValueError, self.theclass,
1302 2000, 1, 31, 23, 59, 59, -1)
1303 self.assertRaises(ValueError, self.theclass,
1304 2000, 1, 31, 23, 59, 59,
1305 1000000)
1306
1307 def test_hash_equality(self):
1308 d = self.theclass(2000, 12, 31, 23, 30, 17)
1309 e = self.theclass(2000, 12, 31, 23, 30, 17)
1310 self.assertEqual(d, e)
1311 self.assertEqual(hash(d), hash(e))
1312
1313 dic = {d: 1}
1314 dic[e] = 2
1315 self.assertEqual(len(dic), 1)
1316 self.assertEqual(dic[d], 2)
1317 self.assertEqual(dic[e], 2)
1318
1319 d = self.theclass(2001, 1, 1, 0, 5, 17)
1320 e = self.theclass(2001, 1, 1, 0, 5, 17)
1321 self.assertEqual(d, e)
1322 self.assertEqual(hash(d), hash(e))
1323
1324 dic = {d: 1}
1325 dic[e] = 2
1326 self.assertEqual(len(dic), 1)
1327 self.assertEqual(dic[d], 2)
1328 self.assertEqual(dic[e], 2)
1329
1330 def test_computations(self):
1331 a = self.theclass(2002, 1, 31)
1332 b = self.theclass(1956, 1, 31)
1333 diff = a-b
1334 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1335 self.assertEqual(diff.seconds, 0)
1336 self.assertEqual(diff.microseconds, 0)
1337 a = self.theclass(2002, 3, 2, 17, 6)
1338 millisec = timedelta(0, 0, 1000)
1339 hour = timedelta(0, 3600)
1340 day = timedelta(1)
1341 week = timedelta(7)
1342 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1343 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1344 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1345 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1346 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1347 self.assertEqual(a - hour, a + -hour)
1348 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1349 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1350 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1351 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1352 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1353 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1354 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1355 self.assertEqual((a + week) - a, week)
1356 self.assertEqual((a + day) - a, day)
1357 self.assertEqual((a + hour) - a, hour)
1358 self.assertEqual((a + millisec) - a, millisec)
1359 self.assertEqual((a - week) - a, -week)
1360 self.assertEqual((a - day) - a, -day)
1361 self.assertEqual((a - hour) - a, -hour)
1362 self.assertEqual((a - millisec) - a, -millisec)
1363 self.assertEqual(a - (a + week), -week)
1364 self.assertEqual(a - (a + day), -day)
1365 self.assertEqual(a - (a + hour), -hour)
1366 self.assertEqual(a - (a + millisec), -millisec)
1367 self.assertEqual(a - (a - week), week)
1368 self.assertEqual(a - (a - day), day)
1369 self.assertEqual(a - (a - hour), hour)
1370 self.assertEqual(a - (a - millisec), millisec)
1371 self.assertEqual(a + (week + day + hour + millisec),
1372 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1373 self.assertEqual(a + (week + day + hour + millisec),
1374 (((a + week) + day) + hour) + millisec)
1375 self.assertEqual(a - (week + day + hour + millisec),
1376 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1377 self.assertEqual(a - (week + day + hour + millisec),
1378 (((a - week) - day) - hour) - millisec)
1379 # Add/sub ints, longs, floats should be illegal
1380 for i in 1, 1L, 1.0:
1381 self.assertRaises(TypeError, lambda: a+i)
1382 self.assertRaises(TypeError, lambda: a-i)
1383 self.assertRaises(TypeError, lambda: i+a)
1384 self.assertRaises(TypeError, lambda: i-a)
1385
1386 # delta - datetime is senseless.
1387 self.assertRaises(TypeError, lambda: day - a)
1388 # mixing datetime and (delta or datetime) via * or // is senseless
1389 self.assertRaises(TypeError, lambda: day * a)
1390 self.assertRaises(TypeError, lambda: a * day)
1391 self.assertRaises(TypeError, lambda: day // a)
1392 self.assertRaises(TypeError, lambda: a // day)
1393 self.assertRaises(TypeError, lambda: a * a)
1394 self.assertRaises(TypeError, lambda: a // a)
1395 # datetime + datetime is senseless
1396 self.assertRaises(TypeError, lambda: a + a)
1397
1398 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001399 args = 6, 7, 23, 20, 59, 1, 64**2
1400 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001401 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001402 green = pickler.dumps(orig, proto)
1403 derived = unpickler.loads(green)
1404 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001405
Guido van Rossum275666f2003-02-07 21:49:01 +00001406 def test_more_pickling(self):
1407 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1408 s = pickle.dumps(a)
1409 b = pickle.loads(s)
1410 self.assertEqual(b.year, 2003)
1411 self.assertEqual(b.month, 2)
1412 self.assertEqual(b.day, 7)
1413
Tim Peters604c0132004-06-07 23:04:33 +00001414 def test_pickling_subclass_datetime(self):
1415 args = 6, 7, 23, 20, 59, 1, 64**2
1416 orig = SubclassDatetime(*args)
1417 for pickler, unpickler, proto in pickle_choices:
1418 green = pickler.dumps(orig, proto)
1419 derived = unpickler.loads(green)
1420 self.assertEqual(orig, derived)
1421
Tim Peters2a799bf2002-12-16 20:18:38 +00001422 def test_more_compare(self):
1423 # The test_compare() inherited from TestDate covers the error cases.
1424 # We just want to test lexicographic ordering on the members datetime
1425 # has that date lacks.
1426 args = [2000, 11, 29, 20, 58, 16, 999998]
1427 t1 = self.theclass(*args)
1428 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001429 self.assertTrue(t1 == t2)
1430 self.assertTrue(t1 <= t2)
1431 self.assertTrue(t1 >= t2)
1432 self.assertTrue(not t1 != t2)
1433 self.assertTrue(not t1 < t2)
1434 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001435 self.assertEqual(cmp(t1, t2), 0)
1436 self.assertEqual(cmp(t2, t1), 0)
1437
1438 for i in range(len(args)):
1439 newargs = args[:]
1440 newargs[i] = args[i] + 1
1441 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001442 self.assertTrue(t1 < t2)
1443 self.assertTrue(t2 > t1)
1444 self.assertTrue(t1 <= t2)
1445 self.assertTrue(t2 >= t1)
1446 self.assertTrue(t1 != t2)
1447 self.assertTrue(t2 != t1)
1448 self.assertTrue(not t1 == t2)
1449 self.assertTrue(not t2 == t1)
1450 self.assertTrue(not t1 > t2)
1451 self.assertTrue(not t2 < t1)
1452 self.assertTrue(not t1 >= t2)
1453 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001454 self.assertEqual(cmp(t1, t2), -1)
1455 self.assertEqual(cmp(t2, t1), 1)
1456
1457
1458 # A helper for timestamp constructor tests.
1459 def verify_field_equality(self, expected, got):
1460 self.assertEqual(expected.tm_year, got.year)
1461 self.assertEqual(expected.tm_mon, got.month)
1462 self.assertEqual(expected.tm_mday, got.day)
1463 self.assertEqual(expected.tm_hour, got.hour)
1464 self.assertEqual(expected.tm_min, got.minute)
1465 self.assertEqual(expected.tm_sec, got.second)
1466
1467 def test_fromtimestamp(self):
1468 import time
1469
1470 ts = time.time()
1471 expected = time.localtime(ts)
1472 got = self.theclass.fromtimestamp(ts)
1473 self.verify_field_equality(expected, got)
1474
1475 def test_utcfromtimestamp(self):
1476 import time
1477
1478 ts = time.time()
1479 expected = time.gmtime(ts)
1480 got = self.theclass.utcfromtimestamp(ts)
1481 self.verify_field_equality(expected, got)
1482
Georg Brandl6d78a582006-04-28 19:09:24 +00001483 def test_microsecond_rounding(self):
1484 # Test whether fromtimestamp "rounds up" floats that are less
1485 # than one microsecond smaller than an integer.
1486 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1487 self.theclass.fromtimestamp(1))
1488
Tim Peters1b6f7a92004-06-20 02:50:16 +00001489 def test_insane_fromtimestamp(self):
1490 # It's possible that some platform maps time_t to double,
1491 # and that this test will fail there. This test should
1492 # exempt such platforms (provided they return reasonable
1493 # results!).
1494 for insane in -1e200, 1e200:
1495 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1496 insane)
1497
1498 def test_insane_utcfromtimestamp(self):
1499 # It's possible that some platform maps time_t to double,
1500 # and that this test will fail there. This test should
1501 # exempt such platforms (provided they return reasonable
1502 # results!).
1503 for insane in -1e200, 1e200:
1504 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1505 insane)
1506
Guido van Rossum2054ee92007-03-06 15:50:01 +00001507 def test_negative_float_fromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001508 # Windows doesn't accept negative timestamps
1509 if os.name == "nt":
1510 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001511 # The result is tz-dependent; at least test that this doesn't
1512 # fail (like it did before bug 1646728 was fixed).
1513 self.theclass.fromtimestamp(-1.05)
1514
1515 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001516 # Windows doesn't accept negative timestamps
1517 if os.name == "nt":
1518 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001519 d = self.theclass.utcfromtimestamp(-1.05)
1520 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1521
Tim Peters2a799bf2002-12-16 20:18:38 +00001522 def test_utcnow(self):
1523 import time
1524
1525 # Call it a success if utcnow() and utcfromtimestamp() are within
1526 # a second of each other.
1527 tolerance = timedelta(seconds=1)
1528 for dummy in range(3):
1529 from_now = self.theclass.utcnow()
1530 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1531 if abs(from_timestamp - from_now) <= tolerance:
1532 break
1533 # Else try again a few times.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001534 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001535
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001536 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001537 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001538
Skip Montanarofc070d22008-03-15 16:04:45 +00001539 string = '2004-12-01 13:02:47.197'
1540 format = '%Y-%m-%d %H:%M:%S.%f'
1541 result, frac = _strptime._strptime(string, format)
1542 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001543 got = self.theclass.strptime(string, format)
1544 self.assertEqual(expected, got)
1545
Tim Peters2a799bf2002-12-16 20:18:38 +00001546 def test_more_timetuple(self):
1547 # This tests fields beyond those tested by the TestDate.test_timetuple.
1548 t = self.theclass(2004, 12, 31, 6, 22, 33)
1549 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1550 self.assertEqual(t.timetuple(),
1551 (t.year, t.month, t.day,
1552 t.hour, t.minute, t.second,
1553 t.weekday(),
1554 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1555 -1))
1556 tt = t.timetuple()
1557 self.assertEqual(tt.tm_year, t.year)
1558 self.assertEqual(tt.tm_mon, t.month)
1559 self.assertEqual(tt.tm_mday, t.day)
1560 self.assertEqual(tt.tm_hour, t.hour)
1561 self.assertEqual(tt.tm_min, t.minute)
1562 self.assertEqual(tt.tm_sec, t.second)
1563 self.assertEqual(tt.tm_wday, t.weekday())
1564 self.assertEqual(tt.tm_yday, t.toordinal() -
1565 date(t.year, 1, 1).toordinal() + 1)
1566 self.assertEqual(tt.tm_isdst, -1)
1567
1568 def test_more_strftime(self):
1569 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001570 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1571 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1572 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001573
1574 def test_extract(self):
1575 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1576 self.assertEqual(dt.date(), date(2002, 3, 4))
1577 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1578
1579 def test_combine(self):
1580 d = date(2002, 3, 4)
1581 t = time(18, 45, 3, 1234)
1582 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1583 combine = self.theclass.combine
1584 dt = combine(d, t)
1585 self.assertEqual(dt, expected)
1586
1587 dt = combine(time=t, date=d)
1588 self.assertEqual(dt, expected)
1589
1590 self.assertEqual(d, dt.date())
1591 self.assertEqual(t, dt.time())
1592 self.assertEqual(dt, combine(dt.date(), dt.time()))
1593
1594 self.assertRaises(TypeError, combine) # need an arg
1595 self.assertRaises(TypeError, combine, d) # need two args
1596 self.assertRaises(TypeError, combine, t, d) # args reversed
1597 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1598 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1599
Tim Peters12bf3392002-12-24 05:41:27 +00001600 def test_replace(self):
1601 cls = self.theclass
1602 args = [1, 2, 3, 4, 5, 6, 7]
1603 base = cls(*args)
1604 self.assertEqual(base, base.replace())
1605
1606 i = 0
1607 for name, newval in (("year", 2),
1608 ("month", 3),
1609 ("day", 4),
1610 ("hour", 5),
1611 ("minute", 6),
1612 ("second", 7),
1613 ("microsecond", 8)):
1614 newargs = args[:]
1615 newargs[i] = newval
1616 expected = cls(*newargs)
1617 got = base.replace(**{name: newval})
1618 self.assertEqual(expected, got)
1619 i += 1
1620
1621 # Out of bounds.
1622 base = cls(2000, 2, 29)
1623 self.assertRaises(ValueError, base.replace, year=2001)
1624
Tim Peters80475bb2002-12-25 07:40:55 +00001625 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001626 # Pretty boring! The TZ test is more interesting here. astimezone()
1627 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001628 dt = self.theclass.now()
1629 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001630 self.assertRaises(TypeError, dt.astimezone) # not enough args
1631 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1632 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001633 self.assertRaises(ValueError, dt.astimezone, f) # naive
1634 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001635
Tim Peters52dcce22003-01-23 16:36:11 +00001636 class Bogus(tzinfo):
1637 def utcoffset(self, dt): return None
1638 def dst(self, dt): return timedelta(0)
1639 bog = Bogus()
1640 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1641
1642 class AlsoBogus(tzinfo):
1643 def utcoffset(self, dt): return timedelta(0)
1644 def dst(self, dt): return None
1645 alsobog = AlsoBogus()
1646 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001647
Tim Petersa98924a2003-05-17 05:55:19 +00001648 def test_subclass_datetime(self):
1649
1650 class C(self.theclass):
1651 theAnswer = 42
1652
1653 def __new__(cls, *args, **kws):
1654 temp = kws.copy()
1655 extra = temp.pop('extra')
1656 result = self.theclass.__new__(cls, *args, **temp)
1657 result.extra = extra
1658 return result
1659
1660 def newmeth(self, start):
1661 return start + self.year + self.month + self.second
1662
1663 args = 2003, 4, 14, 12, 13, 41
1664
1665 dt1 = self.theclass(*args)
1666 dt2 = C(*args, **{'extra': 7})
1667
1668 self.assertEqual(dt2.__class__, C)
1669 self.assertEqual(dt2.theAnswer, 42)
1670 self.assertEqual(dt2.extra, 7)
1671 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1672 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1673 dt1.second - 7)
1674
Tim Peters604c0132004-06-07 23:04:33 +00001675class SubclassTime(time):
1676 sub_var = 1
1677
Collin Winterc2898c52007-04-25 17:29:52 +00001678class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001679
1680 theclass = time
1681
1682 def test_basic_attributes(self):
1683 t = self.theclass(12, 0)
1684 self.assertEqual(t.hour, 12)
1685 self.assertEqual(t.minute, 0)
1686 self.assertEqual(t.second, 0)
1687 self.assertEqual(t.microsecond, 0)
1688
1689 def test_basic_attributes_nonzero(self):
1690 # Make sure all attributes are non-zero so bugs in
1691 # bit-shifting access show up.
1692 t = self.theclass(12, 59, 59, 8000)
1693 self.assertEqual(t.hour, 12)
1694 self.assertEqual(t.minute, 59)
1695 self.assertEqual(t.second, 59)
1696 self.assertEqual(t.microsecond, 8000)
1697
1698 def test_roundtrip(self):
1699 t = self.theclass(1, 2, 3, 4)
1700
1701 # Verify t -> string -> time identity.
1702 s = repr(t)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001703 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001704 s = s[9:]
1705 t2 = eval(s)
1706 self.assertEqual(t, t2)
1707
1708 # Verify identity via reconstructing from pieces.
1709 t2 = self.theclass(t.hour, t.minute, t.second,
1710 t.microsecond)
1711 self.assertEqual(t, t2)
1712
1713 def test_comparing(self):
1714 args = [1, 2, 3, 4]
1715 t1 = self.theclass(*args)
1716 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001717 self.assertTrue(t1 == t2)
1718 self.assertTrue(t1 <= t2)
1719 self.assertTrue(t1 >= t2)
1720 self.assertTrue(not t1 != t2)
1721 self.assertTrue(not t1 < t2)
1722 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001723 self.assertEqual(cmp(t1, t2), 0)
1724 self.assertEqual(cmp(t2, t1), 0)
1725
1726 for i in range(len(args)):
1727 newargs = args[:]
1728 newargs[i] = args[i] + 1
1729 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001730 self.assertTrue(t1 < t2)
1731 self.assertTrue(t2 > t1)
1732 self.assertTrue(t1 <= t2)
1733 self.assertTrue(t2 >= t1)
1734 self.assertTrue(t1 != t2)
1735 self.assertTrue(t2 != t1)
1736 self.assertTrue(not t1 == t2)
1737 self.assertTrue(not t2 == t1)
1738 self.assertTrue(not t1 > t2)
1739 self.assertTrue(not t2 < t1)
1740 self.assertTrue(not t1 >= t2)
1741 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001742 self.assertEqual(cmp(t1, t2), -1)
1743 self.assertEqual(cmp(t2, t1), 1)
1744
Tim Peters68124bb2003-02-08 03:46:31 +00001745 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001746 self.assertEqual(t1 == badarg, False)
1747 self.assertEqual(t1 != badarg, True)
1748 self.assertEqual(badarg == t1, False)
1749 self.assertEqual(badarg != t1, True)
1750
Tim Peters2a799bf2002-12-16 20:18:38 +00001751 self.assertRaises(TypeError, lambda: t1 <= badarg)
1752 self.assertRaises(TypeError, lambda: t1 < badarg)
1753 self.assertRaises(TypeError, lambda: t1 > badarg)
1754 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001755 self.assertRaises(TypeError, lambda: badarg <= t1)
1756 self.assertRaises(TypeError, lambda: badarg < t1)
1757 self.assertRaises(TypeError, lambda: badarg > t1)
1758 self.assertRaises(TypeError, lambda: badarg >= t1)
1759
1760 def test_bad_constructor_arguments(self):
1761 # bad hours
1762 self.theclass(0, 0) # no exception
1763 self.theclass(23, 0) # no exception
1764 self.assertRaises(ValueError, self.theclass, -1, 0)
1765 self.assertRaises(ValueError, self.theclass, 24, 0)
1766 # bad minutes
1767 self.theclass(23, 0) # no exception
1768 self.theclass(23, 59) # no exception
1769 self.assertRaises(ValueError, self.theclass, 23, -1)
1770 self.assertRaises(ValueError, self.theclass, 23, 60)
1771 # bad seconds
1772 self.theclass(23, 59, 0) # no exception
1773 self.theclass(23, 59, 59) # no exception
1774 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1775 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1776 # bad microseconds
1777 self.theclass(23, 59, 59, 0) # no exception
1778 self.theclass(23, 59, 59, 999999) # no exception
1779 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1780 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1781
1782 def test_hash_equality(self):
1783 d = self.theclass(23, 30, 17)
1784 e = self.theclass(23, 30, 17)
1785 self.assertEqual(d, e)
1786 self.assertEqual(hash(d), hash(e))
1787
1788 dic = {d: 1}
1789 dic[e] = 2
1790 self.assertEqual(len(dic), 1)
1791 self.assertEqual(dic[d], 2)
1792 self.assertEqual(dic[e], 2)
1793
1794 d = self.theclass(0, 5, 17)
1795 e = self.theclass(0, 5, 17)
1796 self.assertEqual(d, e)
1797 self.assertEqual(hash(d), hash(e))
1798
1799 dic = {d: 1}
1800 dic[e] = 2
1801 self.assertEqual(len(dic), 1)
1802 self.assertEqual(dic[d], 2)
1803 self.assertEqual(dic[e], 2)
1804
1805 def test_isoformat(self):
1806 t = self.theclass(4, 5, 1, 123)
1807 self.assertEqual(t.isoformat(), "04:05:01.000123")
1808 self.assertEqual(t.isoformat(), str(t))
1809
1810 t = self.theclass()
1811 self.assertEqual(t.isoformat(), "00:00:00")
1812 self.assertEqual(t.isoformat(), str(t))
1813
1814 t = self.theclass(microsecond=1)
1815 self.assertEqual(t.isoformat(), "00:00:00.000001")
1816 self.assertEqual(t.isoformat(), str(t))
1817
1818 t = self.theclass(microsecond=10)
1819 self.assertEqual(t.isoformat(), "00:00:00.000010")
1820 self.assertEqual(t.isoformat(), str(t))
1821
1822 t = self.theclass(microsecond=100)
1823 self.assertEqual(t.isoformat(), "00:00:00.000100")
1824 self.assertEqual(t.isoformat(), str(t))
1825
1826 t = self.theclass(microsecond=1000)
1827 self.assertEqual(t.isoformat(), "00:00:00.001000")
1828 self.assertEqual(t.isoformat(), str(t))
1829
1830 t = self.theclass(microsecond=10000)
1831 self.assertEqual(t.isoformat(), "00:00:00.010000")
1832 self.assertEqual(t.isoformat(), str(t))
1833
1834 t = self.theclass(microsecond=100000)
1835 self.assertEqual(t.isoformat(), "00:00:00.100000")
1836 self.assertEqual(t.isoformat(), str(t))
1837
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001838 def test_1653736(self):
1839 # verify it doesn't accept extra keyword arguments
1840 t = self.theclass(second=1)
1841 self.assertRaises(TypeError, t.isoformat, foo=3)
1842
Tim Peters2a799bf2002-12-16 20:18:38 +00001843 def test_strftime(self):
1844 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001845 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001846 # A naive object replaces %z and %Z with empty strings.
1847 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1848
Eric Smitha9f7d622008-02-17 19:46:49 +00001849 def test_format(self):
1850 t = self.theclass(1, 2, 3, 4)
1851 self.assertEqual(t.__format__(''), str(t))
1852
1853 # check that a derived class's __str__() gets called
1854 class A(self.theclass):
1855 def __str__(self):
1856 return 'A'
1857 a = A(1, 2, 3, 4)
1858 self.assertEqual(a.__format__(''), 'A')
1859
1860 # check that a derived class's strftime gets called
1861 class B(self.theclass):
1862 def strftime(self, format_spec):
1863 return 'B'
1864 b = B(1, 2, 3, 4)
1865 self.assertEqual(b.__format__(''), str(t))
1866
1867 for fmt in ['%H %M %S',
1868 ]:
1869 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1870 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1871 self.assertEqual(b.__format__(fmt), 'B')
1872
Tim Peters2a799bf2002-12-16 20:18:38 +00001873 def test_str(self):
1874 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1875 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1876 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1877 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1878 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1879
1880 def test_repr(self):
1881 name = 'datetime.' + self.theclass.__name__
1882 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1883 "%s(1, 2, 3, 4)" % name)
1884 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1885 "%s(10, 2, 3, 4000)" % name)
1886 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1887 "%s(0, 2, 3, 400000)" % name)
1888 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1889 "%s(12, 2, 3)" % name)
1890 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1891 "%s(23, 15)" % name)
1892
1893 def test_resolution_info(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +00001894 self.assertIsInstance(self.theclass.min, self.theclass)
1895 self.assertIsInstance(self.theclass.max, self.theclass)
1896 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001897 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001898
1899 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001900 args = 20, 59, 16, 64**2
1901 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001902 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001903 green = pickler.dumps(orig, proto)
1904 derived = unpickler.loads(green)
1905 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001906
Tim Peters604c0132004-06-07 23:04:33 +00001907 def test_pickling_subclass_time(self):
1908 args = 20, 59, 16, 64**2
1909 orig = SubclassTime(*args)
1910 for pickler, unpickler, proto in pickle_choices:
1911 green = pickler.dumps(orig, proto)
1912 derived = unpickler.loads(green)
1913 self.assertEqual(orig, derived)
1914
Tim Peters2a799bf2002-12-16 20:18:38 +00001915 def test_bool(self):
1916 cls = self.theclass
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001917 self.assertTrue(cls(1))
1918 self.assertTrue(cls(0, 1))
1919 self.assertTrue(cls(0, 0, 1))
1920 self.assertTrue(cls(0, 0, 0, 1))
1921 self.assertTrue(not cls(0))
1922 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001923
Tim Peters12bf3392002-12-24 05:41:27 +00001924 def test_replace(self):
1925 cls = self.theclass
1926 args = [1, 2, 3, 4]
1927 base = cls(*args)
1928 self.assertEqual(base, base.replace())
1929
1930 i = 0
1931 for name, newval in (("hour", 5),
1932 ("minute", 6),
1933 ("second", 7),
1934 ("microsecond", 8)):
1935 newargs = args[:]
1936 newargs[i] = newval
1937 expected = cls(*newargs)
1938 got = base.replace(**{name: newval})
1939 self.assertEqual(expected, got)
1940 i += 1
1941
1942 # Out of bounds.
1943 base = cls(1)
1944 self.assertRaises(ValueError, base.replace, hour=24)
1945 self.assertRaises(ValueError, base.replace, minute=-1)
1946 self.assertRaises(ValueError, base.replace, second=100)
1947 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1948
Tim Petersa98924a2003-05-17 05:55:19 +00001949 def test_subclass_time(self):
1950
1951 class C(self.theclass):
1952 theAnswer = 42
1953
1954 def __new__(cls, *args, **kws):
1955 temp = kws.copy()
1956 extra = temp.pop('extra')
1957 result = self.theclass.__new__(cls, *args, **temp)
1958 result.extra = extra
1959 return result
1960
1961 def newmeth(self, start):
1962 return start + self.hour + self.second
1963
1964 args = 4, 5, 6
1965
1966 dt1 = self.theclass(*args)
1967 dt2 = C(*args, **{'extra': 7})
1968
1969 self.assertEqual(dt2.__class__, C)
1970 self.assertEqual(dt2.theAnswer, 42)
1971 self.assertEqual(dt2.extra, 7)
1972 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1973 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1974
Armin Rigof4afb212005-11-07 07:15:48 +00001975 def test_backdoor_resistance(self):
1976 # see TestDate.test_backdoor_resistance().
1977 base = '2:59.0'
1978 for hour_byte in ' ', '9', chr(24), '\xff':
1979 self.assertRaises(TypeError, self.theclass,
1980 hour_byte + base[1:])
1981
Tim Peters855fe882002-12-22 03:43:39 +00001982# A mixin for classes with a tzinfo= argument. Subclasses must define
1983# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001984# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001985class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001986
Tim Petersbad8ff02002-12-30 20:52:32 +00001987 def test_argument_passing(self):
1988 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001989 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001990 class introspective(tzinfo):
1991 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001992 def utcoffset(self, dt):
1993 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001994 dst = utcoffset
1995
1996 obj = cls(1, 2, 3, tzinfo=introspective())
1997
Tim Peters0bf60bd2003-01-08 20:40:01 +00001998 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001999 self.assertEqual(obj.tzname(), expected)
2000
Tim Peters0bf60bd2003-01-08 20:40:01 +00002001 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002002 self.assertEqual(obj.utcoffset(), expected)
2003 self.assertEqual(obj.dst(), expected)
2004
Tim Peters855fe882002-12-22 03:43:39 +00002005 def test_bad_tzinfo_classes(self):
2006 cls = self.theclass
2007 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002008
Tim Peters855fe882002-12-22 03:43:39 +00002009 class NiceTry(object):
2010 def __init__(self): pass
2011 def utcoffset(self, dt): pass
2012 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2013
2014 class BetterTry(tzinfo):
2015 def __init__(self): pass
2016 def utcoffset(self, dt): pass
2017 b = BetterTry()
2018 t = cls(1, 1, 1, tzinfo=b)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002019 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002020
2021 def test_utc_offset_out_of_bounds(self):
2022 class Edgy(tzinfo):
2023 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002024 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002025 def utcoffset(self, dt):
2026 return self.offset
2027
2028 cls = self.theclass
2029 for offset, legit in ((-1440, False),
2030 (-1439, True),
2031 (1439, True),
2032 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002033 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002034 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002035 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002036 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002037 else:
2038 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002039 if legit:
2040 aofs = abs(offset)
2041 h, m = divmod(aofs, 60)
2042 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002043 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002044 t = t.timetz()
2045 self.assertEqual(str(t), "01:02:03" + tag)
2046 else:
2047 self.assertRaises(ValueError, str, t)
2048
2049 def test_tzinfo_classes(self):
2050 cls = self.theclass
2051 class C1(tzinfo):
2052 def utcoffset(self, dt): return None
2053 def dst(self, dt): return None
2054 def tzname(self, dt): return None
2055 for t in (cls(1, 1, 1),
2056 cls(1, 1, 1, tzinfo=None),
2057 cls(1, 1, 1, tzinfo=C1())):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002058 self.assertTrue(t.utcoffset() is None)
2059 self.assertTrue(t.dst() is None)
2060 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002061
Tim Peters855fe882002-12-22 03:43:39 +00002062 class C3(tzinfo):
2063 def utcoffset(self, dt): return timedelta(minutes=-1439)
2064 def dst(self, dt): return timedelta(minutes=1439)
2065 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002066 t = cls(1, 1, 1, tzinfo=C3())
2067 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2068 self.assertEqual(t.dst(), timedelta(minutes=1439))
2069 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002070
2071 # Wrong types.
2072 class C4(tzinfo):
2073 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002074 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002075 def tzname(self, dt): return 0
2076 t = cls(1, 1, 1, tzinfo=C4())
2077 self.assertRaises(TypeError, t.utcoffset)
2078 self.assertRaises(TypeError, t.dst)
2079 self.assertRaises(TypeError, t.tzname)
2080
2081 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002082 class C6(tzinfo):
2083 def utcoffset(self, dt): return timedelta(hours=-24)
2084 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002085 t = cls(1, 1, 1, tzinfo=C6())
2086 self.assertRaises(ValueError, t.utcoffset)
2087 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002088
2089 # Not a whole number of minutes.
2090 class C7(tzinfo):
2091 def utcoffset(self, dt): return timedelta(seconds=61)
2092 def dst(self, dt): return timedelta(microseconds=-81)
2093 t = cls(1, 1, 1, tzinfo=C7())
2094 self.assertRaises(ValueError, t.utcoffset)
2095 self.assertRaises(ValueError, t.dst)
2096
Tim Peters4c0db782002-12-26 05:01:19 +00002097 def test_aware_compare(self):
2098 cls = self.theclass
2099
Tim Peters60c76e42002-12-27 00:41:11 +00002100 # Ensure that utcoffset() gets ignored if the comparands have
2101 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002102 class OperandDependentOffset(tzinfo):
2103 def utcoffset(self, t):
2104 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002105 # d0 and d1 equal after adjustment
2106 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002107 else:
Tim Peters397301e2003-01-02 21:28:08 +00002108 # d2 off in the weeds
2109 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002110
2111 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2112 d0 = base.replace(minute=3)
2113 d1 = base.replace(minute=9)
2114 d2 = base.replace(minute=11)
2115 for x in d0, d1, d2:
2116 for y in d0, d1, d2:
2117 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002118 expected = cmp(x.minute, y.minute)
2119 self.assertEqual(got, expected)
2120
2121 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002122 # Note that a time can't actually have an operand-depedent offset,
2123 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2124 # so skip this test for time.
2125 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002126 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2127 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2128 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2129 for x in d0, d1, d2:
2130 for y in d0, d1, d2:
2131 got = cmp(x, y)
2132 if (x is d0 or x is d1) and (y is d0 or y is d1):
2133 expected = 0
2134 elif x is y is d2:
2135 expected = 0
2136 elif x is d2:
2137 expected = -1
2138 else:
2139 assert y is d2
2140 expected = 1
2141 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002142
Tim Peters855fe882002-12-22 03:43:39 +00002143
Tim Peters0bf60bd2003-01-08 20:40:01 +00002144# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002145class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002146 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002147
2148 def test_empty(self):
2149 t = self.theclass()
2150 self.assertEqual(t.hour, 0)
2151 self.assertEqual(t.minute, 0)
2152 self.assertEqual(t.second, 0)
2153 self.assertEqual(t.microsecond, 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002154 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002155
Tim Peters2a799bf2002-12-16 20:18:38 +00002156 def test_zones(self):
2157 est = FixedOffset(-300, "EST", 1)
2158 utc = FixedOffset(0, "UTC", -2)
2159 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002160 t1 = time( 7, 47, tzinfo=est)
2161 t2 = time(12, 47, tzinfo=utc)
2162 t3 = time(13, 47, tzinfo=met)
2163 t4 = time(microsecond=40)
2164 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002165
2166 self.assertEqual(t1.tzinfo, est)
2167 self.assertEqual(t2.tzinfo, utc)
2168 self.assertEqual(t3.tzinfo, met)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002169 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002170 self.assertEqual(t5.tzinfo, utc)
2171
Tim Peters855fe882002-12-22 03:43:39 +00002172 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2173 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2174 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002175 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002176 self.assertRaises(TypeError, t1.utcoffset, "no args")
2177
2178 self.assertEqual(t1.tzname(), "EST")
2179 self.assertEqual(t2.tzname(), "UTC")
2180 self.assertEqual(t3.tzname(), "MET")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002181 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002182 self.assertRaises(TypeError, t1.tzname, "no args")
2183
Tim Peters855fe882002-12-22 03:43:39 +00002184 self.assertEqual(t1.dst(), timedelta(minutes=1))
2185 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2186 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002187 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002188 self.assertRaises(TypeError, t1.dst, "no args")
2189
2190 self.assertEqual(hash(t1), hash(t2))
2191 self.assertEqual(hash(t1), hash(t3))
2192 self.assertEqual(hash(t2), hash(t3))
2193
2194 self.assertEqual(t1, t2)
2195 self.assertEqual(t1, t3)
2196 self.assertEqual(t2, t3)
2197 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2198 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2199 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2200
2201 self.assertEqual(str(t1), "07:47:00-05:00")
2202 self.assertEqual(str(t2), "12:47:00+00:00")
2203 self.assertEqual(str(t3), "13:47:00+01:00")
2204 self.assertEqual(str(t4), "00:00:00.000040")
2205 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2206
2207 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2208 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2209 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2210 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2211 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2212
Tim Peters0bf60bd2003-01-08 20:40:01 +00002213 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002214 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2215 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2216 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2217 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2218 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2219
2220 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2221 "07:47:00 %Z=EST %z=-0500")
2222 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2223 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2224
2225 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002226 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002227 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2228 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2229
Tim Petersb92bb712002-12-21 17:44:07 +00002230 # Check that an invalid tzname result raises an exception.
2231 class Badtzname(tzinfo):
2232 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002233 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002234 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2235 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002236
2237 def test_hash_edge_cases(self):
2238 # Offsets that overflow a basic time.
2239 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2240 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2241 self.assertEqual(hash(t1), hash(t2))
2242
2243 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2244 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2245 self.assertEqual(hash(t1), hash(t2))
2246
Tim Peters2a799bf2002-12-16 20:18:38 +00002247 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002248 # Try one without a tzinfo.
2249 args = 20, 59, 16, 64**2
2250 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002251 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002252 green = pickler.dumps(orig, proto)
2253 derived = unpickler.loads(green)
2254 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002255
2256 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002257 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002258 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002259 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002260 green = pickler.dumps(orig, proto)
2261 derived = unpickler.loads(green)
2262 self.assertEqual(orig, derived)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002263 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002264 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2265 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002266
2267 def test_more_bool(self):
2268 # Test cases with non-None tzinfo.
2269 cls = self.theclass
2270
2271 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002272 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002273
2274 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002275 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002276
2277 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002278 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002279
2280 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002281 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002282
2283 # Mostly ensuring this doesn't overflow internally.
2284 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002285 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002286
2287 # But this should yield a value error -- the utcoffset is bogus.
2288 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2289 self.assertRaises(ValueError, lambda: bool(t))
2290
2291 # Likewise.
2292 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2293 self.assertRaises(ValueError, lambda: bool(t))
2294
Tim Peters12bf3392002-12-24 05:41:27 +00002295 def test_replace(self):
2296 cls = self.theclass
2297 z100 = FixedOffset(100, "+100")
2298 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2299 args = [1, 2, 3, 4, z100]
2300 base = cls(*args)
2301 self.assertEqual(base, base.replace())
2302
2303 i = 0
2304 for name, newval in (("hour", 5),
2305 ("minute", 6),
2306 ("second", 7),
2307 ("microsecond", 8),
2308 ("tzinfo", zm200)):
2309 newargs = args[:]
2310 newargs[i] = newval
2311 expected = cls(*newargs)
2312 got = base.replace(**{name: newval})
2313 self.assertEqual(expected, got)
2314 i += 1
2315
2316 # Ensure we can get rid of a tzinfo.
2317 self.assertEqual(base.tzname(), "+100")
2318 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002319 self.assertTrue(base2.tzinfo is None)
2320 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002321
2322 # Ensure we can add one.
2323 base3 = base2.replace(tzinfo=z100)
2324 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002325 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002326
2327 # Out of bounds.
2328 base = cls(1)
2329 self.assertRaises(ValueError, base.replace, hour=24)
2330 self.assertRaises(ValueError, base.replace, minute=-1)
2331 self.assertRaises(ValueError, base.replace, second=100)
2332 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2333
Tim Peters60c76e42002-12-27 00:41:11 +00002334 def test_mixed_compare(self):
2335 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002336 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002337 self.assertEqual(t1, t2)
2338 t2 = t2.replace(tzinfo=None)
2339 self.assertEqual(t1, t2)
2340 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2341 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002342 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2343 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002344
Tim Peters0bf60bd2003-01-08 20:40:01 +00002345 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002346 class Varies(tzinfo):
2347 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002348 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002349 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002350 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002351 return self.offset
2352
2353 v = Varies()
2354 t1 = t2.replace(tzinfo=v)
2355 t2 = t2.replace(tzinfo=v)
2356 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2357 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2358 self.assertEqual(t1, t2)
2359
2360 # But if they're not identical, it isn't ignored.
2361 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002362 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002363
Tim Petersa98924a2003-05-17 05:55:19 +00002364 def test_subclass_timetz(self):
2365
2366 class C(self.theclass):
2367 theAnswer = 42
2368
2369 def __new__(cls, *args, **kws):
2370 temp = kws.copy()
2371 extra = temp.pop('extra')
2372 result = self.theclass.__new__(cls, *args, **temp)
2373 result.extra = extra
2374 return result
2375
2376 def newmeth(self, start):
2377 return start + self.hour + self.second
2378
2379 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2380
2381 dt1 = self.theclass(*args)
2382 dt2 = C(*args, **{'extra': 7})
2383
2384 self.assertEqual(dt2.__class__, C)
2385 self.assertEqual(dt2.theAnswer, 42)
2386 self.assertEqual(dt2.extra, 7)
2387 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2388 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2389
Tim Peters4c0db782002-12-26 05:01:19 +00002390
Tim Peters0bf60bd2003-01-08 20:40:01 +00002391# Testing datetime objects with a non-None tzinfo.
2392
Collin Winterc2898c52007-04-25 17:29:52 +00002393class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002394 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002395
2396 def test_trivial(self):
2397 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2398 self.assertEqual(dt.year, 1)
2399 self.assertEqual(dt.month, 2)
2400 self.assertEqual(dt.day, 3)
2401 self.assertEqual(dt.hour, 4)
2402 self.assertEqual(dt.minute, 5)
2403 self.assertEqual(dt.second, 6)
2404 self.assertEqual(dt.microsecond, 7)
2405 self.assertEqual(dt.tzinfo, None)
2406
2407 def test_even_more_compare(self):
2408 # The test_compare() and test_more_compare() inherited from TestDate
2409 # and TestDateTime covered non-tzinfo cases.
2410
2411 # Smallest possible after UTC adjustment.
2412 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2413 # Largest possible after UTC adjustment.
2414 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2415 tzinfo=FixedOffset(-1439, ""))
2416
2417 # Make sure those compare correctly, and w/o overflow.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002418 self.assertTrue(t1 < t2)
2419 self.assertTrue(t1 != t2)
2420 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002421
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002422 self.assertTrue(t1 == t1)
2423 self.assertTrue(t2 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002424
2425 # Equal afer adjustment.
2426 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2427 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2428 self.assertEqual(t1, t2)
2429
2430 # Change t1 not to subtract a minute, and t1 should be larger.
2431 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002432 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002433
2434 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2435 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002436 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002437
2438 # Back to the original t1, but make seconds resolve it.
2439 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2440 second=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002441 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002442
2443 # Likewise, but make microseconds resolve it.
2444 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2445 microsecond=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002446 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002447
2448 # Make t2 naive and it should fail.
2449 t2 = self.theclass.min
2450 self.assertRaises(TypeError, lambda: t1 == t2)
2451 self.assertEqual(t2, t2)
2452
2453 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2454 class Naive(tzinfo):
2455 def utcoffset(self, dt): return None
2456 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2457 self.assertRaises(TypeError, lambda: t1 == t2)
2458 self.assertEqual(t2, t2)
2459
2460 # OTOH, it's OK to compare two of these mixing the two ways of being
2461 # naive.
2462 t1 = self.theclass(5, 6, 7)
2463 self.assertEqual(t1, t2)
2464
2465 # Try a bogus uctoffset.
2466 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002467 def utcoffset(self, dt):
2468 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002469 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2470 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002471 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002472
Tim Peters2a799bf2002-12-16 20:18:38 +00002473 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002474 # Try one without a tzinfo.
2475 args = 6, 7, 23, 20, 59, 1, 64**2
2476 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002477 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002478 green = pickler.dumps(orig, proto)
2479 derived = unpickler.loads(green)
2480 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002481
2482 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002483 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002484 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002485 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002486 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002487 green = pickler.dumps(orig, proto)
2488 derived = unpickler.loads(green)
2489 self.assertEqual(orig, derived)
Ezio Melottib0f5adc2010-01-24 16:58:36 +00002490 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002491 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2492 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002493
2494 def test_extreme_hashes(self):
2495 # If an attempt is made to hash these via subtracting the offset
2496 # then hashing a datetime object, OverflowError results. The
2497 # Python implementation used to blow up here.
2498 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2499 hash(t)
2500 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2501 tzinfo=FixedOffset(-1439, ""))
2502 hash(t)
2503
2504 # OTOH, an OOB offset should blow up.
2505 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2506 self.assertRaises(ValueError, hash, t)
2507
2508 def test_zones(self):
2509 est = FixedOffset(-300, "EST")
2510 utc = FixedOffset(0, "UTC")
2511 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002512 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2513 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2514 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002515 self.assertEqual(t1.tzinfo, est)
2516 self.assertEqual(t2.tzinfo, utc)
2517 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002518 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2519 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2520 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002521 self.assertEqual(t1.tzname(), "EST")
2522 self.assertEqual(t2.tzname(), "UTC")
2523 self.assertEqual(t3.tzname(), "MET")
2524 self.assertEqual(hash(t1), hash(t2))
2525 self.assertEqual(hash(t1), hash(t3))
2526 self.assertEqual(hash(t2), hash(t3))
2527 self.assertEqual(t1, t2)
2528 self.assertEqual(t1, t3)
2529 self.assertEqual(t2, t3)
2530 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2531 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2532 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002533 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002534 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2535 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2536 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2537
2538 def test_combine(self):
2539 met = FixedOffset(60, "MET")
2540 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002541 tz = time(18, 45, 3, 1234, tzinfo=met)
2542 dt = datetime.combine(d, tz)
2543 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002544 tzinfo=met))
2545
2546 def test_extract(self):
2547 met = FixedOffset(60, "MET")
2548 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2549 self.assertEqual(dt.date(), date(2002, 3, 4))
2550 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002551 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002552
2553 def test_tz_aware_arithmetic(self):
2554 import random
2555
2556 now = self.theclass.now()
2557 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002558 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002559 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002560 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002561 self.assertEqual(nowaware.timetz(), timeaware)
2562
2563 # Can't mix aware and non-aware.
2564 self.assertRaises(TypeError, lambda: now - nowaware)
2565 self.assertRaises(TypeError, lambda: nowaware - now)
2566
Tim Peters0bf60bd2003-01-08 20:40:01 +00002567 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002568 self.assertRaises(TypeError, lambda: now + nowaware)
2569 self.assertRaises(TypeError, lambda: nowaware + now)
2570 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2571
2572 # Subtracting should yield 0.
2573 self.assertEqual(now - now, timedelta(0))
2574 self.assertEqual(nowaware - nowaware, timedelta(0))
2575
2576 # Adding a delta should preserve tzinfo.
2577 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2578 nowawareplus = nowaware + delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002579 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002580 nowawareplus2 = delta + nowaware
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002581 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002582 self.assertEqual(nowawareplus, nowawareplus2)
2583
2584 # that - delta should be what we started with, and that - what we
2585 # started with should be delta.
2586 diff = nowawareplus - delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002587 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002588 self.assertEqual(nowaware, diff)
2589 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2590 self.assertEqual(nowawareplus - nowaware, delta)
2591
2592 # Make up a random timezone.
2593 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002594 # Attach it to nowawareplus.
2595 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002596 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002597 # Make sure the difference takes the timezone adjustments into account.
2598 got = nowaware - nowawareplus
2599 # Expected: (nowaware base - nowaware offset) -
2600 # (nowawareplus base - nowawareplus offset) =
2601 # (nowaware base - nowawareplus base) +
2602 # (nowawareplus offset - nowaware offset) =
2603 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002604 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002605 self.assertEqual(got, expected)
2606
2607 # Try max possible difference.
2608 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2609 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2610 tzinfo=FixedOffset(-1439, "max"))
2611 maxdiff = max - min
2612 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2613 timedelta(minutes=2*1439))
2614
2615 def test_tzinfo_now(self):
2616 meth = self.theclass.now
2617 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2618 base = meth()
2619 # Try with and without naming the keyword.
2620 off42 = FixedOffset(42, "42")
2621 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002622 again = meth(tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002623 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002624 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002625 # Bad argument with and w/o naming the keyword.
2626 self.assertRaises(TypeError, meth, 16)
2627 self.assertRaises(TypeError, meth, tzinfo=16)
2628 # Bad keyword name.
2629 self.assertRaises(TypeError, meth, tinfo=off42)
2630 # Too many args.
2631 self.assertRaises(TypeError, meth, off42, off42)
2632
Tim Peters10cadce2003-01-23 19:58:02 +00002633 # We don't know which time zone we're in, and don't have a tzinfo
2634 # class to represent it, so seeing whether a tz argument actually
2635 # does a conversion is tricky.
2636 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2637 utc = FixedOffset(0, "utc", 0)
2638 for dummy in range(3):
2639 now = datetime.now(weirdtz)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002640 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002641 utcnow = datetime.utcnow().replace(tzinfo=utc)
2642 now2 = utcnow.astimezone(weirdtz)
2643 if abs(now - now2) < timedelta(seconds=30):
2644 break
2645 # Else the code is broken, or more than 30 seconds passed between
2646 # calls; assuming the latter, just try again.
2647 else:
2648 # Three strikes and we're out.
2649 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2650
Tim Peters2a799bf2002-12-16 20:18:38 +00002651 def test_tzinfo_fromtimestamp(self):
2652 import time
2653 meth = self.theclass.fromtimestamp
2654 ts = time.time()
2655 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2656 base = meth(ts)
2657 # Try with and without naming the keyword.
2658 off42 = FixedOffset(42, "42")
2659 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002660 again = meth(ts, tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002661 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002662 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002663 # Bad argument with and w/o naming the keyword.
2664 self.assertRaises(TypeError, meth, ts, 16)
2665 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2666 # Bad keyword name.
2667 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2668 # Too many args.
2669 self.assertRaises(TypeError, meth, ts, off42, off42)
2670 # Too few args.
2671 self.assertRaises(TypeError, meth)
2672
Tim Peters2a44a8d2003-01-23 20:53:10 +00002673 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002674 timestamp = 1000000000
2675 utcdatetime = datetime.utcfromtimestamp(timestamp)
2676 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2677 # But on some flavor of Mac, it's nowhere near that. So we can't have
2678 # any idea here what time that actually is, we can only test that
2679 # relative changes match.
2680 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2681 tz = FixedOffset(utcoffset, "tz", 0)
2682 expected = utcdatetime + utcoffset
2683 got = datetime.fromtimestamp(timestamp, tz)
2684 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002685
Tim Peters2a799bf2002-12-16 20:18:38 +00002686 def test_tzinfo_utcnow(self):
2687 meth = self.theclass.utcnow
2688 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2689 base = meth()
2690 # Try with and without naming the keyword; for whatever reason,
2691 # utcnow() doesn't accept a tzinfo argument.
2692 off42 = FixedOffset(42, "42")
2693 self.assertRaises(TypeError, meth, off42)
2694 self.assertRaises(TypeError, meth, tzinfo=off42)
2695
2696 def test_tzinfo_utcfromtimestamp(self):
2697 import time
2698 meth = self.theclass.utcfromtimestamp
2699 ts = time.time()
2700 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2701 base = meth(ts)
2702 # Try with and without naming the keyword; for whatever reason,
2703 # utcfromtimestamp() doesn't accept a tzinfo argument.
2704 off42 = FixedOffset(42, "42")
2705 self.assertRaises(TypeError, meth, ts, off42)
2706 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2707
2708 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002709 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002710 # DST flag.
2711 class DST(tzinfo):
2712 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002713 if isinstance(dstvalue, int):
2714 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002715 self.dstvalue = dstvalue
2716 def dst(self, dt):
2717 return self.dstvalue
2718
2719 cls = self.theclass
2720 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2721 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2722 t = d.timetuple()
2723 self.assertEqual(1, t.tm_year)
2724 self.assertEqual(1, t.tm_mon)
2725 self.assertEqual(1, t.tm_mday)
2726 self.assertEqual(10, t.tm_hour)
2727 self.assertEqual(20, t.tm_min)
2728 self.assertEqual(30, t.tm_sec)
2729 self.assertEqual(0, t.tm_wday)
2730 self.assertEqual(1, t.tm_yday)
2731 self.assertEqual(flag, t.tm_isdst)
2732
2733 # dst() returns wrong type.
2734 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2735
2736 # dst() at the edge.
2737 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2738 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2739
2740 # dst() out of range.
2741 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2742 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2743
2744 def test_utctimetuple(self):
2745 class DST(tzinfo):
2746 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002747 if isinstance(dstvalue, int):
2748 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002749 self.dstvalue = dstvalue
2750 def dst(self, dt):
2751 return self.dstvalue
2752
2753 cls = self.theclass
2754 # This can't work: DST didn't implement utcoffset.
2755 self.assertRaises(NotImplementedError,
2756 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2757
2758 class UOFS(DST):
2759 def __init__(self, uofs, dofs=None):
2760 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002761 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002762 def utcoffset(self, dt):
2763 return self.uofs
2764
2765 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2766 # in effect for a UTC time.
2767 for dstvalue in -33, 33, 0, None:
2768 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2769 t = d.utctimetuple()
2770 self.assertEqual(d.year, t.tm_year)
2771 self.assertEqual(d.month, t.tm_mon)
2772 self.assertEqual(d.day, t.tm_mday)
2773 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2774 self.assertEqual(13, t.tm_min)
2775 self.assertEqual(d.second, t.tm_sec)
2776 self.assertEqual(d.weekday(), t.tm_wday)
2777 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2778 t.tm_yday)
2779 self.assertEqual(0, t.tm_isdst)
2780
2781 # At the edges, UTC adjustment can normalize into years out-of-range
2782 # for a datetime object. Ensure that a correct timetuple is
2783 # created anyway.
2784 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2785 # That goes back 1 minute less than a full day.
2786 t = tiny.utctimetuple()
2787 self.assertEqual(t.tm_year, MINYEAR-1)
2788 self.assertEqual(t.tm_mon, 12)
2789 self.assertEqual(t.tm_mday, 31)
2790 self.assertEqual(t.tm_hour, 0)
2791 self.assertEqual(t.tm_min, 1)
2792 self.assertEqual(t.tm_sec, 37)
2793 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2794 self.assertEqual(t.tm_isdst, 0)
2795
2796 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2797 # That goes forward 1 minute less than a full day.
2798 t = huge.utctimetuple()
2799 self.assertEqual(t.tm_year, MAXYEAR+1)
2800 self.assertEqual(t.tm_mon, 1)
2801 self.assertEqual(t.tm_mday, 1)
2802 self.assertEqual(t.tm_hour, 23)
2803 self.assertEqual(t.tm_min, 58)
2804 self.assertEqual(t.tm_sec, 37)
2805 self.assertEqual(t.tm_yday, 1)
2806 self.assertEqual(t.tm_isdst, 0)
2807
2808 def test_tzinfo_isoformat(self):
2809 zero = FixedOffset(0, "+00:00")
2810 plus = FixedOffset(220, "+03:40")
2811 minus = FixedOffset(-231, "-03:51")
2812 unknown = FixedOffset(None, "")
2813
2814 cls = self.theclass
2815 datestr = '0001-02-03'
2816 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002817 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002818 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2819 timestr = '04:05:59' + (us and '.987001' or '')
2820 ofsstr = ofs is not None and d.tzname() or ''
2821 tailstr = timestr + ofsstr
2822 iso = d.isoformat()
2823 self.assertEqual(iso, datestr + 'T' + tailstr)
2824 self.assertEqual(iso, d.isoformat('T'))
2825 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2826 self.assertEqual(str(d), datestr + ' ' + tailstr)
2827
Tim Peters12bf3392002-12-24 05:41:27 +00002828 def test_replace(self):
2829 cls = self.theclass
2830 z100 = FixedOffset(100, "+100")
2831 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2832 args = [1, 2, 3, 4, 5, 6, 7, z100]
2833 base = cls(*args)
2834 self.assertEqual(base, base.replace())
2835
2836 i = 0
2837 for name, newval in (("year", 2),
2838 ("month", 3),
2839 ("day", 4),
2840 ("hour", 5),
2841 ("minute", 6),
2842 ("second", 7),
2843 ("microsecond", 8),
2844 ("tzinfo", zm200)):
2845 newargs = args[:]
2846 newargs[i] = newval
2847 expected = cls(*newargs)
2848 got = base.replace(**{name: newval})
2849 self.assertEqual(expected, got)
2850 i += 1
2851
2852 # Ensure we can get rid of a tzinfo.
2853 self.assertEqual(base.tzname(), "+100")
2854 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002855 self.assertTrue(base2.tzinfo is None)
2856 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002857
2858 # Ensure we can add one.
2859 base3 = base2.replace(tzinfo=z100)
2860 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002861 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002862
2863 # Out of bounds.
2864 base = cls(2000, 2, 29)
2865 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002866
Tim Peters80475bb2002-12-25 07:40:55 +00002867 def test_more_astimezone(self):
2868 # The inherited test_astimezone covered some trivial and error cases.
2869 fnone = FixedOffset(None, "None")
2870 f44m = FixedOffset(44, "44")
2871 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2872
Tim Peters10cadce2003-01-23 19:58:02 +00002873 dt = self.theclass.now(tz=f44m)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002874 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002875 # Replacing with degenerate tzinfo raises an exception.
2876 self.assertRaises(ValueError, dt.astimezone, fnone)
2877 # Ditto with None tz.
2878 self.assertRaises(TypeError, dt.astimezone, None)
2879 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002880 x = dt.astimezone(dt.tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002881 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002882 self.assertEqual(x.date(), dt.date())
2883 self.assertEqual(x.time(), dt.time())
2884
2885 # Replacing with different tzinfo does adjust.
2886 got = dt.astimezone(fm5h)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002887 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002888 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2889 expected = dt - dt.utcoffset() # in effect, convert to UTC
2890 expected += fm5h.utcoffset(dt) # and from there to local time
2891 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2892 self.assertEqual(got.date(), expected.date())
2893 self.assertEqual(got.time(), expected.time())
2894 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002895 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002896 self.assertEqual(got, expected)
2897
Tim Peters4c0db782002-12-26 05:01:19 +00002898 def test_aware_subtract(self):
2899 cls = self.theclass
2900
Tim Peters60c76e42002-12-27 00:41:11 +00002901 # Ensure that utcoffset() is ignored when the operands have the
2902 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002903 class OperandDependentOffset(tzinfo):
2904 def utcoffset(self, t):
2905 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002906 # d0 and d1 equal after adjustment
2907 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002908 else:
Tim Peters397301e2003-01-02 21:28:08 +00002909 # d2 off in the weeds
2910 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002911
2912 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2913 d0 = base.replace(minute=3)
2914 d1 = base.replace(minute=9)
2915 d2 = base.replace(minute=11)
2916 for x in d0, d1, d2:
2917 for y in d0, d1, d2:
2918 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002919 expected = timedelta(minutes=x.minute - y.minute)
2920 self.assertEqual(got, expected)
2921
2922 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2923 # ignored.
2924 base = cls(8, 9, 10, 11, 12, 13, 14)
2925 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2926 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2927 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2928 for x in d0, d1, d2:
2929 for y in d0, d1, d2:
2930 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002931 if (x is d0 or x is d1) and (y is d0 or y is d1):
2932 expected = timedelta(0)
2933 elif x is y is d2:
2934 expected = timedelta(0)
2935 elif x is d2:
2936 expected = timedelta(minutes=(11-59)-0)
2937 else:
2938 assert y is d2
2939 expected = timedelta(minutes=0-(11-59))
2940 self.assertEqual(got, expected)
2941
Tim Peters60c76e42002-12-27 00:41:11 +00002942 def test_mixed_compare(self):
2943 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002944 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002945 self.assertEqual(t1, t2)
2946 t2 = t2.replace(tzinfo=None)
2947 self.assertEqual(t1, t2)
2948 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2949 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002950 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2951 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002952
Tim Peters0bf60bd2003-01-08 20:40:01 +00002953 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002954 class Varies(tzinfo):
2955 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002956 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002957 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002958 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002959 return self.offset
2960
2961 v = Varies()
2962 t1 = t2.replace(tzinfo=v)
2963 t2 = t2.replace(tzinfo=v)
2964 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2965 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2966 self.assertEqual(t1, t2)
2967
2968 # But if they're not identical, it isn't ignored.
2969 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002970 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002971
Tim Petersa98924a2003-05-17 05:55:19 +00002972 def test_subclass_datetimetz(self):
2973
2974 class C(self.theclass):
2975 theAnswer = 42
2976
2977 def __new__(cls, *args, **kws):
2978 temp = kws.copy()
2979 extra = temp.pop('extra')
2980 result = self.theclass.__new__(cls, *args, **temp)
2981 result.extra = extra
2982 return result
2983
2984 def newmeth(self, start):
2985 return start + self.hour + self.year
2986
2987 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2988
2989 dt1 = self.theclass(*args)
2990 dt2 = C(*args, **{'extra': 7})
2991
2992 self.assertEqual(dt2.__class__, C)
2993 self.assertEqual(dt2.theAnswer, 42)
2994 self.assertEqual(dt2.extra, 7)
2995 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2996 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2997
Tim Peters621818b2002-12-29 23:44:49 +00002998# Pain to set up DST-aware tzinfo classes.
2999
3000def first_sunday_on_or_after(dt):
3001 days_to_go = 6 - dt.weekday()
3002 if days_to_go:
3003 dt += timedelta(days_to_go)
3004 return dt
3005
3006ZERO = timedelta(0)
3007HOUR = timedelta(hours=1)
3008DAY = timedelta(days=1)
3009# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3010DSTSTART = datetime(1, 4, 1, 2)
3011# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003012# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3013# being standard time on that day, there is no spelling in local time of
3014# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3015DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003016
3017class USTimeZone(tzinfo):
3018
3019 def __init__(self, hours, reprname, stdname, dstname):
3020 self.stdoffset = timedelta(hours=hours)
3021 self.reprname = reprname
3022 self.stdname = stdname
3023 self.dstname = dstname
3024
3025 def __repr__(self):
3026 return self.reprname
3027
3028 def tzname(self, dt):
3029 if self.dst(dt):
3030 return self.dstname
3031 else:
3032 return self.stdname
3033
3034 def utcoffset(self, dt):
3035 return self.stdoffset + self.dst(dt)
3036
3037 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003038 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003039 # An exception instead may be sensible here, in one or more of
3040 # the cases.
3041 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003042 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003043
3044 # Find first Sunday in April.
3045 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3046 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3047
3048 # Find last Sunday in October.
3049 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3050 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3051
Tim Peters621818b2002-12-29 23:44:49 +00003052 # Can't compare naive to aware objects, so strip the timezone from
3053 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003054 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003055 return HOUR
3056 else:
3057 return ZERO
3058
Tim Peters521fc152002-12-31 17:36:56 +00003059Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3060Central = USTimeZone(-6, "Central", "CST", "CDT")
3061Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3062Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003063utc_real = FixedOffset(0, "UTC", 0)
3064# For better test coverage, we want another flavor of UTC that's west of
3065# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003066utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003067
3068class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003069 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003070 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003071 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003072
Tim Peters0bf60bd2003-01-08 20:40:01 +00003073 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003074
Tim Peters521fc152002-12-31 17:36:56 +00003075 # Check a time that's inside DST.
3076 def checkinside(self, dt, tz, utc, dston, dstoff):
3077 self.assertEqual(dt.dst(), HOUR)
3078
3079 # Conversion to our own timezone is always an identity.
3080 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003081
3082 asutc = dt.astimezone(utc)
3083 there_and_back = asutc.astimezone(tz)
3084
3085 # Conversion to UTC and back isn't always an identity here,
3086 # because there are redundant spellings (in local time) of
3087 # UTC time when DST begins: the clock jumps from 1:59:59
3088 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3089 # make sense then. The classes above treat 2:MM:SS as
3090 # daylight time then (it's "after 2am"), really an alias
3091 # for 1:MM:SS standard time. The latter form is what
3092 # conversion back from UTC produces.
3093 if dt.date() == dston.date() and dt.hour == 2:
3094 # We're in the redundant hour, and coming back from
3095 # UTC gives the 1:MM:SS standard-time spelling.
3096 self.assertEqual(there_and_back + HOUR, dt)
3097 # Although during was considered to be in daylight
3098 # time, there_and_back is not.
3099 self.assertEqual(there_and_back.dst(), ZERO)
3100 # They're the same times in UTC.
3101 self.assertEqual(there_and_back.astimezone(utc),
3102 dt.astimezone(utc))
3103 else:
3104 # We're not in the redundant hour.
3105 self.assertEqual(dt, there_and_back)
3106
Tim Peters327098a2003-01-20 22:54:38 +00003107 # Because we have a redundant spelling when DST begins, there is
3108 # (unforunately) an hour when DST ends that can't be spelled at all in
3109 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3110 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3111 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3112 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3113 # expressed in local time. Nevertheless, we want conversion back
3114 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003115 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003116 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003117 if dt.date() == dstoff.date() and dt.hour == 0:
3118 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003119 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003120 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3121 nexthour_utc += HOUR
3122 nexthour_tz = nexthour_utc.astimezone(tz)
3123 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003124 else:
Tim Peters327098a2003-01-20 22:54:38 +00003125 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003126
3127 # Check a time that's outside DST.
3128 def checkoutside(self, dt, tz, utc):
3129 self.assertEqual(dt.dst(), ZERO)
3130
3131 # Conversion to our own timezone is always an identity.
3132 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003133
3134 # Converting to UTC and back is an identity too.
3135 asutc = dt.astimezone(utc)
3136 there_and_back = asutc.astimezone(tz)
3137 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003138
Tim Peters1024bf82002-12-30 17:09:40 +00003139 def convert_between_tz_and_utc(self, tz, utc):
3140 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003141 # Because 1:MM on the day DST ends is taken as being standard time,
3142 # there is no spelling in tz for the last hour of daylight time.
3143 # For purposes of the test, the last hour of DST is 0:MM, which is
3144 # taken as being daylight time (and 1:MM is taken as being standard
3145 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003146 dstoff = self.dstoff.replace(tzinfo=tz)
3147 for delta in (timedelta(weeks=13),
3148 DAY,
3149 HOUR,
3150 timedelta(minutes=1),
3151 timedelta(microseconds=1)):
3152
Tim Peters521fc152002-12-31 17:36:56 +00003153 self.checkinside(dston, tz, utc, dston, dstoff)
3154 for during in dston + delta, dstoff - delta:
3155 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003156
Tim Peters521fc152002-12-31 17:36:56 +00003157 self.checkoutside(dstoff, tz, utc)
3158 for outside in dston - delta, dstoff + delta:
3159 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003160
Tim Peters621818b2002-12-29 23:44:49 +00003161 def test_easy(self):
3162 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003163 self.convert_between_tz_and_utc(Eastern, utc_real)
3164 self.convert_between_tz_and_utc(Pacific, utc_real)
3165 self.convert_between_tz_and_utc(Eastern, utc_fake)
3166 self.convert_between_tz_and_utc(Pacific, utc_fake)
3167 # The next is really dancing near the edge. It works because
3168 # Pacific and Eastern are far enough apart that their "problem
3169 # hours" don't overlap.
3170 self.convert_between_tz_and_utc(Eastern, Pacific)
3171 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003172 # OTOH, these fail! Don't enable them. The difficulty is that
3173 # the edge case tests assume that every hour is representable in
3174 # the "utc" class. This is always true for a fixed-offset tzinfo
3175 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3176 # For these adjacent DST-aware time zones, the range of time offsets
3177 # tested ends up creating hours in the one that aren't representable
3178 # in the other. For the same reason, we would see failures in the
3179 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3180 # offset deltas in convert_between_tz_and_utc().
3181 #
3182 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3183 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003184
Tim Petersf3615152003-01-01 21:51:37 +00003185 def test_tricky(self):
3186 # 22:00 on day before daylight starts.
3187 fourback = self.dston - timedelta(hours=4)
3188 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003189 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003190 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3191 # 2", we should get the 3 spelling.
3192 # If we plug 22:00 the day before into Eastern, it "looks like std
3193 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3194 # to 22:00 lands on 2:00, which makes no sense in local time (the
3195 # local clock jumps from 1 to 3). The point here is to make sure we
3196 # get the 3 spelling.
3197 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003198 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003199 self.assertEqual(expected, got)
3200
3201 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3202 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003203 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003204 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3205 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3206 # spelling.
3207 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003208 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003209 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003210
Tim Petersadf64202003-01-04 06:03:15 +00003211 # Now on the day DST ends, we want "repeat an hour" behavior.
3212 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3213 # EST 23:MM 0:MM 1:MM 2:MM
3214 # EDT 0:MM 1:MM 2:MM 3:MM
3215 # wall 0:MM 1:MM 1:MM 2:MM against these
3216 for utc in utc_real, utc_fake:
3217 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003218 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003219 # Convert that to UTC.
3220 first_std_hour -= tz.utcoffset(None)
3221 # Adjust for possibly fake UTC.
3222 asutc = first_std_hour + utc.utcoffset(None)
3223 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3224 # tz=Eastern.
3225 asutcbase = asutc.replace(tzinfo=utc)
3226 for tzhour in (0, 1, 1, 2):
3227 expectedbase = self.dstoff.replace(hour=tzhour)
3228 for minute in 0, 30, 59:
3229 expected = expectedbase.replace(minute=minute)
3230 asutc = asutcbase.replace(minute=minute)
3231 astz = asutc.astimezone(tz)
3232 self.assertEqual(astz.replace(tzinfo=None), expected)
3233 asutcbase += HOUR
3234
3235
Tim Peters710fb152003-01-02 19:35:54 +00003236 def test_bogus_dst(self):
3237 class ok(tzinfo):
3238 def utcoffset(self, dt): return HOUR
3239 def dst(self, dt): return HOUR
3240
3241 now = self.theclass.now().replace(tzinfo=utc_real)
3242 # Doesn't blow up.
3243 now.astimezone(ok())
3244
3245 # Does blow up.
3246 class notok(ok):
3247 def dst(self, dt): return None
3248 self.assertRaises(ValueError, now.astimezone, notok())
3249
Tim Peters52dcce22003-01-23 16:36:11 +00003250 def test_fromutc(self):
3251 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3252 now = datetime.utcnow().replace(tzinfo=utc_real)
3253 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3254 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3255 enow = Eastern.fromutc(now) # doesn't blow up
3256 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3257 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3258 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3259
3260 # Always converts UTC to standard time.
3261 class FauxUSTimeZone(USTimeZone):
3262 def fromutc(self, dt):
3263 return dt + self.stdoffset
3264 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3265
3266 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3267 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3268 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3269
3270 # Check around DST start.
3271 start = self.dston.replace(hour=4, tzinfo=Eastern)
3272 fstart = start.replace(tzinfo=FEastern)
3273 for wall in 23, 0, 1, 3, 4, 5:
3274 expected = start.replace(hour=wall)
3275 if wall == 23:
3276 expected -= timedelta(days=1)
3277 got = Eastern.fromutc(start)
3278 self.assertEqual(expected, got)
3279
3280 expected = fstart + FEastern.stdoffset
3281 got = FEastern.fromutc(fstart)
3282 self.assertEqual(expected, got)
3283
3284 # Ensure astimezone() calls fromutc() too.
3285 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3286 self.assertEqual(expected, got)
3287
3288 start += HOUR
3289 fstart += HOUR
3290
3291 # Check around DST end.
3292 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3293 fstart = start.replace(tzinfo=FEastern)
3294 for wall in 0, 1, 1, 2, 3, 4:
3295 expected = start.replace(hour=wall)
3296 got = Eastern.fromutc(start)
3297 self.assertEqual(expected, got)
3298
3299 expected = fstart + FEastern.stdoffset
3300 got = FEastern.fromutc(fstart)
3301 self.assertEqual(expected, got)
3302
3303 # Ensure astimezone() calls fromutc() too.
3304 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3305 self.assertEqual(expected, got)
3306
3307 start += HOUR
3308 fstart += HOUR
3309
Tim Peters710fb152003-01-02 19:35:54 +00003310
Tim Peters528ca532004-09-16 01:30:50 +00003311#############################################################################
3312# oddballs
3313
3314class Oddballs(unittest.TestCase):
3315
3316 def test_bug_1028306(self):
3317 # Trying to compare a date to a datetime should act like a mixed-
3318 # type comparison, despite that datetime is a subclass of date.
3319 as_date = date.today()
3320 as_datetime = datetime.combine(as_date, time())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003321 self.assertTrue(as_date != as_datetime)
3322 self.assertTrue(as_datetime != as_date)
3323 self.assertTrue(not as_date == as_datetime)
3324 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003325 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3326 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3327 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3328 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3329 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3330 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3331 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3332 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3333
3334 # Neverthelss, comparison should work with the base-class (date)
3335 # projection if use of a date method is forced.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003336 self.assertTrue(as_date.__eq__(as_datetime))
Tim Peters528ca532004-09-16 01:30:50 +00003337 different_day = (as_date.day + 1) % 20 + 1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003338 self.assertTrue(not as_date.__eq__(as_datetime.replace(day=
Tim Peters528ca532004-09-16 01:30:50 +00003339 different_day)))
3340
3341 # And date should compare with other subclasses of date. If a
3342 # subclass wants to stop this, it's up to the subclass to do so.
3343 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3344 self.assertEqual(as_date, date_sc)
3345 self.assertEqual(date_sc, as_date)
3346
3347 # Ditto for datetimes.
3348 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3349 as_date.day, 0, 0, 0)
3350 self.assertEqual(as_datetime, datetime_sc)
3351 self.assertEqual(datetime_sc, as_datetime)
3352
Tim Peters2a799bf2002-12-16 20:18:38 +00003353def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003354 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003355
3356if __name__ == "__main__":
3357 test_main()