blob: ad36398598c1526f4cb755d60ab0ac1ccac386c3 [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")
Benjamin Peterson5c8da862009-06-30 22:57:08 +000084 self.assertTrue(isinstance(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")
Benjamin Peterson5c8da862009-06-30 22:57:08 +000093 self.assertTrue(isinstance(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')
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000114 self.assertTrue(isinstance(orig, tzinfo))
115 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)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000121 self.assertTrue(isinstance(derived, tzinfo))
122 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
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000144 self.assertTrue(me in [1, 20L, [], me])
145 self.assertFalse(me not in [1, 20L, [], me])
Tim Peters07534a62003-02-07 22:50:28 +0000146
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000147 self.assertTrue([] in [me, 1, 20L, []])
148 self.assertFalse([] not in [me, 1, 20L, []])
Tim Peters07534a62003-02-07 22:50:28 +0000149
150 def test_harmful_mixed_comparison(self):
151 me = self.theclass(1, 1, 1)
152
153 self.assertRaises(TypeError, lambda: me < ())
154 self.assertRaises(TypeError, lambda: me <= ())
155 self.assertRaises(TypeError, lambda: me > ())
156 self.assertRaises(TypeError, lambda: me >= ())
157
158 self.assertRaises(TypeError, lambda: () < me)
159 self.assertRaises(TypeError, lambda: () <= me)
160 self.assertRaises(TypeError, lambda: () > me)
161 self.assertRaises(TypeError, lambda: () >= me)
162
163 self.assertRaises(TypeError, cmp, (), me)
164 self.assertRaises(TypeError, cmp, me, ())
165
166#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000167# timedelta tests
168
Collin Winterc2898c52007-04-25 17:29:52 +0000169class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000170
171 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000172
173 def test_constructor(self):
174 eq = self.assertEqual
175 td = timedelta
176
177 # Check keyword args to constructor
178 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
179 milliseconds=0, microseconds=0))
180 eq(td(1), td(days=1))
181 eq(td(0, 1), td(seconds=1))
182 eq(td(0, 0, 1), td(microseconds=1))
183 eq(td(weeks=1), td(days=7))
184 eq(td(days=1), td(hours=24))
185 eq(td(hours=1), td(minutes=60))
186 eq(td(minutes=1), td(seconds=60))
187 eq(td(seconds=1), td(milliseconds=1000))
188 eq(td(milliseconds=1), td(microseconds=1000))
189
190 # Check float args to constructor
191 eq(td(weeks=1.0/7), td(days=1))
192 eq(td(days=1.0/24), td(hours=1))
193 eq(td(hours=1.0/60), td(minutes=1))
194 eq(td(minutes=1.0/60), td(seconds=1))
195 eq(td(seconds=0.001), td(milliseconds=1))
196 eq(td(milliseconds=0.001), td(microseconds=1))
197
198 def test_computations(self):
199 eq = self.assertEqual
200 td = timedelta
201
202 a = td(7) # One week
203 b = td(0, 60) # One minute
204 c = td(0, 0, 1000) # One millisecond
205 eq(a+b+c, td(7, 60, 1000))
206 eq(a-b, td(6, 24*3600 - 60))
207 eq(-a, td(-7))
208 eq(+a, td(7))
209 eq(-b, td(-1, 24*3600 - 60))
210 eq(-c, td(-1, 24*3600 - 1, 999000))
211 eq(abs(a), a)
212 eq(abs(-a), a)
213 eq(td(6, 24*3600), a)
214 eq(td(0, 0, 60*1000000), b)
215 eq(a*10, td(70))
216 eq(a*10, 10*a)
217 eq(a*10L, 10*a)
218 eq(b*10, td(0, 600))
219 eq(10*b, td(0, 600))
220 eq(b*10L, td(0, 600))
221 eq(c*10, td(0, 0, 10000))
222 eq(10*c, td(0, 0, 10000))
223 eq(c*10L, td(0, 0, 10000))
224 eq(a*-1, -a)
225 eq(b*-2, -b-b)
226 eq(c*-2, -c+-c)
227 eq(b*(60*24), (b*60)*24)
228 eq(b*(60*24), (60*b)*24)
229 eq(c*1000, td(0, 1))
230 eq(1000*c, td(0, 1))
231 eq(a//7, td(1))
232 eq(b//10, td(0, 6))
233 eq(c//1000, td(0, 0, 1))
234 eq(a//10, td(0, 7*24*360))
235 eq(a//3600000, td(0, 0, 7*24*1000))
236
237 def test_disallowed_computations(self):
238 a = timedelta(42)
239
240 # Add/sub ints, longs, floats should be illegal
241 for i in 1, 1L, 1.0:
242 self.assertRaises(TypeError, lambda: a+i)
243 self.assertRaises(TypeError, lambda: a-i)
244 self.assertRaises(TypeError, lambda: i+a)
245 self.assertRaises(TypeError, lambda: i-a)
246
247 # Mul/div by float isn't supported.
248 x = 2.3
249 self.assertRaises(TypeError, lambda: a*x)
250 self.assertRaises(TypeError, lambda: x*a)
251 self.assertRaises(TypeError, lambda: a/x)
252 self.assertRaises(TypeError, lambda: x/a)
253 self.assertRaises(TypeError, lambda: a // x)
254 self.assertRaises(TypeError, lambda: x // a)
255
Andrew M. Kuchling081bb452008-10-03 16:42:52 +0000256 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000257 # Division by zero doesn't make sense.
258 for zero in 0, 0L:
259 self.assertRaises(TypeError, lambda: zero // a)
260 self.assertRaises(ZeroDivisionError, lambda: a // zero)
261
262 def test_basic_attributes(self):
263 days, seconds, us = 1, 7, 31
264 td = timedelta(days, seconds, us)
265 self.assertEqual(td.days, days)
266 self.assertEqual(td.seconds, seconds)
267 self.assertEqual(td.microseconds, us)
268
269 def test_carries(self):
270 t1 = timedelta(days=100,
271 weeks=-7,
272 hours=-24*(100-49),
273 minutes=-3,
274 seconds=12,
275 microseconds=(3*60 - 12) * 1e6 + 1)
276 t2 = timedelta(microseconds=1)
277 self.assertEqual(t1, t2)
278
279 def test_hash_equality(self):
280 t1 = timedelta(days=100,
281 weeks=-7,
282 hours=-24*(100-49),
283 minutes=-3,
284 seconds=12,
285 microseconds=(3*60 - 12) * 1000000)
286 t2 = timedelta()
287 self.assertEqual(hash(t1), hash(t2))
288
289 t1 += timedelta(weeks=7)
290 t2 += timedelta(days=7*7)
291 self.assertEqual(t1, t2)
292 self.assertEqual(hash(t1), hash(t2))
293
294 d = {t1: 1}
295 d[t2] = 2
296 self.assertEqual(len(d), 1)
297 self.assertEqual(d[t1], 2)
298
299 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000300 args = 12, 34, 56
301 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000302 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000303 green = pickler.dumps(orig, proto)
304 derived = unpickler.loads(green)
305 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000306
307 def test_compare(self):
308 t1 = timedelta(2, 3, 4)
309 t2 = timedelta(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000310 self.assertTrue(t1 == t2)
311 self.assertTrue(t1 <= t2)
312 self.assertTrue(t1 >= t2)
313 self.assertTrue(not t1 != t2)
314 self.assertTrue(not t1 < t2)
315 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000316 self.assertEqual(cmp(t1, t2), 0)
317 self.assertEqual(cmp(t2, t1), 0)
318
319 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
320 t2 = timedelta(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000321 self.assertTrue(t1 < t2)
322 self.assertTrue(t2 > t1)
323 self.assertTrue(t1 <= t2)
324 self.assertTrue(t2 >= t1)
325 self.assertTrue(t1 != t2)
326 self.assertTrue(t2 != t1)
327 self.assertTrue(not t1 == t2)
328 self.assertTrue(not t2 == t1)
329 self.assertTrue(not t1 > t2)
330 self.assertTrue(not t2 < t1)
331 self.assertTrue(not t1 >= t2)
332 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000333 self.assertEqual(cmp(t1, t2), -1)
334 self.assertEqual(cmp(t2, t1), 1)
335
Tim Peters68124bb2003-02-08 03:46:31 +0000336 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000337 self.assertEqual(t1 == badarg, False)
338 self.assertEqual(t1 != badarg, True)
339 self.assertEqual(badarg == t1, False)
340 self.assertEqual(badarg != t1, True)
341
Tim Peters2a799bf2002-12-16 20:18:38 +0000342 self.assertRaises(TypeError, lambda: t1 <= badarg)
343 self.assertRaises(TypeError, lambda: t1 < badarg)
344 self.assertRaises(TypeError, lambda: t1 > badarg)
345 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000346 self.assertRaises(TypeError, lambda: badarg <= t1)
347 self.assertRaises(TypeError, lambda: badarg < t1)
348 self.assertRaises(TypeError, lambda: badarg > t1)
349 self.assertRaises(TypeError, lambda: badarg >= t1)
350
351 def test_str(self):
352 td = timedelta
353 eq = self.assertEqual
354
355 eq(str(td(1)), "1 day, 0:00:00")
356 eq(str(td(-1)), "-1 day, 0:00:00")
357 eq(str(td(2)), "2 days, 0:00:00")
358 eq(str(td(-2)), "-2 days, 0:00:00")
359
360 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
361 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
362 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
363 "-210 days, 23:12:34")
364
365 eq(str(td(milliseconds=1)), "0:00:00.001000")
366 eq(str(td(microseconds=3)), "0:00:00.000003")
367
368 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
369 microseconds=999999)),
370 "999999999 days, 23:59:59.999999")
371
372 def test_roundtrip(self):
373 for td in (timedelta(days=999999999, hours=23, minutes=59,
374 seconds=59, microseconds=999999),
375 timedelta(days=-999999999),
376 timedelta(days=1, seconds=2, microseconds=3)):
377
378 # Verify td -> string -> td identity.
379 s = repr(td)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000380 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000381 s = s[9:]
382 td2 = eval(s)
383 self.assertEqual(td, td2)
384
385 # Verify identity via reconstructing from pieces.
386 td2 = timedelta(td.days, td.seconds, td.microseconds)
387 self.assertEqual(td, td2)
388
389 def test_resolution_info(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000390 self.assertTrue(isinstance(timedelta.min, timedelta))
391 self.assertTrue(isinstance(timedelta.max, timedelta))
392 self.assertTrue(isinstance(timedelta.resolution, timedelta))
393 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000394 self.assertEqual(timedelta.min, timedelta(-999999999))
395 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
396 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
397
398 def test_overflow(self):
399 tiny = timedelta.resolution
400
401 td = timedelta.min + tiny
402 td -= tiny # no problem
403 self.assertRaises(OverflowError, td.__sub__, tiny)
404 self.assertRaises(OverflowError, td.__add__, -tiny)
405
406 td = timedelta.max - tiny
407 td += tiny # no problem
408 self.assertRaises(OverflowError, td.__add__, tiny)
409 self.assertRaises(OverflowError, td.__sub__, -tiny)
410
411 self.assertRaises(OverflowError, lambda: -timedelta.max)
412
413 def test_microsecond_rounding(self):
414 td = timedelta
415 eq = self.assertEqual
416
417 # Single-field rounding.
418 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
419 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
420 eq(td(milliseconds=0.6/1000), td(microseconds=1))
421 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
422
423 # Rounding due to contributions from more than one field.
424 us_per_hour = 3600e6
425 us_per_day = us_per_hour * 24
426 eq(td(days=.4/us_per_day), td(0))
427 eq(td(hours=.2/us_per_hour), td(0))
428 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
429
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 def test_massive_normalization(self):
435 td = timedelta(microseconds=-1)
436 self.assertEqual((td.days, td.seconds, td.microseconds),
437 (-1, 24*3600-1, 999999))
438
439 def test_bool(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000440 self.assertTrue(timedelta(1))
441 self.assertTrue(timedelta(0, 1))
442 self.assertTrue(timedelta(0, 0, 1))
443 self.assertTrue(timedelta(microseconds=1))
444 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000445
Tim Petersb0c854d2003-05-17 15:57:00 +0000446 def test_subclass_timedelta(self):
447
448 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000449 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000450 def from_td(td):
451 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000452
453 def as_hours(self):
454 sum = (self.days * 24 +
455 self.seconds / 3600.0 +
456 self.microseconds / 3600e6)
457 return round(sum)
458
459 t1 = T(days=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000460 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000461 self.assertEqual(t1.as_hours(), 24)
462
463 t2 = T(days=-1, seconds=-3600)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000464 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000465 self.assertEqual(t2.as_hours(), -25)
466
467 t3 = t1 + t2
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000468 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000469 t4 = T.from_td(t3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000470 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000471 self.assertEqual(t3.days, t4.days)
472 self.assertEqual(t3.seconds, t4.seconds)
473 self.assertEqual(t3.microseconds, t4.microseconds)
474 self.assertEqual(str(t3), str(t4))
475 self.assertEqual(t4.as_hours(), -1)
476
Tim Peters2a799bf2002-12-16 20:18:38 +0000477#############################################################################
478# date tests
479
480class TestDateOnly(unittest.TestCase):
481 # Tests here won't pass if also run on datetime objects, so don't
482 # subclass this to test datetimes too.
483
484 def test_delta_non_days_ignored(self):
485 dt = date(2000, 1, 2)
486 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
487 microseconds=5)
488 days = timedelta(delta.days)
489 self.assertEqual(days, timedelta(1))
490
491 dt2 = dt + delta
492 self.assertEqual(dt2, dt + days)
493
494 dt2 = delta + dt
495 self.assertEqual(dt2, dt + days)
496
497 dt2 = dt - delta
498 self.assertEqual(dt2, dt - days)
499
500 delta = -delta
501 days = timedelta(delta.days)
502 self.assertEqual(days, timedelta(-2))
503
504 dt2 = dt + delta
505 self.assertEqual(dt2, dt + days)
506
507 dt2 = delta + dt
508 self.assertEqual(dt2, dt + days)
509
510 dt2 = dt - delta
511 self.assertEqual(dt2, dt - days)
512
Tim Peters604c0132004-06-07 23:04:33 +0000513class SubclassDate(date):
514 sub_var = 1
515
Collin Winterc2898c52007-04-25 17:29:52 +0000516class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000517 # Tests here should pass for both dates and datetimes, except for a
518 # few tests that TestDateTime overrides.
519
520 theclass = date
521
522 def test_basic_attributes(self):
523 dt = self.theclass(2002, 3, 1)
524 self.assertEqual(dt.year, 2002)
525 self.assertEqual(dt.month, 3)
526 self.assertEqual(dt.day, 1)
527
528 def test_roundtrip(self):
529 for dt in (self.theclass(1, 2, 3),
530 self.theclass.today()):
531 # Verify dt -> string -> date identity.
532 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000533 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000534 s = s[9:]
535 dt2 = eval(s)
536 self.assertEqual(dt, dt2)
537
538 # Verify identity via reconstructing from pieces.
539 dt2 = self.theclass(dt.year, dt.month, dt.day)
540 self.assertEqual(dt, dt2)
541
542 def test_ordinal_conversions(self):
543 # Check some fixed values.
544 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
545 (1, 12, 31, 365),
546 (2, 1, 1, 366),
547 # first example from "Calendrical Calculations"
548 (1945, 11, 12, 710347)]:
549 d = self.theclass(y, m, d)
550 self.assertEqual(n, d.toordinal())
551 fromord = self.theclass.fromordinal(n)
552 self.assertEqual(d, fromord)
553 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000554 # if we're checking something fancier than a date, verify
555 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000556 self.assertEqual(fromord.hour, 0)
557 self.assertEqual(fromord.minute, 0)
558 self.assertEqual(fromord.second, 0)
559 self.assertEqual(fromord.microsecond, 0)
560
Tim Peters0bf60bd2003-01-08 20:40:01 +0000561 # Check first and last days of year spottily across the whole
562 # range of years supported.
563 for year in xrange(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000564 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
565 d = self.theclass(year, 1, 1)
566 n = d.toordinal()
567 d2 = self.theclass.fromordinal(n)
568 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000569 # Verify that moving back a day gets to the end of year-1.
570 if year > 1:
571 d = self.theclass.fromordinal(n-1)
572 d2 = self.theclass(year-1, 12, 31)
573 self.assertEqual(d, d2)
574 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000575
576 # Test every day in a leap-year and a non-leap year.
577 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
578 for year, isleap in (2000, True), (2002, False):
579 n = self.theclass(year, 1, 1).toordinal()
580 for month, maxday in zip(range(1, 13), dim):
581 if month == 2 and isleap:
582 maxday += 1
583 for day in range(1, maxday+1):
584 d = self.theclass(year, month, day)
585 self.assertEqual(d.toordinal(), n)
586 self.assertEqual(d, self.theclass.fromordinal(n))
587 n += 1
588
589 def test_extreme_ordinals(self):
590 a = self.theclass.min
591 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
592 aord = a.toordinal()
593 b = a.fromordinal(aord)
594 self.assertEqual(a, b)
595
596 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
597
598 b = a + timedelta(days=1)
599 self.assertEqual(b.toordinal(), aord + 1)
600 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
601
602 a = self.theclass.max
603 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
604 aord = a.toordinal()
605 b = a.fromordinal(aord)
606 self.assertEqual(a, b)
607
608 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
609
610 b = a - timedelta(days=1)
611 self.assertEqual(b.toordinal(), aord - 1)
612 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
613
614 def test_bad_constructor_arguments(self):
615 # bad years
616 self.theclass(MINYEAR, 1, 1) # no exception
617 self.theclass(MAXYEAR, 1, 1) # no exception
618 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
619 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
620 # bad months
621 self.theclass(2000, 1, 1) # no exception
622 self.theclass(2000, 12, 1) # no exception
623 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
624 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
625 # bad days
626 self.theclass(2000, 2, 29) # no exception
627 self.theclass(2004, 2, 29) # no exception
628 self.theclass(2400, 2, 29) # no exception
629 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
630 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
631 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
632 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
633 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
634 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
635
636 def test_hash_equality(self):
637 d = self.theclass(2000, 12, 31)
638 # same thing
639 e = self.theclass(2000, 12, 31)
640 self.assertEqual(d, e)
641 self.assertEqual(hash(d), hash(e))
642
643 dic = {d: 1}
644 dic[e] = 2
645 self.assertEqual(len(dic), 1)
646 self.assertEqual(dic[d], 2)
647 self.assertEqual(dic[e], 2)
648
649 d = self.theclass(2001, 1, 1)
650 # same thing
651 e = self.theclass(2001, 1, 1)
652 self.assertEqual(d, e)
653 self.assertEqual(hash(d), hash(e))
654
655 dic = {d: 1}
656 dic[e] = 2
657 self.assertEqual(len(dic), 1)
658 self.assertEqual(dic[d], 2)
659 self.assertEqual(dic[e], 2)
660
661 def test_computations(self):
662 a = self.theclass(2002, 1, 31)
663 b = self.theclass(1956, 1, 31)
664
665 diff = a-b
666 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
667 self.assertEqual(diff.seconds, 0)
668 self.assertEqual(diff.microseconds, 0)
669
670 day = timedelta(1)
671 week = timedelta(7)
672 a = self.theclass(2002, 3, 2)
673 self.assertEqual(a + day, self.theclass(2002, 3, 3))
674 self.assertEqual(day + a, self.theclass(2002, 3, 3))
675 self.assertEqual(a - day, self.theclass(2002, 3, 1))
676 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
677 self.assertEqual(a + week, self.theclass(2002, 3, 9))
678 self.assertEqual(a - week, self.theclass(2002, 2, 23))
679 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
680 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
681 self.assertEqual((a + week) - a, week)
682 self.assertEqual((a + day) - a, day)
683 self.assertEqual((a - week) - a, -week)
684 self.assertEqual((a - day) - a, -day)
685 self.assertEqual(a - (a + week), -week)
686 self.assertEqual(a - (a + day), -day)
687 self.assertEqual(a - (a - week), week)
688 self.assertEqual(a - (a - day), day)
689
690 # Add/sub ints, longs, floats should be illegal
691 for i in 1, 1L, 1.0:
692 self.assertRaises(TypeError, lambda: a+i)
693 self.assertRaises(TypeError, lambda: a-i)
694 self.assertRaises(TypeError, lambda: i+a)
695 self.assertRaises(TypeError, lambda: i-a)
696
697 # delta - date is senseless.
698 self.assertRaises(TypeError, lambda: day - a)
699 # mixing date and (delta or date) via * or // is senseless
700 self.assertRaises(TypeError, lambda: day * a)
701 self.assertRaises(TypeError, lambda: a * day)
702 self.assertRaises(TypeError, lambda: day // a)
703 self.assertRaises(TypeError, lambda: a // day)
704 self.assertRaises(TypeError, lambda: a * a)
705 self.assertRaises(TypeError, lambda: a // a)
706 # date + date is senseless
707 self.assertRaises(TypeError, lambda: a + a)
708
709 def test_overflow(self):
710 tiny = self.theclass.resolution
711
712 dt = self.theclass.min + tiny
713 dt -= tiny # no problem
714 self.assertRaises(OverflowError, dt.__sub__, tiny)
715 self.assertRaises(OverflowError, dt.__add__, -tiny)
716
717 dt = self.theclass.max - tiny
718 dt += tiny # no problem
719 self.assertRaises(OverflowError, dt.__add__, tiny)
720 self.assertRaises(OverflowError, dt.__sub__, -tiny)
721
722 def test_fromtimestamp(self):
723 import time
724
725 # Try an arbitrary fixed value.
726 year, month, day = 1999, 9, 19
727 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
728 d = self.theclass.fromtimestamp(ts)
729 self.assertEqual(d.year, year)
730 self.assertEqual(d.month, month)
731 self.assertEqual(d.day, day)
732
Tim Peters1b6f7a92004-06-20 02:50:16 +0000733 def test_insane_fromtimestamp(self):
734 # It's possible that some platform maps time_t to double,
735 # and that this test will fail there. This test should
736 # exempt such platforms (provided they return reasonable
737 # results!).
738 for insane in -1e200, 1e200:
739 self.assertRaises(ValueError, self.theclass.fromtimestamp,
740 insane)
741
Tim Peters2a799bf2002-12-16 20:18:38 +0000742 def test_today(self):
743 import time
744
745 # We claim that today() is like fromtimestamp(time.time()), so
746 # prove it.
747 for dummy in range(3):
748 today = self.theclass.today()
749 ts = time.time()
750 todayagain = self.theclass.fromtimestamp(ts)
751 if today == todayagain:
752 break
753 # There are several legit reasons that could fail:
754 # 1. It recently became midnight, between the today() and the
755 # time() calls.
756 # 2. The platform time() has such fine resolution that we'll
757 # never get the same value twice.
758 # 3. The platform time() has poor resolution, and we just
759 # happened to call today() right before a resolution quantum
760 # boundary.
761 # 4. The system clock got fiddled between calls.
762 # In any case, wait a little while and try again.
763 time.sleep(0.1)
764
765 # It worked or it didn't. If it didn't, assume it's reason #2, and
766 # let the test pass if they're within half a second of each other.
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000767 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000768 abs(todayagain - today) < timedelta(seconds=0.5))
769
770 def test_weekday(self):
771 for i in range(7):
772 # March 4, 2002 is a Monday
773 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
774 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
775 # January 2, 1956 is a Monday
776 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
777 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
778
779 def test_isocalendar(self):
780 # Check examples from
781 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
782 for i in range(7):
783 d = self.theclass(2003, 12, 22+i)
784 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
785 d = self.theclass(2003, 12, 29) + timedelta(i)
786 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
787 d = self.theclass(2004, 1, 5+i)
788 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
789 d = self.theclass(2009, 12, 21+i)
790 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
791 d = self.theclass(2009, 12, 28) + timedelta(i)
792 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
793 d = self.theclass(2010, 1, 4+i)
794 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
795
796 def test_iso_long_years(self):
797 # Calculate long ISO years and compare to table from
798 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
799 ISO_LONG_YEARS_TABLE = """
800 4 32 60 88
801 9 37 65 93
802 15 43 71 99
803 20 48 76
804 26 54 82
805
806 105 133 161 189
807 111 139 167 195
808 116 144 172
809 122 150 178
810 128 156 184
811
812 201 229 257 285
813 207 235 263 291
814 212 240 268 296
815 218 246 274
816 224 252 280
817
818 303 331 359 387
819 308 336 364 392
820 314 342 370 398
821 320 348 376
822 325 353 381
823 """
824 iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
825 iso_long_years.sort()
826 L = []
827 for i in range(400):
828 d = self.theclass(2000+i, 12, 31)
829 d1 = self.theclass(1600+i, 12, 31)
830 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
831 if d.isocalendar()[1] == 53:
832 L.append(i)
833 self.assertEqual(L, iso_long_years)
834
835 def test_isoformat(self):
836 t = self.theclass(2, 3, 2)
837 self.assertEqual(t.isoformat(), "0002-03-02")
838
839 def test_ctime(self):
840 t = self.theclass(2002, 3, 2)
841 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
842
843 def test_strftime(self):
844 t = self.theclass(2005, 3, 2)
845 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +0000846 self.assertEqual(t.strftime(""), "") # SF bug #761337
Georg Brandl4ddfcd32006-09-30 11:17:34 +0000847 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +0000848
849 self.assertRaises(TypeError, t.strftime) # needs an arg
850 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
851 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
852
Gregory P. Smith137d8242008-06-02 04:05:52 +0000853 # test that unicode input is allowed (issue 2782)
854 self.assertEqual(t.strftime(u"%m"), "03")
855
Tim Peters2a799bf2002-12-16 20:18:38 +0000856 # A naive object replaces %z and %Z w/ empty strings.
857 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
858
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000859 #make sure that invalid format specifiers are handled correctly
Kristján Valur Jónsson8adc0b52009-01-15 09:09:13 +0000860 #self.assertRaises(ValueError, t.strftime, "%e")
861 #self.assertRaises(ValueError, t.strftime, "%")
862 #self.assertRaises(ValueError, t.strftime, "%#")
863
864 #oh well, some systems just ignore those invalid ones.
865 #at least, excercise them to make sure that no crashes
866 #are generated
867 for f in ["%e", "%", "%#"]:
868 try:
869 t.strftime(f)
870 except ValueError:
871 pass
Kristján Valur Jónsson1c62b652009-01-12 18:09:27 +0000872
873 #check that this standard extension works
874 t.strftime("%f")
875
Gregory P. Smith137d8242008-06-02 04:05:52 +0000876
Eric Smitha9f7d622008-02-17 19:46:49 +0000877 def test_format(self):
878 dt = self.theclass(2007, 9, 10)
879 self.assertEqual(dt.__format__(''), str(dt))
880
881 # check that a derived class's __str__() gets called
882 class A(self.theclass):
883 def __str__(self):
884 return 'A'
885 a = A(2007, 9, 10)
886 self.assertEqual(a.__format__(''), 'A')
887
888 # check that a derived class's strftime gets called
889 class B(self.theclass):
890 def strftime(self, format_spec):
891 return 'B'
892 b = B(2007, 9, 10)
893 self.assertEqual(b.__format__(''), str(dt))
894
895 for fmt in ["m:%m d:%d y:%y",
896 "m:%m d:%d y:%y H:%H M:%M S:%S",
897 "%z %Z",
898 ]:
899 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
900 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
901 self.assertEqual(b.__format__(fmt), 'B')
902
Tim Peters2a799bf2002-12-16 20:18:38 +0000903 def test_resolution_info(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000904 self.assertTrue(isinstance(self.theclass.min, self.theclass))
905 self.assertTrue(isinstance(self.theclass.max, self.theclass))
906 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
907 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000908
909 def test_extreme_timedelta(self):
910 big = self.theclass.max - self.theclass.min
911 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
912 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
913 # n == 315537897599999999 ~= 2**58.13
914 justasbig = timedelta(0, 0, n)
915 self.assertEqual(big, justasbig)
916 self.assertEqual(self.theclass.min + big, self.theclass.max)
917 self.assertEqual(self.theclass.max - big, self.theclass.min)
918
919 def test_timetuple(self):
920 for i in range(7):
921 # January 2, 1956 is a Monday (0)
922 d = self.theclass(1956, 1, 2+i)
923 t = d.timetuple()
924 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
925 # February 1, 1956 is a Wednesday (2)
926 d = self.theclass(1956, 2, 1+i)
927 t = d.timetuple()
928 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
929 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
930 # of the year.
931 d = self.theclass(1956, 3, 1+i)
932 t = d.timetuple()
933 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
934 self.assertEqual(t.tm_year, 1956)
935 self.assertEqual(t.tm_mon, 3)
936 self.assertEqual(t.tm_mday, 1+i)
937 self.assertEqual(t.tm_hour, 0)
938 self.assertEqual(t.tm_min, 0)
939 self.assertEqual(t.tm_sec, 0)
940 self.assertEqual(t.tm_wday, (3+i)%7)
941 self.assertEqual(t.tm_yday, 61+i)
942 self.assertEqual(t.tm_isdst, -1)
943
944 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000945 args = 6, 7, 23
946 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000947 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000948 green = pickler.dumps(orig, proto)
949 derived = unpickler.loads(green)
950 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000951
952 def test_compare(self):
953 t1 = self.theclass(2, 3, 4)
954 t2 = self.theclass(2, 3, 4)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000955 self.assertTrue(t1 == t2)
956 self.assertTrue(t1 <= t2)
957 self.assertTrue(t1 >= t2)
958 self.assertTrue(not t1 != t2)
959 self.assertTrue(not t1 < t2)
960 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000961 self.assertEqual(cmp(t1, t2), 0)
962 self.assertEqual(cmp(t2, t1), 0)
963
964 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
965 t2 = self.theclass(*args) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000966 self.assertTrue(t1 < t2)
967 self.assertTrue(t2 > t1)
968 self.assertTrue(t1 <= t2)
969 self.assertTrue(t2 >= t1)
970 self.assertTrue(t1 != t2)
971 self.assertTrue(t2 != t1)
972 self.assertTrue(not t1 == t2)
973 self.assertTrue(not t2 == t1)
974 self.assertTrue(not t1 > t2)
975 self.assertTrue(not t2 < t1)
976 self.assertTrue(not t1 >= t2)
977 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000978 self.assertEqual(cmp(t1, t2), -1)
979 self.assertEqual(cmp(t2, t1), 1)
980
Tim Peters68124bb2003-02-08 03:46:31 +0000981 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000982 self.assertEqual(t1 == badarg, False)
983 self.assertEqual(t1 != badarg, True)
984 self.assertEqual(badarg == t1, False)
985 self.assertEqual(badarg != t1, True)
986
Tim Peters2a799bf2002-12-16 20:18:38 +0000987 self.assertRaises(TypeError, lambda: t1 < badarg)
988 self.assertRaises(TypeError, lambda: t1 > badarg)
989 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000990 self.assertRaises(TypeError, lambda: badarg <= t1)
991 self.assertRaises(TypeError, lambda: badarg < t1)
992 self.assertRaises(TypeError, lambda: badarg > t1)
993 self.assertRaises(TypeError, lambda: badarg >= t1)
994
Tim Peters8d81a012003-01-24 22:36:34 +0000995 def test_mixed_compare(self):
996 our = self.theclass(2000, 4, 5)
997 self.assertRaises(TypeError, cmp, our, 1)
998 self.assertRaises(TypeError, cmp, 1, our)
999
1000 class AnotherDateTimeClass(object):
1001 def __cmp__(self, other):
1002 # Return "equal" so calling this can't be confused with
1003 # compare-by-address (which never says "equal" for distinct
1004 # objects).
1005 return 0
Nick Coghlan48361f52008-08-11 15:45:58 +00001006 __hash__ = None # Silence Py3k warning
Tim Peters8d81a012003-01-24 22:36:34 +00001007
1008 # This still errors, because date and datetime comparison raise
1009 # TypeError instead of NotImplemented when they don't know what to
1010 # do, in order to stop comparison from falling back to the default
1011 # compare-by-address.
1012 their = AnotherDateTimeClass()
1013 self.assertRaises(TypeError, cmp, our, their)
1014 # Oops: The next stab raises TypeError in the C implementation,
1015 # but not in the Python implementation of datetime. The difference
1016 # is due to that the Python implementation defines __cmp__ but
1017 # the C implementation defines tp_richcompare. This is more pain
1018 # to fix than it's worth, so commenting out the test.
1019 # self.assertEqual(cmp(their, our), 0)
1020
1021 # But date and datetime comparison return NotImplemented instead if the
1022 # other object has a timetuple attr. This gives the other object a
1023 # chance to do the comparison.
1024 class Comparable(AnotherDateTimeClass):
1025 def timetuple(self):
1026 return ()
1027
1028 their = Comparable()
1029 self.assertEqual(cmp(our, their), 0)
1030 self.assertEqual(cmp(their, our), 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001031 self.assertTrue(our == their)
1032 self.assertTrue(their == our)
Tim Peters8d81a012003-01-24 22:36:34 +00001033
Tim Peters2a799bf2002-12-16 20:18:38 +00001034 def test_bool(self):
1035 # All dates are considered true.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001036 self.assertTrue(self.theclass.min)
1037 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001038
Guido van Rossum966bb8c2007-08-24 14:53:14 +00001039 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001040 # For nasty technical reasons, we can't handle years before 1900.
1041 cls = self.theclass
1042 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1043 for y in 1, 49, 51, 99, 100, 1000, 1899:
1044 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001045
1046 def test_replace(self):
1047 cls = self.theclass
1048 args = [1, 2, 3]
1049 base = cls(*args)
1050 self.assertEqual(base, base.replace())
1051
1052 i = 0
1053 for name, newval in (("year", 2),
1054 ("month", 3),
1055 ("day", 4)):
1056 newargs = args[:]
1057 newargs[i] = newval
1058 expected = cls(*newargs)
1059 got = base.replace(**{name: newval})
1060 self.assertEqual(expected, got)
1061 i += 1
1062
1063 # Out of bounds.
1064 base = cls(2000, 2, 29)
1065 self.assertRaises(ValueError, base.replace, year=2001)
1066
Tim Petersa98924a2003-05-17 05:55:19 +00001067 def test_subclass_date(self):
1068
1069 class C(self.theclass):
1070 theAnswer = 42
1071
1072 def __new__(cls, *args, **kws):
1073 temp = kws.copy()
1074 extra = temp.pop('extra')
1075 result = self.theclass.__new__(cls, *args, **temp)
1076 result.extra = extra
1077 return result
1078
1079 def newmeth(self, start):
1080 return start + self.year + self.month
1081
1082 args = 2003, 4, 14
1083
1084 dt1 = self.theclass(*args)
1085 dt2 = C(*args, **{'extra': 7})
1086
1087 self.assertEqual(dt2.__class__, C)
1088 self.assertEqual(dt2.theAnswer, 42)
1089 self.assertEqual(dt2.extra, 7)
1090 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1091 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1092
Tim Peters604c0132004-06-07 23:04:33 +00001093 def test_pickling_subclass_date(self):
1094
1095 args = 6, 7, 23
1096 orig = SubclassDate(*args)
1097 for pickler, unpickler, proto in pickle_choices:
1098 green = pickler.dumps(orig, proto)
1099 derived = unpickler.loads(green)
1100 self.assertEqual(orig, derived)
1101
Tim Peters3f606292004-03-21 23:38:41 +00001102 def test_backdoor_resistance(self):
1103 # For fast unpickling, the constructor accepts a pickle string.
1104 # This is a low-overhead backdoor. A user can (by intent or
1105 # mistake) pass a string directly, which (if it's the right length)
1106 # will get treated like a pickle, and bypass the normal sanity
1107 # checks in the constructor. This can create insane objects.
1108 # The constructor doesn't want to burn the time to validate all
1109 # fields, but does check the month field. This stops, e.g.,
1110 # datetime.datetime('1995-03-25') from yielding an insane object.
1111 base = '1995-03-25'
1112 if not issubclass(self.theclass, datetime):
1113 base = base[:4]
1114 for month_byte in '9', chr(0), chr(13), '\xff':
1115 self.assertRaises(TypeError, self.theclass,
1116 base[:2] + month_byte + base[3:])
1117 for ord_byte in range(1, 13):
1118 # This shouldn't blow up because of the month byte alone. If
1119 # the implementation changes to do more-careful checking, it may
1120 # blow up because other fields are insane.
1121 self.theclass(base[:2] + chr(ord_byte) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001122
Tim Peters2a799bf2002-12-16 20:18:38 +00001123#############################################################################
1124# datetime tests
1125
Tim Peters604c0132004-06-07 23:04:33 +00001126class SubclassDatetime(datetime):
1127 sub_var = 1
1128
Tim Peters2a799bf2002-12-16 20:18:38 +00001129class TestDateTime(TestDate):
1130
1131 theclass = datetime
1132
1133 def test_basic_attributes(self):
1134 dt = self.theclass(2002, 3, 1, 12, 0)
1135 self.assertEqual(dt.year, 2002)
1136 self.assertEqual(dt.month, 3)
1137 self.assertEqual(dt.day, 1)
1138 self.assertEqual(dt.hour, 12)
1139 self.assertEqual(dt.minute, 0)
1140 self.assertEqual(dt.second, 0)
1141 self.assertEqual(dt.microsecond, 0)
1142
1143 def test_basic_attributes_nonzero(self):
1144 # Make sure all attributes are non-zero so bugs in
1145 # bit-shifting access show up.
1146 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1147 self.assertEqual(dt.year, 2002)
1148 self.assertEqual(dt.month, 3)
1149 self.assertEqual(dt.day, 1)
1150 self.assertEqual(dt.hour, 12)
1151 self.assertEqual(dt.minute, 59)
1152 self.assertEqual(dt.second, 59)
1153 self.assertEqual(dt.microsecond, 8000)
1154
1155 def test_roundtrip(self):
1156 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1157 self.theclass.now()):
1158 # Verify dt -> string -> datetime identity.
1159 s = repr(dt)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001160 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001161 s = s[9:]
1162 dt2 = eval(s)
1163 self.assertEqual(dt, dt2)
1164
1165 # Verify identity via reconstructing from pieces.
1166 dt2 = self.theclass(dt.year, dt.month, dt.day,
1167 dt.hour, dt.minute, dt.second,
1168 dt.microsecond)
1169 self.assertEqual(dt, dt2)
1170
1171 def test_isoformat(self):
1172 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1173 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1174 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1175 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1176 # str is ISO format with the separator forced to a blank.
1177 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1178
1179 t = self.theclass(2, 3, 2)
1180 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1181 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1182 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1183 # str is ISO format with the separator forced to a blank.
1184 self.assertEqual(str(t), "0002-03-02 00:00:00")
1185
Eric Smitha9f7d622008-02-17 19:46:49 +00001186 def test_format(self):
1187 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1188 self.assertEqual(dt.__format__(''), str(dt))
1189
1190 # check that a derived class's __str__() gets called
1191 class A(self.theclass):
1192 def __str__(self):
1193 return 'A'
1194 a = A(2007, 9, 10, 4, 5, 1, 123)
1195 self.assertEqual(a.__format__(''), 'A')
1196
1197 # check that a derived class's strftime gets called
1198 class B(self.theclass):
1199 def strftime(self, format_spec):
1200 return 'B'
1201 b = B(2007, 9, 10, 4, 5, 1, 123)
1202 self.assertEqual(b.__format__(''), str(dt))
1203
1204 for fmt in ["m:%m d:%d y:%y",
1205 "m:%m d:%d y:%y H:%H M:%M S:%S",
1206 "%z %Z",
1207 ]:
1208 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1209 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1210 self.assertEqual(b.__format__(fmt), 'B')
1211
Tim Peters2a799bf2002-12-16 20:18:38 +00001212 def test_more_ctime(self):
1213 # Test fields that TestDate doesn't touch.
1214 import time
1215
1216 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1217 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1218 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1219 # out. The difference is that t.ctime() produces " 2" for the day,
1220 # but platform ctime() produces "02" for the day. According to
1221 # C99, t.ctime() is correct here.
1222 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1223
1224 # So test a case where that difference doesn't matter.
1225 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1226 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1227
1228 def test_tz_independent_comparing(self):
1229 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1230 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1231 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1232 self.assertEqual(dt1, dt3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001233 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001234
1235 # Make sure comparison doesn't forget microseconds, and isn't done
1236 # via comparing a float timestamp (an IEEE double doesn't have enough
1237 # precision to span microsecond resolution across years 1 thru 9999,
1238 # so comparing via timestamp necessarily calls some distinct values
1239 # equal).
1240 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1241 us = timedelta(microseconds=1)
1242 dt2 = dt1 + us
1243 self.assertEqual(dt2 - dt1, us)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001244 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001245
Neal Norwitzd5b0c9b2006-03-20 01:58:39 +00001246 def test_strftime_with_bad_tzname_replace(self):
1247 # verify ok if tzinfo.tzname().replace() returns a non-string
1248 class MyTzInfo(FixedOffset):
1249 def tzname(self, dt):
1250 class MyStr(str):
1251 def replace(self, *args):
1252 return None
1253 return MyStr('name')
1254 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1255 self.assertRaises(TypeError, t.strftime, '%Z')
1256
Tim Peters2a799bf2002-12-16 20:18:38 +00001257 def test_bad_constructor_arguments(self):
1258 # bad years
1259 self.theclass(MINYEAR, 1, 1) # no exception
1260 self.theclass(MAXYEAR, 1, 1) # no exception
1261 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1262 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1263 # bad months
1264 self.theclass(2000, 1, 1) # no exception
1265 self.theclass(2000, 12, 1) # no exception
1266 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1267 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1268 # bad days
1269 self.theclass(2000, 2, 29) # no exception
1270 self.theclass(2004, 2, 29) # no exception
1271 self.theclass(2400, 2, 29) # no exception
1272 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1273 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1274 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1275 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1276 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1277 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1278 # bad hours
1279 self.theclass(2000, 1, 31, 0) # no exception
1280 self.theclass(2000, 1, 31, 23) # no exception
1281 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1282 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1283 # bad minutes
1284 self.theclass(2000, 1, 31, 23, 0) # no exception
1285 self.theclass(2000, 1, 31, 23, 59) # no exception
1286 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1287 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1288 # bad seconds
1289 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1290 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1291 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1292 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1293 # bad microseconds
1294 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1295 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1296 self.assertRaises(ValueError, self.theclass,
1297 2000, 1, 31, 23, 59, 59, -1)
1298 self.assertRaises(ValueError, self.theclass,
1299 2000, 1, 31, 23, 59, 59,
1300 1000000)
1301
1302 def test_hash_equality(self):
1303 d = self.theclass(2000, 12, 31, 23, 30, 17)
1304 e = self.theclass(2000, 12, 31, 23, 30, 17)
1305 self.assertEqual(d, e)
1306 self.assertEqual(hash(d), hash(e))
1307
1308 dic = {d: 1}
1309 dic[e] = 2
1310 self.assertEqual(len(dic), 1)
1311 self.assertEqual(dic[d], 2)
1312 self.assertEqual(dic[e], 2)
1313
1314 d = self.theclass(2001, 1, 1, 0, 5, 17)
1315 e = self.theclass(2001, 1, 1, 0, 5, 17)
1316 self.assertEqual(d, e)
1317 self.assertEqual(hash(d), hash(e))
1318
1319 dic = {d: 1}
1320 dic[e] = 2
1321 self.assertEqual(len(dic), 1)
1322 self.assertEqual(dic[d], 2)
1323 self.assertEqual(dic[e], 2)
1324
1325 def test_computations(self):
1326 a = self.theclass(2002, 1, 31)
1327 b = self.theclass(1956, 1, 31)
1328 diff = a-b
1329 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1330 self.assertEqual(diff.seconds, 0)
1331 self.assertEqual(diff.microseconds, 0)
1332 a = self.theclass(2002, 3, 2, 17, 6)
1333 millisec = timedelta(0, 0, 1000)
1334 hour = timedelta(0, 3600)
1335 day = timedelta(1)
1336 week = timedelta(7)
1337 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1338 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1339 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1340 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1341 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1342 self.assertEqual(a - hour, a + -hour)
1343 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1344 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1345 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1346 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1347 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1348 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1349 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1350 self.assertEqual((a + week) - a, week)
1351 self.assertEqual((a + day) - a, day)
1352 self.assertEqual((a + hour) - a, hour)
1353 self.assertEqual((a + millisec) - a, millisec)
1354 self.assertEqual((a - week) - a, -week)
1355 self.assertEqual((a - day) - a, -day)
1356 self.assertEqual((a - hour) - a, -hour)
1357 self.assertEqual((a - millisec) - a, -millisec)
1358 self.assertEqual(a - (a + week), -week)
1359 self.assertEqual(a - (a + day), -day)
1360 self.assertEqual(a - (a + hour), -hour)
1361 self.assertEqual(a - (a + millisec), -millisec)
1362 self.assertEqual(a - (a - week), week)
1363 self.assertEqual(a - (a - day), day)
1364 self.assertEqual(a - (a - hour), hour)
1365 self.assertEqual(a - (a - millisec), millisec)
1366 self.assertEqual(a + (week + day + hour + millisec),
1367 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1368 self.assertEqual(a + (week + day + hour + millisec),
1369 (((a + week) + day) + hour) + millisec)
1370 self.assertEqual(a - (week + day + hour + millisec),
1371 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1372 self.assertEqual(a - (week + day + hour + millisec),
1373 (((a - week) - day) - hour) - millisec)
1374 # Add/sub ints, longs, floats should be illegal
1375 for i in 1, 1L, 1.0:
1376 self.assertRaises(TypeError, lambda: a+i)
1377 self.assertRaises(TypeError, lambda: a-i)
1378 self.assertRaises(TypeError, lambda: i+a)
1379 self.assertRaises(TypeError, lambda: i-a)
1380
1381 # delta - datetime is senseless.
1382 self.assertRaises(TypeError, lambda: day - a)
1383 # mixing datetime and (delta or datetime) via * or // is senseless
1384 self.assertRaises(TypeError, lambda: day * a)
1385 self.assertRaises(TypeError, lambda: a * day)
1386 self.assertRaises(TypeError, lambda: day // a)
1387 self.assertRaises(TypeError, lambda: a // day)
1388 self.assertRaises(TypeError, lambda: a * a)
1389 self.assertRaises(TypeError, lambda: a // a)
1390 # datetime + datetime is senseless
1391 self.assertRaises(TypeError, lambda: a + a)
1392
1393 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001394 args = 6, 7, 23, 20, 59, 1, 64**2
1395 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001396 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001397 green = pickler.dumps(orig, proto)
1398 derived = unpickler.loads(green)
1399 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001400
Guido van Rossum275666f2003-02-07 21:49:01 +00001401 def test_more_pickling(self):
1402 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1403 s = pickle.dumps(a)
1404 b = pickle.loads(s)
1405 self.assertEqual(b.year, 2003)
1406 self.assertEqual(b.month, 2)
1407 self.assertEqual(b.day, 7)
1408
Tim Peters604c0132004-06-07 23:04:33 +00001409 def test_pickling_subclass_datetime(self):
1410 args = 6, 7, 23, 20, 59, 1, 64**2
1411 orig = SubclassDatetime(*args)
1412 for pickler, unpickler, proto in pickle_choices:
1413 green = pickler.dumps(orig, proto)
1414 derived = unpickler.loads(green)
1415 self.assertEqual(orig, derived)
1416
Tim Peters2a799bf2002-12-16 20:18:38 +00001417 def test_more_compare(self):
1418 # The test_compare() inherited from TestDate covers the error cases.
1419 # We just want to test lexicographic ordering on the members datetime
1420 # has that date lacks.
1421 args = [2000, 11, 29, 20, 58, 16, 999998]
1422 t1 = self.theclass(*args)
1423 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001424 self.assertTrue(t1 == t2)
1425 self.assertTrue(t1 <= t2)
1426 self.assertTrue(t1 >= t2)
1427 self.assertTrue(not t1 != t2)
1428 self.assertTrue(not t1 < t2)
1429 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001430 self.assertEqual(cmp(t1, t2), 0)
1431 self.assertEqual(cmp(t2, t1), 0)
1432
1433 for i in range(len(args)):
1434 newargs = args[:]
1435 newargs[i] = args[i] + 1
1436 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001437 self.assertTrue(t1 < t2)
1438 self.assertTrue(t2 > t1)
1439 self.assertTrue(t1 <= t2)
1440 self.assertTrue(t2 >= t1)
1441 self.assertTrue(t1 != t2)
1442 self.assertTrue(t2 != t1)
1443 self.assertTrue(not t1 == t2)
1444 self.assertTrue(not t2 == t1)
1445 self.assertTrue(not t1 > t2)
1446 self.assertTrue(not t2 < t1)
1447 self.assertTrue(not t1 >= t2)
1448 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001449 self.assertEqual(cmp(t1, t2), -1)
1450 self.assertEqual(cmp(t2, t1), 1)
1451
1452
1453 # A helper for timestamp constructor tests.
1454 def verify_field_equality(self, expected, got):
1455 self.assertEqual(expected.tm_year, got.year)
1456 self.assertEqual(expected.tm_mon, got.month)
1457 self.assertEqual(expected.tm_mday, got.day)
1458 self.assertEqual(expected.tm_hour, got.hour)
1459 self.assertEqual(expected.tm_min, got.minute)
1460 self.assertEqual(expected.tm_sec, got.second)
1461
1462 def test_fromtimestamp(self):
1463 import time
1464
1465 ts = time.time()
1466 expected = time.localtime(ts)
1467 got = self.theclass.fromtimestamp(ts)
1468 self.verify_field_equality(expected, got)
1469
1470 def test_utcfromtimestamp(self):
1471 import time
1472
1473 ts = time.time()
1474 expected = time.gmtime(ts)
1475 got = self.theclass.utcfromtimestamp(ts)
1476 self.verify_field_equality(expected, got)
1477
Georg Brandl6d78a582006-04-28 19:09:24 +00001478 def test_microsecond_rounding(self):
1479 # Test whether fromtimestamp "rounds up" floats that are less
1480 # than one microsecond smaller than an integer.
1481 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1482 self.theclass.fromtimestamp(1))
1483
Tim Peters1b6f7a92004-06-20 02:50:16 +00001484 def test_insane_fromtimestamp(self):
1485 # It's possible that some platform maps time_t to double,
1486 # and that this test will fail there. This test should
1487 # exempt such platforms (provided they return reasonable
1488 # results!).
1489 for insane in -1e200, 1e200:
1490 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1491 insane)
1492
1493 def test_insane_utcfromtimestamp(self):
1494 # It's possible that some platform maps time_t to double,
1495 # and that this test will fail there. This test should
1496 # exempt such platforms (provided they return reasonable
1497 # results!).
1498 for insane in -1e200, 1e200:
1499 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1500 insane)
1501
Guido van Rossum2054ee92007-03-06 15:50:01 +00001502 def test_negative_float_fromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001503 # Windows doesn't accept negative timestamps
1504 if os.name == "nt":
1505 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001506 # The result is tz-dependent; at least test that this doesn't
1507 # fail (like it did before bug 1646728 was fixed).
1508 self.theclass.fromtimestamp(-1.05)
1509
1510 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumf1200f82007-03-07 15:16:29 +00001511 # Windows doesn't accept negative timestamps
1512 if os.name == "nt":
1513 return
Guido van Rossum2054ee92007-03-06 15:50:01 +00001514 d = self.theclass.utcfromtimestamp(-1.05)
1515 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1516
Tim Peters2a799bf2002-12-16 20:18:38 +00001517 def test_utcnow(self):
1518 import time
1519
1520 # Call it a success if utcnow() and utcfromtimestamp() are within
1521 # a second of each other.
1522 tolerance = timedelta(seconds=1)
1523 for dummy in range(3):
1524 from_now = self.theclass.utcnow()
1525 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1526 if abs(from_timestamp - from_now) <= tolerance:
1527 break
1528 # Else try again a few times.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001529 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001530
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001531 def test_strptime(self):
Skip Montanarofc070d22008-03-15 16:04:45 +00001532 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001533
Skip Montanarofc070d22008-03-15 16:04:45 +00001534 string = '2004-12-01 13:02:47.197'
1535 format = '%Y-%m-%d %H:%M:%S.%f'
1536 result, frac = _strptime._strptime(string, format)
1537 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001538 got = self.theclass.strptime(string, format)
1539 self.assertEqual(expected, got)
1540
Tim Peters2a799bf2002-12-16 20:18:38 +00001541 def test_more_timetuple(self):
1542 # This tests fields beyond those tested by the TestDate.test_timetuple.
1543 t = self.theclass(2004, 12, 31, 6, 22, 33)
1544 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1545 self.assertEqual(t.timetuple(),
1546 (t.year, t.month, t.day,
1547 t.hour, t.minute, t.second,
1548 t.weekday(),
1549 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1550 -1))
1551 tt = t.timetuple()
1552 self.assertEqual(tt.tm_year, t.year)
1553 self.assertEqual(tt.tm_mon, t.month)
1554 self.assertEqual(tt.tm_mday, t.day)
1555 self.assertEqual(tt.tm_hour, t.hour)
1556 self.assertEqual(tt.tm_min, t.minute)
1557 self.assertEqual(tt.tm_sec, t.second)
1558 self.assertEqual(tt.tm_wday, t.weekday())
1559 self.assertEqual(tt.tm_yday, t.toordinal() -
1560 date(t.year, 1, 1).toordinal() + 1)
1561 self.assertEqual(tt.tm_isdst, -1)
1562
1563 def test_more_strftime(self):
1564 # This tests fields beyond those tested by the TestDate.test_strftime.
Skip Montanarofc070d22008-03-15 16:04:45 +00001565 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1566 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1567 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001568
1569 def test_extract(self):
1570 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1571 self.assertEqual(dt.date(), date(2002, 3, 4))
1572 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1573
1574 def test_combine(self):
1575 d = date(2002, 3, 4)
1576 t = time(18, 45, 3, 1234)
1577 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1578 combine = self.theclass.combine
1579 dt = combine(d, t)
1580 self.assertEqual(dt, expected)
1581
1582 dt = combine(time=t, date=d)
1583 self.assertEqual(dt, expected)
1584
1585 self.assertEqual(d, dt.date())
1586 self.assertEqual(t, dt.time())
1587 self.assertEqual(dt, combine(dt.date(), dt.time()))
1588
1589 self.assertRaises(TypeError, combine) # need an arg
1590 self.assertRaises(TypeError, combine, d) # need two args
1591 self.assertRaises(TypeError, combine, t, d) # args reversed
1592 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1593 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1594
Tim Peters12bf3392002-12-24 05:41:27 +00001595 def test_replace(self):
1596 cls = self.theclass
1597 args = [1, 2, 3, 4, 5, 6, 7]
1598 base = cls(*args)
1599 self.assertEqual(base, base.replace())
1600
1601 i = 0
1602 for name, newval in (("year", 2),
1603 ("month", 3),
1604 ("day", 4),
1605 ("hour", 5),
1606 ("minute", 6),
1607 ("second", 7),
1608 ("microsecond", 8)):
1609 newargs = args[:]
1610 newargs[i] = newval
1611 expected = cls(*newargs)
1612 got = base.replace(**{name: newval})
1613 self.assertEqual(expected, got)
1614 i += 1
1615
1616 # Out of bounds.
1617 base = cls(2000, 2, 29)
1618 self.assertRaises(ValueError, base.replace, year=2001)
1619
Tim Peters80475bb2002-12-25 07:40:55 +00001620 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001621 # Pretty boring! The TZ test is more interesting here. astimezone()
1622 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001623 dt = self.theclass.now()
1624 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001625 self.assertRaises(TypeError, dt.astimezone) # not enough args
1626 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1627 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001628 self.assertRaises(ValueError, dt.astimezone, f) # naive
1629 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001630
Tim Peters52dcce22003-01-23 16:36:11 +00001631 class Bogus(tzinfo):
1632 def utcoffset(self, dt): return None
1633 def dst(self, dt): return timedelta(0)
1634 bog = Bogus()
1635 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1636
1637 class AlsoBogus(tzinfo):
1638 def utcoffset(self, dt): return timedelta(0)
1639 def dst(self, dt): return None
1640 alsobog = AlsoBogus()
1641 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001642
Tim Petersa98924a2003-05-17 05:55:19 +00001643 def test_subclass_datetime(self):
1644
1645 class C(self.theclass):
1646 theAnswer = 42
1647
1648 def __new__(cls, *args, **kws):
1649 temp = kws.copy()
1650 extra = temp.pop('extra')
1651 result = self.theclass.__new__(cls, *args, **temp)
1652 result.extra = extra
1653 return result
1654
1655 def newmeth(self, start):
1656 return start + self.year + self.month + self.second
1657
1658 args = 2003, 4, 14, 12, 13, 41
1659
1660 dt1 = self.theclass(*args)
1661 dt2 = C(*args, **{'extra': 7})
1662
1663 self.assertEqual(dt2.__class__, C)
1664 self.assertEqual(dt2.theAnswer, 42)
1665 self.assertEqual(dt2.extra, 7)
1666 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1667 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1668 dt1.second - 7)
1669
Tim Peters604c0132004-06-07 23:04:33 +00001670class SubclassTime(time):
1671 sub_var = 1
1672
Collin Winterc2898c52007-04-25 17:29:52 +00001673class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001674
1675 theclass = time
1676
1677 def test_basic_attributes(self):
1678 t = self.theclass(12, 0)
1679 self.assertEqual(t.hour, 12)
1680 self.assertEqual(t.minute, 0)
1681 self.assertEqual(t.second, 0)
1682 self.assertEqual(t.microsecond, 0)
1683
1684 def test_basic_attributes_nonzero(self):
1685 # Make sure all attributes are non-zero so bugs in
1686 # bit-shifting access show up.
1687 t = self.theclass(12, 59, 59, 8000)
1688 self.assertEqual(t.hour, 12)
1689 self.assertEqual(t.minute, 59)
1690 self.assertEqual(t.second, 59)
1691 self.assertEqual(t.microsecond, 8000)
1692
1693 def test_roundtrip(self):
1694 t = self.theclass(1, 2, 3, 4)
1695
1696 # Verify t -> string -> time identity.
1697 s = repr(t)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001698 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001699 s = s[9:]
1700 t2 = eval(s)
1701 self.assertEqual(t, t2)
1702
1703 # Verify identity via reconstructing from pieces.
1704 t2 = self.theclass(t.hour, t.minute, t.second,
1705 t.microsecond)
1706 self.assertEqual(t, t2)
1707
1708 def test_comparing(self):
1709 args = [1, 2, 3, 4]
1710 t1 = self.theclass(*args)
1711 t2 = self.theclass(*args)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001712 self.assertTrue(t1 == t2)
1713 self.assertTrue(t1 <= t2)
1714 self.assertTrue(t1 >= t2)
1715 self.assertTrue(not t1 != t2)
1716 self.assertTrue(not t1 < t2)
1717 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001718 self.assertEqual(cmp(t1, t2), 0)
1719 self.assertEqual(cmp(t2, t1), 0)
1720
1721 for i in range(len(args)):
1722 newargs = args[:]
1723 newargs[i] = args[i] + 1
1724 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001725 self.assertTrue(t1 < t2)
1726 self.assertTrue(t2 > t1)
1727 self.assertTrue(t1 <= t2)
1728 self.assertTrue(t2 >= t1)
1729 self.assertTrue(t1 != t2)
1730 self.assertTrue(t2 != t1)
1731 self.assertTrue(not t1 == t2)
1732 self.assertTrue(not t2 == t1)
1733 self.assertTrue(not t1 > t2)
1734 self.assertTrue(not t2 < t1)
1735 self.assertTrue(not t1 >= t2)
1736 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001737 self.assertEqual(cmp(t1, t2), -1)
1738 self.assertEqual(cmp(t2, t1), 1)
1739
Tim Peters68124bb2003-02-08 03:46:31 +00001740 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001741 self.assertEqual(t1 == badarg, False)
1742 self.assertEqual(t1 != badarg, True)
1743 self.assertEqual(badarg == t1, False)
1744 self.assertEqual(badarg != t1, True)
1745
Tim Peters2a799bf2002-12-16 20:18:38 +00001746 self.assertRaises(TypeError, lambda: t1 <= badarg)
1747 self.assertRaises(TypeError, lambda: t1 < badarg)
1748 self.assertRaises(TypeError, lambda: t1 > badarg)
1749 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001750 self.assertRaises(TypeError, lambda: badarg <= t1)
1751 self.assertRaises(TypeError, lambda: badarg < t1)
1752 self.assertRaises(TypeError, lambda: badarg > t1)
1753 self.assertRaises(TypeError, lambda: badarg >= t1)
1754
1755 def test_bad_constructor_arguments(self):
1756 # bad hours
1757 self.theclass(0, 0) # no exception
1758 self.theclass(23, 0) # no exception
1759 self.assertRaises(ValueError, self.theclass, -1, 0)
1760 self.assertRaises(ValueError, self.theclass, 24, 0)
1761 # bad minutes
1762 self.theclass(23, 0) # no exception
1763 self.theclass(23, 59) # no exception
1764 self.assertRaises(ValueError, self.theclass, 23, -1)
1765 self.assertRaises(ValueError, self.theclass, 23, 60)
1766 # bad seconds
1767 self.theclass(23, 59, 0) # no exception
1768 self.theclass(23, 59, 59) # no exception
1769 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1770 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1771 # bad microseconds
1772 self.theclass(23, 59, 59, 0) # no exception
1773 self.theclass(23, 59, 59, 999999) # no exception
1774 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1775 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1776
1777 def test_hash_equality(self):
1778 d = self.theclass(23, 30, 17)
1779 e = self.theclass(23, 30, 17)
1780 self.assertEqual(d, e)
1781 self.assertEqual(hash(d), hash(e))
1782
1783 dic = {d: 1}
1784 dic[e] = 2
1785 self.assertEqual(len(dic), 1)
1786 self.assertEqual(dic[d], 2)
1787 self.assertEqual(dic[e], 2)
1788
1789 d = self.theclass(0, 5, 17)
1790 e = self.theclass(0, 5, 17)
1791 self.assertEqual(d, e)
1792 self.assertEqual(hash(d), hash(e))
1793
1794 dic = {d: 1}
1795 dic[e] = 2
1796 self.assertEqual(len(dic), 1)
1797 self.assertEqual(dic[d], 2)
1798 self.assertEqual(dic[e], 2)
1799
1800 def test_isoformat(self):
1801 t = self.theclass(4, 5, 1, 123)
1802 self.assertEqual(t.isoformat(), "04:05:01.000123")
1803 self.assertEqual(t.isoformat(), str(t))
1804
1805 t = self.theclass()
1806 self.assertEqual(t.isoformat(), "00:00:00")
1807 self.assertEqual(t.isoformat(), str(t))
1808
1809 t = self.theclass(microsecond=1)
1810 self.assertEqual(t.isoformat(), "00:00:00.000001")
1811 self.assertEqual(t.isoformat(), str(t))
1812
1813 t = self.theclass(microsecond=10)
1814 self.assertEqual(t.isoformat(), "00:00:00.000010")
1815 self.assertEqual(t.isoformat(), str(t))
1816
1817 t = self.theclass(microsecond=100)
1818 self.assertEqual(t.isoformat(), "00:00:00.000100")
1819 self.assertEqual(t.isoformat(), str(t))
1820
1821 t = self.theclass(microsecond=1000)
1822 self.assertEqual(t.isoformat(), "00:00:00.001000")
1823 self.assertEqual(t.isoformat(), str(t))
1824
1825 t = self.theclass(microsecond=10000)
1826 self.assertEqual(t.isoformat(), "00:00:00.010000")
1827 self.assertEqual(t.isoformat(), str(t))
1828
1829 t = self.theclass(microsecond=100000)
1830 self.assertEqual(t.isoformat(), "00:00:00.100000")
1831 self.assertEqual(t.isoformat(), str(t))
1832
Martin v. Löwis4c11a922007-02-08 09:13:36 +00001833 def test_1653736(self):
1834 # verify it doesn't accept extra keyword arguments
1835 t = self.theclass(second=1)
1836 self.assertRaises(TypeError, t.isoformat, foo=3)
1837
Tim Peters2a799bf2002-12-16 20:18:38 +00001838 def test_strftime(self):
1839 t = self.theclass(1, 2, 3, 4)
Skip Montanarofc070d22008-03-15 16:04:45 +00001840 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00001841 # A naive object replaces %z and %Z with empty strings.
1842 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1843
Eric Smitha9f7d622008-02-17 19:46:49 +00001844 def test_format(self):
1845 t = self.theclass(1, 2, 3, 4)
1846 self.assertEqual(t.__format__(''), str(t))
1847
1848 # check that a derived class's __str__() gets called
1849 class A(self.theclass):
1850 def __str__(self):
1851 return 'A'
1852 a = A(1, 2, 3, 4)
1853 self.assertEqual(a.__format__(''), 'A')
1854
1855 # check that a derived class's strftime gets called
1856 class B(self.theclass):
1857 def strftime(self, format_spec):
1858 return 'B'
1859 b = B(1, 2, 3, 4)
1860 self.assertEqual(b.__format__(''), str(t))
1861
1862 for fmt in ['%H %M %S',
1863 ]:
1864 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1865 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1866 self.assertEqual(b.__format__(fmt), 'B')
1867
Tim Peters2a799bf2002-12-16 20:18:38 +00001868 def test_str(self):
1869 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
1870 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1871 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1872 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
1873 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
1874
1875 def test_repr(self):
1876 name = 'datetime.' + self.theclass.__name__
1877 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
1878 "%s(1, 2, 3, 4)" % name)
1879 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
1880 "%s(10, 2, 3, 4000)" % name)
1881 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
1882 "%s(0, 2, 3, 400000)" % name)
1883 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
1884 "%s(12, 2, 3)" % name)
1885 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
1886 "%s(23, 15)" % name)
1887
1888 def test_resolution_info(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001889 self.assertTrue(isinstance(self.theclass.min, self.theclass))
1890 self.assertTrue(isinstance(self.theclass.max, self.theclass))
1891 self.assertTrue(isinstance(self.theclass.resolution, timedelta))
1892 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001893
1894 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001895 args = 20, 59, 16, 64**2
1896 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001897 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001898 green = pickler.dumps(orig, proto)
1899 derived = unpickler.loads(green)
1900 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001901
Tim Peters604c0132004-06-07 23:04:33 +00001902 def test_pickling_subclass_time(self):
1903 args = 20, 59, 16, 64**2
1904 orig = SubclassTime(*args)
1905 for pickler, unpickler, proto in pickle_choices:
1906 green = pickler.dumps(orig, proto)
1907 derived = unpickler.loads(green)
1908 self.assertEqual(orig, derived)
1909
Tim Peters2a799bf2002-12-16 20:18:38 +00001910 def test_bool(self):
1911 cls = self.theclass
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001912 self.assertTrue(cls(1))
1913 self.assertTrue(cls(0, 1))
1914 self.assertTrue(cls(0, 0, 1))
1915 self.assertTrue(cls(0, 0, 0, 1))
1916 self.assertTrue(not cls(0))
1917 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00001918
Tim Peters12bf3392002-12-24 05:41:27 +00001919 def test_replace(self):
1920 cls = self.theclass
1921 args = [1, 2, 3, 4]
1922 base = cls(*args)
1923 self.assertEqual(base, base.replace())
1924
1925 i = 0
1926 for name, newval in (("hour", 5),
1927 ("minute", 6),
1928 ("second", 7),
1929 ("microsecond", 8)):
1930 newargs = args[:]
1931 newargs[i] = newval
1932 expected = cls(*newargs)
1933 got = base.replace(**{name: newval})
1934 self.assertEqual(expected, got)
1935 i += 1
1936
1937 # Out of bounds.
1938 base = cls(1)
1939 self.assertRaises(ValueError, base.replace, hour=24)
1940 self.assertRaises(ValueError, base.replace, minute=-1)
1941 self.assertRaises(ValueError, base.replace, second=100)
1942 self.assertRaises(ValueError, base.replace, microsecond=1000000)
1943
Tim Petersa98924a2003-05-17 05:55:19 +00001944 def test_subclass_time(self):
1945
1946 class C(self.theclass):
1947 theAnswer = 42
1948
1949 def __new__(cls, *args, **kws):
1950 temp = kws.copy()
1951 extra = temp.pop('extra')
1952 result = self.theclass.__new__(cls, *args, **temp)
1953 result.extra = extra
1954 return result
1955
1956 def newmeth(self, start):
1957 return start + self.hour + self.second
1958
1959 args = 4, 5, 6
1960
1961 dt1 = self.theclass(*args)
1962 dt2 = C(*args, **{'extra': 7})
1963
1964 self.assertEqual(dt2.__class__, C)
1965 self.assertEqual(dt2.theAnswer, 42)
1966 self.assertEqual(dt2.extra, 7)
1967 self.assertEqual(dt1.isoformat(), dt2.isoformat())
1968 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
1969
Armin Rigof4afb212005-11-07 07:15:48 +00001970 def test_backdoor_resistance(self):
1971 # see TestDate.test_backdoor_resistance().
1972 base = '2:59.0'
1973 for hour_byte in ' ', '9', chr(24), '\xff':
1974 self.assertRaises(TypeError, self.theclass,
1975 hour_byte + base[1:])
1976
Tim Peters855fe882002-12-22 03:43:39 +00001977# A mixin for classes with a tzinfo= argument. Subclasses must define
1978# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00001979# must be legit (which is true for time and datetime).
Collin Winterc2898c52007-04-25 17:29:52 +00001980class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00001981
Tim Petersbad8ff02002-12-30 20:52:32 +00001982 def test_argument_passing(self):
1983 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00001984 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00001985 class introspective(tzinfo):
1986 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00001987 def utcoffset(self, dt):
1988 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00001989 dst = utcoffset
1990
1991 obj = cls(1, 2, 3, tzinfo=introspective())
1992
Tim Peters0bf60bd2003-01-08 20:40:01 +00001993 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00001994 self.assertEqual(obj.tzname(), expected)
1995
Tim Peters0bf60bd2003-01-08 20:40:01 +00001996 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00001997 self.assertEqual(obj.utcoffset(), expected)
1998 self.assertEqual(obj.dst(), expected)
1999
Tim Peters855fe882002-12-22 03:43:39 +00002000 def test_bad_tzinfo_classes(self):
2001 cls = self.theclass
2002 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002003
Tim Peters855fe882002-12-22 03:43:39 +00002004 class NiceTry(object):
2005 def __init__(self): pass
2006 def utcoffset(self, dt): pass
2007 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2008
2009 class BetterTry(tzinfo):
2010 def __init__(self): pass
2011 def utcoffset(self, dt): pass
2012 b = BetterTry()
2013 t = cls(1, 1, 1, tzinfo=b)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002014 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002015
2016 def test_utc_offset_out_of_bounds(self):
2017 class Edgy(tzinfo):
2018 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002019 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002020 def utcoffset(self, dt):
2021 return self.offset
2022
2023 cls = self.theclass
2024 for offset, legit in ((-1440, False),
2025 (-1439, True),
2026 (1439, True),
2027 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002028 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002029 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002030 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002031 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002032 else:
2033 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002034 if legit:
2035 aofs = abs(offset)
2036 h, m = divmod(aofs, 60)
2037 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002038 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002039 t = t.timetz()
2040 self.assertEqual(str(t), "01:02:03" + tag)
2041 else:
2042 self.assertRaises(ValueError, str, t)
2043
2044 def test_tzinfo_classes(self):
2045 cls = self.theclass
2046 class C1(tzinfo):
2047 def utcoffset(self, dt): return None
2048 def dst(self, dt): return None
2049 def tzname(self, dt): return None
2050 for t in (cls(1, 1, 1),
2051 cls(1, 1, 1, tzinfo=None),
2052 cls(1, 1, 1, tzinfo=C1())):
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002053 self.assertTrue(t.utcoffset() is None)
2054 self.assertTrue(t.dst() is None)
2055 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002056
Tim Peters855fe882002-12-22 03:43:39 +00002057 class C3(tzinfo):
2058 def utcoffset(self, dt): return timedelta(minutes=-1439)
2059 def dst(self, dt): return timedelta(minutes=1439)
2060 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002061 t = cls(1, 1, 1, tzinfo=C3())
2062 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2063 self.assertEqual(t.dst(), timedelta(minutes=1439))
2064 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002065
2066 # Wrong types.
2067 class C4(tzinfo):
2068 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002069 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002070 def tzname(self, dt): return 0
2071 t = cls(1, 1, 1, tzinfo=C4())
2072 self.assertRaises(TypeError, t.utcoffset)
2073 self.assertRaises(TypeError, t.dst)
2074 self.assertRaises(TypeError, t.tzname)
2075
2076 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002077 class C6(tzinfo):
2078 def utcoffset(self, dt): return timedelta(hours=-24)
2079 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002080 t = cls(1, 1, 1, tzinfo=C6())
2081 self.assertRaises(ValueError, t.utcoffset)
2082 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002083
2084 # Not a whole number of minutes.
2085 class C7(tzinfo):
2086 def utcoffset(self, dt): return timedelta(seconds=61)
2087 def dst(self, dt): return timedelta(microseconds=-81)
2088 t = cls(1, 1, 1, tzinfo=C7())
2089 self.assertRaises(ValueError, t.utcoffset)
2090 self.assertRaises(ValueError, t.dst)
2091
Tim Peters4c0db782002-12-26 05:01:19 +00002092 def test_aware_compare(self):
2093 cls = self.theclass
2094
Tim Peters60c76e42002-12-27 00:41:11 +00002095 # Ensure that utcoffset() gets ignored if the comparands have
2096 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002097 class OperandDependentOffset(tzinfo):
2098 def utcoffset(self, t):
2099 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002100 # d0 and d1 equal after adjustment
2101 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002102 else:
Tim Peters397301e2003-01-02 21:28:08 +00002103 # d2 off in the weeds
2104 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002105
2106 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2107 d0 = base.replace(minute=3)
2108 d1 = base.replace(minute=9)
2109 d2 = base.replace(minute=11)
2110 for x in d0, d1, d2:
2111 for y in d0, d1, d2:
2112 got = cmp(x, y)
Tim Peters60c76e42002-12-27 00:41:11 +00002113 expected = cmp(x.minute, y.minute)
2114 self.assertEqual(got, expected)
2115
2116 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002117 # Note that a time can't actually have an operand-depedent offset,
2118 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2119 # so skip this test for time.
2120 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002121 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2122 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2123 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2124 for x in d0, d1, d2:
2125 for y in d0, d1, d2:
2126 got = cmp(x, y)
2127 if (x is d0 or x is d1) and (y is d0 or y is d1):
2128 expected = 0
2129 elif x is y is d2:
2130 expected = 0
2131 elif x is d2:
2132 expected = -1
2133 else:
2134 assert y is d2
2135 expected = 1
2136 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002137
Tim Peters855fe882002-12-22 03:43:39 +00002138
Tim Peters0bf60bd2003-01-08 20:40:01 +00002139# Testing time objects with a non-None tzinfo.
Collin Winterc2898c52007-04-25 17:29:52 +00002140class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002141 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002142
2143 def test_empty(self):
2144 t = self.theclass()
2145 self.assertEqual(t.hour, 0)
2146 self.assertEqual(t.minute, 0)
2147 self.assertEqual(t.second, 0)
2148 self.assertEqual(t.microsecond, 0)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002149 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002150
Tim Peters2a799bf2002-12-16 20:18:38 +00002151 def test_zones(self):
2152 est = FixedOffset(-300, "EST", 1)
2153 utc = FixedOffset(0, "UTC", -2)
2154 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002155 t1 = time( 7, 47, tzinfo=est)
2156 t2 = time(12, 47, tzinfo=utc)
2157 t3 = time(13, 47, tzinfo=met)
2158 t4 = time(microsecond=40)
2159 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002160
2161 self.assertEqual(t1.tzinfo, est)
2162 self.assertEqual(t2.tzinfo, utc)
2163 self.assertEqual(t3.tzinfo, met)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002164 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002165 self.assertEqual(t5.tzinfo, utc)
2166
Tim Peters855fe882002-12-22 03:43:39 +00002167 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2168 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2169 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002170 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002171 self.assertRaises(TypeError, t1.utcoffset, "no args")
2172
2173 self.assertEqual(t1.tzname(), "EST")
2174 self.assertEqual(t2.tzname(), "UTC")
2175 self.assertEqual(t3.tzname(), "MET")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002176 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002177 self.assertRaises(TypeError, t1.tzname, "no args")
2178
Tim Peters855fe882002-12-22 03:43:39 +00002179 self.assertEqual(t1.dst(), timedelta(minutes=1))
2180 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2181 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002182 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002183 self.assertRaises(TypeError, t1.dst, "no args")
2184
2185 self.assertEqual(hash(t1), hash(t2))
2186 self.assertEqual(hash(t1), hash(t3))
2187 self.assertEqual(hash(t2), hash(t3))
2188
2189 self.assertEqual(t1, t2)
2190 self.assertEqual(t1, t3)
2191 self.assertEqual(t2, t3)
2192 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2193 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2194 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2195
2196 self.assertEqual(str(t1), "07:47:00-05:00")
2197 self.assertEqual(str(t2), "12:47:00+00:00")
2198 self.assertEqual(str(t3), "13:47:00+01:00")
2199 self.assertEqual(str(t4), "00:00:00.000040")
2200 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2201
2202 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2203 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2204 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2205 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2206 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2207
Tim Peters0bf60bd2003-01-08 20:40:01 +00002208 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002209 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2210 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2211 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2212 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2213 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2214
2215 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2216 "07:47:00 %Z=EST %z=-0500")
2217 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2218 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2219
2220 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002221 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002222 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2223 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2224
Tim Petersb92bb712002-12-21 17:44:07 +00002225 # Check that an invalid tzname result raises an exception.
2226 class Badtzname(tzinfo):
2227 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002228 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002229 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2230 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002231
2232 def test_hash_edge_cases(self):
2233 # Offsets that overflow a basic time.
2234 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2235 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2236 self.assertEqual(hash(t1), hash(t2))
2237
2238 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2239 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2240 self.assertEqual(hash(t1), hash(t2))
2241
Tim Peters2a799bf2002-12-16 20:18:38 +00002242 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002243 # Try one without a tzinfo.
2244 args = 20, 59, 16, 64**2
2245 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002246 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002247 green = pickler.dumps(orig, proto)
2248 derived = unpickler.loads(green)
2249 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002250
2251 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002252 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002253 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002254 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002255 green = pickler.dumps(orig, proto)
2256 derived = unpickler.loads(green)
2257 self.assertEqual(orig, derived)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002258 self.assertTrue(isinstance(derived.tzinfo, PicklableFixedOffset))
Tim Peters96940c92003-01-31 21:55:33 +00002259 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2260 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002261
2262 def test_more_bool(self):
2263 # Test cases with non-None tzinfo.
2264 cls = self.theclass
2265
2266 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002267 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002268
2269 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002270 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002271
2272 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002273 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002274
2275 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002276 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002277
2278 # Mostly ensuring this doesn't overflow internally.
2279 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002280 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002281
2282 # But this should yield a value error -- the utcoffset is bogus.
2283 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2284 self.assertRaises(ValueError, lambda: bool(t))
2285
2286 # Likewise.
2287 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2288 self.assertRaises(ValueError, lambda: bool(t))
2289
Tim Peters12bf3392002-12-24 05:41:27 +00002290 def test_replace(self):
2291 cls = self.theclass
2292 z100 = FixedOffset(100, "+100")
2293 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2294 args = [1, 2, 3, 4, z100]
2295 base = cls(*args)
2296 self.assertEqual(base, base.replace())
2297
2298 i = 0
2299 for name, newval in (("hour", 5),
2300 ("minute", 6),
2301 ("second", 7),
2302 ("microsecond", 8),
2303 ("tzinfo", zm200)):
2304 newargs = args[:]
2305 newargs[i] = newval
2306 expected = cls(*newargs)
2307 got = base.replace(**{name: newval})
2308 self.assertEqual(expected, got)
2309 i += 1
2310
2311 # Ensure we can get rid of a tzinfo.
2312 self.assertEqual(base.tzname(), "+100")
2313 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002314 self.assertTrue(base2.tzinfo is None)
2315 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002316
2317 # Ensure we can add one.
2318 base3 = base2.replace(tzinfo=z100)
2319 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002320 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002321
2322 # Out of bounds.
2323 base = cls(1)
2324 self.assertRaises(ValueError, base.replace, hour=24)
2325 self.assertRaises(ValueError, base.replace, minute=-1)
2326 self.assertRaises(ValueError, base.replace, second=100)
2327 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2328
Tim Peters60c76e42002-12-27 00:41:11 +00002329 def test_mixed_compare(self):
2330 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002331 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002332 self.assertEqual(t1, t2)
2333 t2 = t2.replace(tzinfo=None)
2334 self.assertEqual(t1, t2)
2335 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2336 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002337 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2338 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002339
Tim Peters0bf60bd2003-01-08 20:40:01 +00002340 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002341 class Varies(tzinfo):
2342 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002343 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002344 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002345 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002346 return self.offset
2347
2348 v = Varies()
2349 t1 = t2.replace(tzinfo=v)
2350 t2 = t2.replace(tzinfo=v)
2351 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2352 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2353 self.assertEqual(t1, t2)
2354
2355 # But if they're not identical, it isn't ignored.
2356 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002357 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002358
Tim Petersa98924a2003-05-17 05:55:19 +00002359 def test_subclass_timetz(self):
2360
2361 class C(self.theclass):
2362 theAnswer = 42
2363
2364 def __new__(cls, *args, **kws):
2365 temp = kws.copy()
2366 extra = temp.pop('extra')
2367 result = self.theclass.__new__(cls, *args, **temp)
2368 result.extra = extra
2369 return result
2370
2371 def newmeth(self, start):
2372 return start + self.hour + self.second
2373
2374 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2375
2376 dt1 = self.theclass(*args)
2377 dt2 = C(*args, **{'extra': 7})
2378
2379 self.assertEqual(dt2.__class__, C)
2380 self.assertEqual(dt2.theAnswer, 42)
2381 self.assertEqual(dt2.extra, 7)
2382 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2383 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2384
Tim Peters4c0db782002-12-26 05:01:19 +00002385
Tim Peters0bf60bd2003-01-08 20:40:01 +00002386# Testing datetime objects with a non-None tzinfo.
2387
Collin Winterc2898c52007-04-25 17:29:52 +00002388class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002389 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002390
2391 def test_trivial(self):
2392 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2393 self.assertEqual(dt.year, 1)
2394 self.assertEqual(dt.month, 2)
2395 self.assertEqual(dt.day, 3)
2396 self.assertEqual(dt.hour, 4)
2397 self.assertEqual(dt.minute, 5)
2398 self.assertEqual(dt.second, 6)
2399 self.assertEqual(dt.microsecond, 7)
2400 self.assertEqual(dt.tzinfo, None)
2401
2402 def test_even_more_compare(self):
2403 # The test_compare() and test_more_compare() inherited from TestDate
2404 # and TestDateTime covered non-tzinfo cases.
2405
2406 # Smallest possible after UTC adjustment.
2407 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2408 # Largest possible after UTC adjustment.
2409 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2410 tzinfo=FixedOffset(-1439, ""))
2411
2412 # Make sure those compare correctly, and w/o overflow.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002413 self.assertTrue(t1 < t2)
2414 self.assertTrue(t1 != t2)
2415 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002416
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002417 self.assertTrue(t1 == t1)
2418 self.assertTrue(t2 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002419
2420 # Equal afer adjustment.
2421 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2422 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2423 self.assertEqual(t1, t2)
2424
2425 # Change t1 not to subtract a minute, and t1 should be larger.
2426 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002427 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002428
2429 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2430 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002431 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002432
2433 # Back to the original t1, but make seconds resolve it.
2434 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2435 second=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002436 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002437
2438 # Likewise, but make microseconds resolve it.
2439 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2440 microsecond=1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002441 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002442
2443 # Make t2 naive and it should fail.
2444 t2 = self.theclass.min
2445 self.assertRaises(TypeError, lambda: t1 == t2)
2446 self.assertEqual(t2, t2)
2447
2448 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2449 class Naive(tzinfo):
2450 def utcoffset(self, dt): return None
2451 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2452 self.assertRaises(TypeError, lambda: t1 == t2)
2453 self.assertEqual(t2, t2)
2454
2455 # OTOH, it's OK to compare two of these mixing the two ways of being
2456 # naive.
2457 t1 = self.theclass(5, 6, 7)
2458 self.assertEqual(t1, t2)
2459
2460 # Try a bogus uctoffset.
2461 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002462 def utcoffset(self, dt):
2463 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002464 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2465 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002466 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002467
Tim Peters2a799bf2002-12-16 20:18:38 +00002468 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002469 # Try one without a tzinfo.
2470 args = 6, 7, 23, 20, 59, 1, 64**2
2471 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002472 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002473 green = pickler.dumps(orig, proto)
2474 derived = unpickler.loads(green)
2475 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002476
2477 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002478 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002479 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002480 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002481 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002482 green = pickler.dumps(orig, proto)
2483 derived = unpickler.loads(green)
2484 self.assertEqual(orig, derived)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002485 self.assertTrue(isinstance(derived.tzinfo,
Tim Peters96940c92003-01-31 21:55:33 +00002486 PicklableFixedOffset))
2487 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2488 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002489
2490 def test_extreme_hashes(self):
2491 # If an attempt is made to hash these via subtracting the offset
2492 # then hashing a datetime object, OverflowError results. The
2493 # Python implementation used to blow up here.
2494 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2495 hash(t)
2496 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2497 tzinfo=FixedOffset(-1439, ""))
2498 hash(t)
2499
2500 # OTOH, an OOB offset should blow up.
2501 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2502 self.assertRaises(ValueError, hash, t)
2503
2504 def test_zones(self):
2505 est = FixedOffset(-300, "EST")
2506 utc = FixedOffset(0, "UTC")
2507 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002508 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2509 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2510 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002511 self.assertEqual(t1.tzinfo, est)
2512 self.assertEqual(t2.tzinfo, utc)
2513 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002514 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2515 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2516 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002517 self.assertEqual(t1.tzname(), "EST")
2518 self.assertEqual(t2.tzname(), "UTC")
2519 self.assertEqual(t3.tzname(), "MET")
2520 self.assertEqual(hash(t1), hash(t2))
2521 self.assertEqual(hash(t1), hash(t3))
2522 self.assertEqual(hash(t2), hash(t3))
2523 self.assertEqual(t1, t2)
2524 self.assertEqual(t1, t3)
2525 self.assertEqual(t2, t3)
2526 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2527 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2528 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002529 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002530 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2531 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2532 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2533
2534 def test_combine(self):
2535 met = FixedOffset(60, "MET")
2536 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002537 tz = time(18, 45, 3, 1234, tzinfo=met)
2538 dt = datetime.combine(d, tz)
2539 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002540 tzinfo=met))
2541
2542 def test_extract(self):
2543 met = FixedOffset(60, "MET")
2544 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2545 self.assertEqual(dt.date(), date(2002, 3, 4))
2546 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002547 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002548
2549 def test_tz_aware_arithmetic(self):
2550 import random
2551
2552 now = self.theclass.now()
2553 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002554 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002555 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002556 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002557 self.assertEqual(nowaware.timetz(), timeaware)
2558
2559 # Can't mix aware and non-aware.
2560 self.assertRaises(TypeError, lambda: now - nowaware)
2561 self.assertRaises(TypeError, lambda: nowaware - now)
2562
Tim Peters0bf60bd2003-01-08 20:40:01 +00002563 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002564 self.assertRaises(TypeError, lambda: now + nowaware)
2565 self.assertRaises(TypeError, lambda: nowaware + now)
2566 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2567
2568 # Subtracting should yield 0.
2569 self.assertEqual(now - now, timedelta(0))
2570 self.assertEqual(nowaware - nowaware, timedelta(0))
2571
2572 # Adding a delta should preserve tzinfo.
2573 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2574 nowawareplus = nowaware + delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002575 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002576 nowawareplus2 = delta + nowaware
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002577 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002578 self.assertEqual(nowawareplus, nowawareplus2)
2579
2580 # that - delta should be what we started with, and that - what we
2581 # started with should be delta.
2582 diff = nowawareplus - delta
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002583 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002584 self.assertEqual(nowaware, diff)
2585 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2586 self.assertEqual(nowawareplus - nowaware, delta)
2587
2588 # Make up a random timezone.
2589 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002590 # Attach it to nowawareplus.
2591 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002592 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002593 # Make sure the difference takes the timezone adjustments into account.
2594 got = nowaware - nowawareplus
2595 # Expected: (nowaware base - nowaware offset) -
2596 # (nowawareplus base - nowawareplus offset) =
2597 # (nowaware base - nowawareplus base) +
2598 # (nowawareplus offset - nowaware offset) =
2599 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002600 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002601 self.assertEqual(got, expected)
2602
2603 # Try max possible difference.
2604 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2605 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2606 tzinfo=FixedOffset(-1439, "max"))
2607 maxdiff = max - min
2608 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2609 timedelta(minutes=2*1439))
2610
2611 def test_tzinfo_now(self):
2612 meth = self.theclass.now
2613 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2614 base = meth()
2615 # Try with and without naming the keyword.
2616 off42 = FixedOffset(42, "42")
2617 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002618 again = meth(tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002619 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002620 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002621 # Bad argument with and w/o naming the keyword.
2622 self.assertRaises(TypeError, meth, 16)
2623 self.assertRaises(TypeError, meth, tzinfo=16)
2624 # Bad keyword name.
2625 self.assertRaises(TypeError, meth, tinfo=off42)
2626 # Too many args.
2627 self.assertRaises(TypeError, meth, off42, off42)
2628
Tim Peters10cadce2003-01-23 19:58:02 +00002629 # We don't know which time zone we're in, and don't have a tzinfo
2630 # class to represent it, so seeing whether a tz argument actually
2631 # does a conversion is tricky.
2632 weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
2633 utc = FixedOffset(0, "utc", 0)
2634 for dummy in range(3):
2635 now = datetime.now(weirdtz)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002636 self.assertTrue(now.tzinfo is weirdtz)
Tim Peters10cadce2003-01-23 19:58:02 +00002637 utcnow = datetime.utcnow().replace(tzinfo=utc)
2638 now2 = utcnow.astimezone(weirdtz)
2639 if abs(now - now2) < timedelta(seconds=30):
2640 break
2641 # Else the code is broken, or more than 30 seconds passed between
2642 # calls; assuming the latter, just try again.
2643 else:
2644 # Three strikes and we're out.
2645 self.fail("utcnow(), now(tz), or astimezone() may be broken")
2646
Tim Peters2a799bf2002-12-16 20:18:38 +00002647 def test_tzinfo_fromtimestamp(self):
2648 import time
2649 meth = self.theclass.fromtimestamp
2650 ts = time.time()
2651 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2652 base = meth(ts)
2653 # Try with and without naming the keyword.
2654 off42 = FixedOffset(42, "42")
2655 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002656 again = meth(ts, tz=off42)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002657 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002658 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002659 # Bad argument with and w/o naming the keyword.
2660 self.assertRaises(TypeError, meth, ts, 16)
2661 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2662 # Bad keyword name.
2663 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2664 # Too many args.
2665 self.assertRaises(TypeError, meth, ts, off42, off42)
2666 # Too few args.
2667 self.assertRaises(TypeError, meth)
2668
Tim Peters2a44a8d2003-01-23 20:53:10 +00002669 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002670 timestamp = 1000000000
2671 utcdatetime = datetime.utcfromtimestamp(timestamp)
2672 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2673 # But on some flavor of Mac, it's nowhere near that. So we can't have
2674 # any idea here what time that actually is, we can only test that
2675 # relative changes match.
2676 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2677 tz = FixedOffset(utcoffset, "tz", 0)
2678 expected = utcdatetime + utcoffset
2679 got = datetime.fromtimestamp(timestamp, tz)
2680 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002681
Tim Peters2a799bf2002-12-16 20:18:38 +00002682 def test_tzinfo_utcnow(self):
2683 meth = self.theclass.utcnow
2684 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2685 base = meth()
2686 # Try with and without naming the keyword; for whatever reason,
2687 # utcnow() doesn't accept a tzinfo argument.
2688 off42 = FixedOffset(42, "42")
2689 self.assertRaises(TypeError, meth, off42)
2690 self.assertRaises(TypeError, meth, tzinfo=off42)
2691
2692 def test_tzinfo_utcfromtimestamp(self):
2693 import time
2694 meth = self.theclass.utcfromtimestamp
2695 ts = time.time()
2696 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2697 base = meth(ts)
2698 # Try with and without naming the keyword; for whatever reason,
2699 # utcfromtimestamp() doesn't accept a tzinfo argument.
2700 off42 = FixedOffset(42, "42")
2701 self.assertRaises(TypeError, meth, ts, off42)
2702 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2703
2704 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002705 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002706 # DST flag.
2707 class DST(tzinfo):
2708 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002709 if isinstance(dstvalue, int):
2710 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002711 self.dstvalue = dstvalue
2712 def dst(self, dt):
2713 return self.dstvalue
2714
2715 cls = self.theclass
2716 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2717 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2718 t = d.timetuple()
2719 self.assertEqual(1, t.tm_year)
2720 self.assertEqual(1, t.tm_mon)
2721 self.assertEqual(1, t.tm_mday)
2722 self.assertEqual(10, t.tm_hour)
2723 self.assertEqual(20, t.tm_min)
2724 self.assertEqual(30, t.tm_sec)
2725 self.assertEqual(0, t.tm_wday)
2726 self.assertEqual(1, t.tm_yday)
2727 self.assertEqual(flag, t.tm_isdst)
2728
2729 # dst() returns wrong type.
2730 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2731
2732 # dst() at the edge.
2733 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2734 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2735
2736 # dst() out of range.
2737 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2738 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2739
2740 def test_utctimetuple(self):
2741 class DST(tzinfo):
2742 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002743 if isinstance(dstvalue, int):
2744 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002745 self.dstvalue = dstvalue
2746 def dst(self, dt):
2747 return self.dstvalue
2748
2749 cls = self.theclass
2750 # This can't work: DST didn't implement utcoffset.
2751 self.assertRaises(NotImplementedError,
2752 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2753
2754 class UOFS(DST):
2755 def __init__(self, uofs, dofs=None):
2756 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002757 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002758 def utcoffset(self, dt):
2759 return self.uofs
2760
2761 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2762 # in effect for a UTC time.
2763 for dstvalue in -33, 33, 0, None:
2764 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2765 t = d.utctimetuple()
2766 self.assertEqual(d.year, t.tm_year)
2767 self.assertEqual(d.month, t.tm_mon)
2768 self.assertEqual(d.day, t.tm_mday)
2769 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2770 self.assertEqual(13, t.tm_min)
2771 self.assertEqual(d.second, t.tm_sec)
2772 self.assertEqual(d.weekday(), t.tm_wday)
2773 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2774 t.tm_yday)
2775 self.assertEqual(0, t.tm_isdst)
2776
2777 # At the edges, UTC adjustment can normalize into years out-of-range
2778 # for a datetime object. Ensure that a correct timetuple is
2779 # created anyway.
2780 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2781 # That goes back 1 minute less than a full day.
2782 t = tiny.utctimetuple()
2783 self.assertEqual(t.tm_year, MINYEAR-1)
2784 self.assertEqual(t.tm_mon, 12)
2785 self.assertEqual(t.tm_mday, 31)
2786 self.assertEqual(t.tm_hour, 0)
2787 self.assertEqual(t.tm_min, 1)
2788 self.assertEqual(t.tm_sec, 37)
2789 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2790 self.assertEqual(t.tm_isdst, 0)
2791
2792 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2793 # That goes forward 1 minute less than a full day.
2794 t = huge.utctimetuple()
2795 self.assertEqual(t.tm_year, MAXYEAR+1)
2796 self.assertEqual(t.tm_mon, 1)
2797 self.assertEqual(t.tm_mday, 1)
2798 self.assertEqual(t.tm_hour, 23)
2799 self.assertEqual(t.tm_min, 58)
2800 self.assertEqual(t.tm_sec, 37)
2801 self.assertEqual(t.tm_yday, 1)
2802 self.assertEqual(t.tm_isdst, 0)
2803
2804 def test_tzinfo_isoformat(self):
2805 zero = FixedOffset(0, "+00:00")
2806 plus = FixedOffset(220, "+03:40")
2807 minus = FixedOffset(-231, "-03:51")
2808 unknown = FixedOffset(None, "")
2809
2810 cls = self.theclass
2811 datestr = '0001-02-03'
2812 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00002813 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00002814 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
2815 timestr = '04:05:59' + (us and '.987001' or '')
2816 ofsstr = ofs is not None and d.tzname() or ''
2817 tailstr = timestr + ofsstr
2818 iso = d.isoformat()
2819 self.assertEqual(iso, datestr + 'T' + tailstr)
2820 self.assertEqual(iso, d.isoformat('T'))
2821 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
2822 self.assertEqual(str(d), datestr + ' ' + tailstr)
2823
Tim Peters12bf3392002-12-24 05:41:27 +00002824 def test_replace(self):
2825 cls = self.theclass
2826 z100 = FixedOffset(100, "+100")
2827 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2828 args = [1, 2, 3, 4, 5, 6, 7, z100]
2829 base = cls(*args)
2830 self.assertEqual(base, base.replace())
2831
2832 i = 0
2833 for name, newval in (("year", 2),
2834 ("month", 3),
2835 ("day", 4),
2836 ("hour", 5),
2837 ("minute", 6),
2838 ("second", 7),
2839 ("microsecond", 8),
2840 ("tzinfo", zm200)):
2841 newargs = args[:]
2842 newargs[i] = newval
2843 expected = cls(*newargs)
2844 got = base.replace(**{name: newval})
2845 self.assertEqual(expected, got)
2846 i += 1
2847
2848 # Ensure we can get rid of a tzinfo.
2849 self.assertEqual(base.tzname(), "+100")
2850 base2 = base.replace(tzinfo=None)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002851 self.assertTrue(base2.tzinfo is None)
2852 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002853
2854 # Ensure we can add one.
2855 base3 = base2.replace(tzinfo=z100)
2856 self.assertEqual(base, base3)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002857 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002858
2859 # Out of bounds.
2860 base = cls(2000, 2, 29)
2861 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00002862
Tim Peters80475bb2002-12-25 07:40:55 +00002863 def test_more_astimezone(self):
2864 # The inherited test_astimezone covered some trivial and error cases.
2865 fnone = FixedOffset(None, "None")
2866 f44m = FixedOffset(44, "44")
2867 fm5h = FixedOffset(-timedelta(hours=5), "m300")
2868
Tim Peters10cadce2003-01-23 19:58:02 +00002869 dt = self.theclass.now(tz=f44m)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002870 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00002871 # Replacing with degenerate tzinfo raises an exception.
2872 self.assertRaises(ValueError, dt.astimezone, fnone)
2873 # Ditto with None tz.
2874 self.assertRaises(TypeError, dt.astimezone, None)
2875 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00002876 x = dt.astimezone(dt.tzinfo)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002877 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00002878 self.assertEqual(x.date(), dt.date())
2879 self.assertEqual(x.time(), dt.time())
2880
2881 # Replacing with different tzinfo does adjust.
2882 got = dt.astimezone(fm5h)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002883 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00002884 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2885 expected = dt - dt.utcoffset() # in effect, convert to UTC
2886 expected += fm5h.utcoffset(dt) # and from there to local time
2887 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2888 self.assertEqual(got.date(), expected.date())
2889 self.assertEqual(got.time(), expected.time())
2890 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002891 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00002892 self.assertEqual(got, expected)
2893
Tim Peters4c0db782002-12-26 05:01:19 +00002894 def test_aware_subtract(self):
2895 cls = self.theclass
2896
Tim Peters60c76e42002-12-27 00:41:11 +00002897 # Ensure that utcoffset() is ignored when the operands have the
2898 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002899 class OperandDependentOffset(tzinfo):
2900 def utcoffset(self, t):
2901 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002902 # d0 and d1 equal after adjustment
2903 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002904 else:
Tim Peters397301e2003-01-02 21:28:08 +00002905 # d2 off in the weeds
2906 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002907
2908 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
2909 d0 = base.replace(minute=3)
2910 d1 = base.replace(minute=9)
2911 d2 = base.replace(minute=11)
2912 for x in d0, d1, d2:
2913 for y in d0, d1, d2:
2914 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00002915 expected = timedelta(minutes=x.minute - y.minute)
2916 self.assertEqual(got, expected)
2917
2918 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2919 # ignored.
2920 base = cls(8, 9, 10, 11, 12, 13, 14)
2921 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2922 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2923 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2924 for x in d0, d1, d2:
2925 for y in d0, d1, d2:
2926 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00002927 if (x is d0 or x is d1) and (y is d0 or y is d1):
2928 expected = timedelta(0)
2929 elif x is y is d2:
2930 expected = timedelta(0)
2931 elif x is d2:
2932 expected = timedelta(minutes=(11-59)-0)
2933 else:
2934 assert y is d2
2935 expected = timedelta(minutes=0-(11-59))
2936 self.assertEqual(got, expected)
2937
Tim Peters60c76e42002-12-27 00:41:11 +00002938 def test_mixed_compare(self):
2939 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002940 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00002941 self.assertEqual(t1, t2)
2942 t2 = t2.replace(tzinfo=None)
2943 self.assertEqual(t1, t2)
2944 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2945 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002946 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2947 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002948
Tim Peters0bf60bd2003-01-08 20:40:01 +00002949 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002950 class Varies(tzinfo):
2951 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002952 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002953 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002954 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002955 return self.offset
2956
2957 v = Varies()
2958 t1 = t2.replace(tzinfo=v)
2959 t2 = t2.replace(tzinfo=v)
2960 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2961 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2962 self.assertEqual(t1, t2)
2963
2964 # But if they're not identical, it isn't ignored.
2965 t2 = t2.replace(tzinfo=Varies())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00002966 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00002967
Tim Petersa98924a2003-05-17 05:55:19 +00002968 def test_subclass_datetimetz(self):
2969
2970 class C(self.theclass):
2971 theAnswer = 42
2972
2973 def __new__(cls, *args, **kws):
2974 temp = kws.copy()
2975 extra = temp.pop('extra')
2976 result = self.theclass.__new__(cls, *args, **temp)
2977 result.extra = extra
2978 return result
2979
2980 def newmeth(self, start):
2981 return start + self.hour + self.year
2982
2983 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2984
2985 dt1 = self.theclass(*args)
2986 dt2 = C(*args, **{'extra': 7})
2987
2988 self.assertEqual(dt2.__class__, C)
2989 self.assertEqual(dt2.theAnswer, 42)
2990 self.assertEqual(dt2.extra, 7)
2991 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2992 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
2993
Tim Peters621818b2002-12-29 23:44:49 +00002994# Pain to set up DST-aware tzinfo classes.
2995
2996def first_sunday_on_or_after(dt):
2997 days_to_go = 6 - dt.weekday()
2998 if days_to_go:
2999 dt += timedelta(days_to_go)
3000 return dt
3001
3002ZERO = timedelta(0)
3003HOUR = timedelta(hours=1)
3004DAY = timedelta(days=1)
3005# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3006DSTSTART = datetime(1, 4, 1, 2)
3007# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003008# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3009# being standard time on that day, there is no spelling in local time of
3010# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3011DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003012
3013class USTimeZone(tzinfo):
3014
3015 def __init__(self, hours, reprname, stdname, dstname):
3016 self.stdoffset = timedelta(hours=hours)
3017 self.reprname = reprname
3018 self.stdname = stdname
3019 self.dstname = dstname
3020
3021 def __repr__(self):
3022 return self.reprname
3023
3024 def tzname(self, dt):
3025 if self.dst(dt):
3026 return self.dstname
3027 else:
3028 return self.stdname
3029
3030 def utcoffset(self, dt):
3031 return self.stdoffset + self.dst(dt)
3032
3033 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003034 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003035 # An exception instead may be sensible here, in one or more of
3036 # the cases.
3037 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003038 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003039
3040 # Find first Sunday in April.
3041 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3042 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3043
3044 # Find last Sunday in October.
3045 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3046 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3047
Tim Peters621818b2002-12-29 23:44:49 +00003048 # Can't compare naive to aware objects, so strip the timezone from
3049 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003050 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003051 return HOUR
3052 else:
3053 return ZERO
3054
Tim Peters521fc152002-12-31 17:36:56 +00003055Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3056Central = USTimeZone(-6, "Central", "CST", "CDT")
3057Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3058Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003059utc_real = FixedOffset(0, "UTC", 0)
3060# For better test coverage, we want another flavor of UTC that's west of
3061# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003062utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003063
3064class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003065 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003066 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003067 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003068
Tim Peters0bf60bd2003-01-08 20:40:01 +00003069 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003070
Tim Peters521fc152002-12-31 17:36:56 +00003071 # Check a time that's inside DST.
3072 def checkinside(self, dt, tz, utc, dston, dstoff):
3073 self.assertEqual(dt.dst(), HOUR)
3074
3075 # Conversion to our own timezone is always an identity.
3076 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003077
3078 asutc = dt.astimezone(utc)
3079 there_and_back = asutc.astimezone(tz)
3080
3081 # Conversion to UTC and back isn't always an identity here,
3082 # because there are redundant spellings (in local time) of
3083 # UTC time when DST begins: the clock jumps from 1:59:59
3084 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3085 # make sense then. The classes above treat 2:MM:SS as
3086 # daylight time then (it's "after 2am"), really an alias
3087 # for 1:MM:SS standard time. The latter form is what
3088 # conversion back from UTC produces.
3089 if dt.date() == dston.date() and dt.hour == 2:
3090 # We're in the redundant hour, and coming back from
3091 # UTC gives the 1:MM:SS standard-time spelling.
3092 self.assertEqual(there_and_back + HOUR, dt)
3093 # Although during was considered to be in daylight
3094 # time, there_and_back is not.
3095 self.assertEqual(there_and_back.dst(), ZERO)
3096 # They're the same times in UTC.
3097 self.assertEqual(there_and_back.astimezone(utc),
3098 dt.astimezone(utc))
3099 else:
3100 # We're not in the redundant hour.
3101 self.assertEqual(dt, there_and_back)
3102
Tim Peters327098a2003-01-20 22:54:38 +00003103 # Because we have a redundant spelling when DST begins, there is
3104 # (unforunately) an hour when DST ends that can't be spelled at all in
3105 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3106 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3107 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3108 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3109 # expressed in local time. Nevertheless, we want conversion back
3110 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003111 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003112 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003113 if dt.date() == dstoff.date() and dt.hour == 0:
3114 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003115 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003116 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3117 nexthour_utc += HOUR
3118 nexthour_tz = nexthour_utc.astimezone(tz)
3119 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003120 else:
Tim Peters327098a2003-01-20 22:54:38 +00003121 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003122
3123 # Check a time that's outside DST.
3124 def checkoutside(self, dt, tz, utc):
3125 self.assertEqual(dt.dst(), ZERO)
3126
3127 # Conversion to our own timezone is always an identity.
3128 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003129
3130 # Converting to UTC and back is an identity too.
3131 asutc = dt.astimezone(utc)
3132 there_and_back = asutc.astimezone(tz)
3133 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003134
Tim Peters1024bf82002-12-30 17:09:40 +00003135 def convert_between_tz_and_utc(self, tz, utc):
3136 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003137 # Because 1:MM on the day DST ends is taken as being standard time,
3138 # there is no spelling in tz for the last hour of daylight time.
3139 # For purposes of the test, the last hour of DST is 0:MM, which is
3140 # taken as being daylight time (and 1:MM is taken as being standard
3141 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003142 dstoff = self.dstoff.replace(tzinfo=tz)
3143 for delta in (timedelta(weeks=13),
3144 DAY,
3145 HOUR,
3146 timedelta(minutes=1),
3147 timedelta(microseconds=1)):
3148
Tim Peters521fc152002-12-31 17:36:56 +00003149 self.checkinside(dston, tz, utc, dston, dstoff)
3150 for during in dston + delta, dstoff - delta:
3151 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003152
Tim Peters521fc152002-12-31 17:36:56 +00003153 self.checkoutside(dstoff, tz, utc)
3154 for outside in dston - delta, dstoff + delta:
3155 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003156
Tim Peters621818b2002-12-29 23:44:49 +00003157 def test_easy(self):
3158 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003159 self.convert_between_tz_and_utc(Eastern, utc_real)
3160 self.convert_between_tz_and_utc(Pacific, utc_real)
3161 self.convert_between_tz_and_utc(Eastern, utc_fake)
3162 self.convert_between_tz_and_utc(Pacific, utc_fake)
3163 # The next is really dancing near the edge. It works because
3164 # Pacific and Eastern are far enough apart that their "problem
3165 # hours" don't overlap.
3166 self.convert_between_tz_and_utc(Eastern, Pacific)
3167 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003168 # OTOH, these fail! Don't enable them. The difficulty is that
3169 # the edge case tests assume that every hour is representable in
3170 # the "utc" class. This is always true for a fixed-offset tzinfo
3171 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3172 # For these adjacent DST-aware time zones, the range of time offsets
3173 # tested ends up creating hours in the one that aren't representable
3174 # in the other. For the same reason, we would see failures in the
3175 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3176 # offset deltas in convert_between_tz_and_utc().
3177 #
3178 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3179 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003180
Tim Petersf3615152003-01-01 21:51:37 +00003181 def test_tricky(self):
3182 # 22:00 on day before daylight starts.
3183 fourback = self.dston - timedelta(hours=4)
3184 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003185 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003186 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3187 # 2", we should get the 3 spelling.
3188 # If we plug 22:00 the day before into Eastern, it "looks like std
3189 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3190 # to 22:00 lands on 2:00, which makes no sense in local time (the
3191 # local clock jumps from 1 to 3). The point here is to make sure we
3192 # get the 3 spelling.
3193 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003194 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003195 self.assertEqual(expected, got)
3196
3197 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3198 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003199 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003200 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3201 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3202 # spelling.
3203 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003204 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003205 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003206
Tim Petersadf64202003-01-04 06:03:15 +00003207 # Now on the day DST ends, we want "repeat an hour" behavior.
3208 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3209 # EST 23:MM 0:MM 1:MM 2:MM
3210 # EDT 0:MM 1:MM 2:MM 3:MM
3211 # wall 0:MM 1:MM 1:MM 2:MM against these
3212 for utc in utc_real, utc_fake:
3213 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003214 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003215 # Convert that to UTC.
3216 first_std_hour -= tz.utcoffset(None)
3217 # Adjust for possibly fake UTC.
3218 asutc = first_std_hour + utc.utcoffset(None)
3219 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3220 # tz=Eastern.
3221 asutcbase = asutc.replace(tzinfo=utc)
3222 for tzhour in (0, 1, 1, 2):
3223 expectedbase = self.dstoff.replace(hour=tzhour)
3224 for minute in 0, 30, 59:
3225 expected = expectedbase.replace(minute=minute)
3226 asutc = asutcbase.replace(minute=minute)
3227 astz = asutc.astimezone(tz)
3228 self.assertEqual(astz.replace(tzinfo=None), expected)
3229 asutcbase += HOUR
3230
3231
Tim Peters710fb152003-01-02 19:35:54 +00003232 def test_bogus_dst(self):
3233 class ok(tzinfo):
3234 def utcoffset(self, dt): return HOUR
3235 def dst(self, dt): return HOUR
3236
3237 now = self.theclass.now().replace(tzinfo=utc_real)
3238 # Doesn't blow up.
3239 now.astimezone(ok())
3240
3241 # Does blow up.
3242 class notok(ok):
3243 def dst(self, dt): return None
3244 self.assertRaises(ValueError, now.astimezone, notok())
3245
Tim Peters52dcce22003-01-23 16:36:11 +00003246 def test_fromutc(self):
3247 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3248 now = datetime.utcnow().replace(tzinfo=utc_real)
3249 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3250 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3251 enow = Eastern.fromutc(now) # doesn't blow up
3252 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3253 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3254 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3255
3256 # Always converts UTC to standard time.
3257 class FauxUSTimeZone(USTimeZone):
3258 def fromutc(self, dt):
3259 return dt + self.stdoffset
3260 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3261
3262 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3263 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3264 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3265
3266 # Check around DST start.
3267 start = self.dston.replace(hour=4, tzinfo=Eastern)
3268 fstart = start.replace(tzinfo=FEastern)
3269 for wall in 23, 0, 1, 3, 4, 5:
3270 expected = start.replace(hour=wall)
3271 if wall == 23:
3272 expected -= timedelta(days=1)
3273 got = Eastern.fromutc(start)
3274 self.assertEqual(expected, got)
3275
3276 expected = fstart + FEastern.stdoffset
3277 got = FEastern.fromutc(fstart)
3278 self.assertEqual(expected, got)
3279
3280 # Ensure astimezone() calls fromutc() too.
3281 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3282 self.assertEqual(expected, got)
3283
3284 start += HOUR
3285 fstart += HOUR
3286
3287 # Check around DST end.
3288 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3289 fstart = start.replace(tzinfo=FEastern)
3290 for wall in 0, 1, 1, 2, 3, 4:
3291 expected = start.replace(hour=wall)
3292 got = Eastern.fromutc(start)
3293 self.assertEqual(expected, got)
3294
3295 expected = fstart + FEastern.stdoffset
3296 got = FEastern.fromutc(fstart)
3297 self.assertEqual(expected, got)
3298
3299 # Ensure astimezone() calls fromutc() too.
3300 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3301 self.assertEqual(expected, got)
3302
3303 start += HOUR
3304 fstart += HOUR
3305
Tim Peters710fb152003-01-02 19:35:54 +00003306
Tim Peters528ca532004-09-16 01:30:50 +00003307#############################################################################
3308# oddballs
3309
3310class Oddballs(unittest.TestCase):
3311
3312 def test_bug_1028306(self):
3313 # Trying to compare a date to a datetime should act like a mixed-
3314 # type comparison, despite that datetime is a subclass of date.
3315 as_date = date.today()
3316 as_datetime = datetime.combine(as_date, time())
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003317 self.assertTrue(as_date != as_datetime)
3318 self.assertTrue(as_datetime != as_date)
3319 self.assertTrue(not as_date == as_datetime)
3320 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003321 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3322 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3323 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3324 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3325 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
3330 # Neverthelss, comparison should work with the base-class (date)
3331 # projection if use of a date method is forced.
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003332 self.assertTrue(as_date.__eq__(as_datetime))
Tim Peters528ca532004-09-16 01:30:50 +00003333 different_day = (as_date.day + 1) % 20 + 1
Benjamin Peterson5c8da862009-06-30 22:57:08 +00003334 self.assertTrue(not as_date.__eq__(as_datetime.replace(day=
Tim Peters528ca532004-09-16 01:30:50 +00003335 different_day)))
3336
3337 # And date should compare with other subclasses of date. If a
3338 # subclass wants to stop this, it's up to the subclass to do so.
3339 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3340 self.assertEqual(as_date, date_sc)
3341 self.assertEqual(date_sc, as_date)
3342
3343 # Ditto for datetimes.
3344 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3345 as_date.day, 0, 0, 0)
3346 self.assertEqual(as_datetime, datetime_sc)
3347 self.assertEqual(datetime_sc, as_datetime)
3348
Tim Peters2a799bf2002-12-16 20:18:38 +00003349def test_main():
Collin Winterbec754c2007-04-25 17:37:35 +00003350 test_support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003351
3352if __name__ == "__main__":
3353 test_main()