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