blob: 1e163f7aa65ea1825b512c86888be3d77b5fb13a [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
Alexander Belopolskyd5442cd2010-05-26 20:00:12 +00006import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00008import unittest
9
Mark Dickinson7c186e22010-04-20 22:32:49 +000010from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
Mark Dickinsona56c4672009-01-27 18:17:45 +000011
Benjamin Petersonee8712c2008-05-20 21:35:26 +000012from test import support
Tim Peters2a799bf2002-12-16 20:18:38 +000013
14from datetime import MINYEAR, MAXYEAR
15from datetime import timedelta
16from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000017from datetime import time
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000018from datetime import timezone
Tim Peters0bf60bd2003-01-08 20:40:01 +000019from datetime import date, datetime
20
Guido van Rossumbe6fe542007-07-19 23:55:34 +000021pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
22assert len(pickle_choices) == 3
Guido van Rossum177e41a2003-01-30 22:06:23 +000023
Tim Peters68124bb2003-02-08 03:46:31 +000024# An arbitrary collection of objects of non-datetime types, for testing
25# mixed-type comparisons.
Mark Dickinson5c2db372009-12-05 20:28:34 +000026OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000027
Tim Peters2a799bf2002-12-16 20:18:38 +000028
Alexander Belopolsky1790bc42010-05-31 17:33:47 +000029# XXX Copied from test_float.
30INF = float("inf")
31NAN = float("nan")
32
33# decorator for skipping tests on non-IEEE 754 platforms
34requires_IEEE_754 = unittest.skipUnless(
35 float.__getformat__("double").startswith("IEEE"),
36 "test requires IEEE 754 doubles")
37
38
Tim Peters2a799bf2002-12-16 20:18:38 +000039#############################################################################
40# module tests
41
42class TestModule(unittest.TestCase):
43
44 def test_constants(self):
45 import datetime
46 self.assertEqual(datetime.MINYEAR, 1)
47 self.assertEqual(datetime.MAXYEAR, 9999)
48
49#############################################################################
50# tzinfo tests
51
52class FixedOffset(tzinfo):
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000053
Tim Peters2a799bf2002-12-16 20:18:38 +000054 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000055 if isinstance(offset, int):
56 offset = timedelta(minutes=offset)
57 if isinstance(dstoffset, int):
58 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000059 self.__offset = offset
60 self.__name = name
61 self.__dstoffset = dstoffset
62 def __repr__(self):
63 return self.__name.lower()
64 def utcoffset(self, dt):
65 return self.__offset
66 def tzname(self, dt):
67 return self.__name
68 def dst(self, dt):
69 return self.__dstoffset
70
Tim Petersfb8472c2002-12-21 05:04:42 +000071class PicklableFixedOffset(FixedOffset):
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000072
Tim Petersfb8472c2002-12-21 05:04:42 +000073 def __init__(self, offset=None, name=None, dstoffset=None):
74 FixedOffset.__init__(self, offset, name, dstoffset)
75
Tim Peters2a799bf2002-12-16 20:18:38 +000076class TestTZInfo(unittest.TestCase):
77
78 def test_non_abstractness(self):
79 # In order to allow subclasses to get pickled, the C implementation
80 # wasn't able to get away with having __init__ raise
81 # NotImplementedError.
82 useless = tzinfo()
83 dt = datetime.max
84 self.assertRaises(NotImplementedError, useless.tzname, dt)
85 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
86 self.assertRaises(NotImplementedError, useless.dst, dt)
87
88 def test_subclass_must_override(self):
89 class NotEnough(tzinfo):
90 def __init__(self, offset, name):
91 self.__offset = offset
92 self.__name = name
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000093 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000094 ne = NotEnough(3, "NotByALongShot")
Ezio Melottie9615932010-01-24 19:26:24 +000095 self.assertIsInstance(ne, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000096
97 dt = datetime.now()
98 self.assertRaises(NotImplementedError, ne.tzname, dt)
99 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
100 self.assertRaises(NotImplementedError, ne.dst, dt)
101
102 def test_normal(self):
103 fo = FixedOffset(3, "Three")
Ezio Melottie9615932010-01-24 19:26:24 +0000104 self.assertIsInstance(fo, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000105 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000106 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000107 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000108 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000109
110 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000111 # There's no point to pickling tzinfo objects on their own (they
112 # carry no data), but they need to be picklable anyway else
113 # concrete subclasses can't be pickled.
114 orig = tzinfo.__new__(tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000115 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000116 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000117 green = pickler.dumps(orig, proto)
118 derived = unpickler.loads(green)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000119 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000120
121 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000122 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000123 offset = timedelta(minutes=-300)
124 orig = PicklableFixedOffset(offset, 'cookie')
Ezio Melottie9615932010-01-24 19:26:24 +0000125 self.assertIsInstance(orig, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000126 self.assertTrue(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000127 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000128 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000129 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000130 green = pickler.dumps(orig, proto)
131 derived = unpickler.loads(green)
Ezio Melottie9615932010-01-24 19:26:24 +0000132 self.assertIsInstance(derived, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000133 self.assertTrue(type(derived) is PicklableFixedOffset)
Tim Petersf2715e02003-02-19 02:35:07 +0000134 self.assertEqual(derived.utcoffset(None), offset)
135 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000136
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000137class TestTimeZone(unittest.TestCase):
138
139 def setUp(self):
140 self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
141 self.EST = timezone(-timedelta(hours=5), 'EST')
142 self.DT = datetime(2010, 1, 1)
143
144 def test_str(self):
145 for tz in [self.ACDT, self.EST, timezone.utc,
146 timezone.min, timezone.max]:
147 self.assertEqual(str(tz), tz.tzname(None))
148
149 def test_class_members(self):
150 limit = timedelta(hours=23, minutes=59)
151 self.assertEquals(timezone.utc.utcoffset(None), ZERO)
152 self.assertEquals(timezone.min.utcoffset(None), -limit)
153 self.assertEquals(timezone.max.utcoffset(None), limit)
154
155
156 def test_constructor(self):
157 self.assertEquals(timezone.utc, timezone(timedelta(0)))
158 # invalid offsets
159 for invalid in [timedelta(microseconds=1), timedelta(1, 1),
160 timedelta(seconds=1), timedelta(1), -timedelta(1)]:
161 self.assertRaises(ValueError, timezone, invalid)
162 self.assertRaises(ValueError, timezone, -invalid)
163
164 with self.assertRaises(TypeError): timezone(None)
165 with self.assertRaises(TypeError): timezone(42)
166 with self.assertRaises(TypeError): timezone(ZERO, None)
167 with self.assertRaises(TypeError): timezone(ZERO, 42)
168
169 def test_inheritance(self):
170 self.assertTrue(isinstance(timezone.utc, tzinfo))
171 self.assertTrue(isinstance(self.EST, tzinfo))
172
173 def test_utcoffset(self):
174 dummy = self.DT
175 for h in [0, 1.5, 12]:
176 offset = h * HOUR
177 self.assertEquals(offset, timezone(offset).utcoffset(dummy))
178 self.assertEquals(-offset, timezone(-offset).utcoffset(dummy))
179
180 with self.assertRaises(TypeError): self.EST.utcoffset('')
181 with self.assertRaises(TypeError): self.EST.utcoffset(5)
182
183
184 def test_dst(self):
185 self.assertEquals(None, timezone.utc.dst(self.DT))
186
187 with self.assertRaises(TypeError): self.EST.dst('')
188 with self.assertRaises(TypeError): self.EST.dst(5)
189
190 def test_tzname(self):
191 self.assertEquals('UTC+00:00', timezone(ZERO).tzname(None))
192 self.assertEquals('UTC-05:00', timezone(-5 * HOUR).tzname(None))
193 self.assertEquals('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
194 self.assertEquals('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
195 self.assertEquals('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
196
197 with self.assertRaises(TypeError): self.EST.tzname('')
198 with self.assertRaises(TypeError): self.EST.tzname(5)
199
200 def test_fromutc(self):
201 with self.assertRaises(ValueError):
202 timezone.utc.fromutc(self.DT)
203 for tz in [self.EST, self.ACDT, Eastern]:
204 utctime = self.DT.replace(tzinfo=tz)
205 local = tz.fromutc(utctime)
206 self.assertEquals(local - utctime, tz.utcoffset(local))
207 self.assertEquals(local,
208 self.DT.replace(tzinfo=timezone.utc))
209
210 def test_comparison(self):
211 self.assertNotEqual(timezone(ZERO), timezone(HOUR))
212 self.assertEqual(timezone(HOUR), timezone(HOUR))
213 self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
214 with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
215 self.assertIn(timezone(ZERO), {timezone(ZERO)})
216
217 def test_aware_datetime(self):
218 # test that timezone instances can be used by datetime
219 t = datetime(1, 1, 1)
220 for tz in [timezone.min, timezone.max, timezone.utc]:
221 self.assertEquals(tz.tzname(t),
222 t.replace(tzinfo=tz).tzname())
223 self.assertEquals(tz.utcoffset(t),
224 t.replace(tzinfo=tz).utcoffset())
225 self.assertEquals(tz.dst(t),
226 t.replace(tzinfo=tz).dst())
227
Tim Peters2a799bf2002-12-16 20:18:38 +0000228#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000229# Base clase for testing a particular aspect of timedelta, time, date and
230# datetime comparisons.
231
Guido van Rossumd8faa362007-04-27 19:54:29 +0000232class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000233 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
234
235 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
236 # legit constructor.
237
238 def test_harmless_mixed_comparison(self):
239 me = self.theclass(1, 1, 1)
240
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000241 self.assertFalse(me == ())
242 self.assertTrue(me != ())
243 self.assertFalse(() == me)
244 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000245
Benjamin Peterson577473f2010-01-19 00:09:57 +0000246 self.assertIn(me, [1, 20, [], me])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000247 self.assertIn([], [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000248
249 def test_harmful_mixed_comparison(self):
250 me = self.theclass(1, 1, 1)
251
252 self.assertRaises(TypeError, lambda: me < ())
253 self.assertRaises(TypeError, lambda: me <= ())
254 self.assertRaises(TypeError, lambda: me > ())
255 self.assertRaises(TypeError, lambda: me >= ())
256
257 self.assertRaises(TypeError, lambda: () < me)
258 self.assertRaises(TypeError, lambda: () <= me)
259 self.assertRaises(TypeError, lambda: () > me)
260 self.assertRaises(TypeError, lambda: () >= me)
261
Tim Peters07534a62003-02-07 22:50:28 +0000262#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000263# timedelta tests
264
Guido van Rossumd8faa362007-04-27 19:54:29 +0000265class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000266
267 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000268
269 def test_constructor(self):
270 eq = self.assertEqual
271 td = timedelta
272
273 # Check keyword args to constructor
274 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
275 milliseconds=0, microseconds=0))
276 eq(td(1), td(days=1))
277 eq(td(0, 1), td(seconds=1))
278 eq(td(0, 0, 1), td(microseconds=1))
279 eq(td(weeks=1), td(days=7))
280 eq(td(days=1), td(hours=24))
281 eq(td(hours=1), td(minutes=60))
282 eq(td(minutes=1), td(seconds=60))
283 eq(td(seconds=1), td(milliseconds=1000))
284 eq(td(milliseconds=1), td(microseconds=1000))
285
286 # Check float args to constructor
287 eq(td(weeks=1.0/7), td(days=1))
288 eq(td(days=1.0/24), td(hours=1))
289 eq(td(hours=1.0/60), td(minutes=1))
290 eq(td(minutes=1.0/60), td(seconds=1))
291 eq(td(seconds=0.001), td(milliseconds=1))
292 eq(td(milliseconds=0.001), td(microseconds=1))
293
294 def test_computations(self):
295 eq = self.assertEqual
296 td = timedelta
297
298 a = td(7) # One week
299 b = td(0, 60) # One minute
300 c = td(0, 0, 1000) # One millisecond
301 eq(a+b+c, td(7, 60, 1000))
302 eq(a-b, td(6, 24*3600 - 60))
303 eq(-a, td(-7))
304 eq(+a, td(7))
305 eq(-b, td(-1, 24*3600 - 60))
306 eq(-c, td(-1, 24*3600 - 1, 999000))
307 eq(abs(a), a)
308 eq(abs(-a), a)
309 eq(td(6, 24*3600), a)
310 eq(td(0, 0, 60*1000000), b)
311 eq(a*10, td(70))
312 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000313 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000314 eq(b*10, td(0, 600))
315 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000316 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000317 eq(c*10, td(0, 0, 10000))
318 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000319 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000320 eq(a*-1, -a)
321 eq(b*-2, -b-b)
322 eq(c*-2, -c+-c)
323 eq(b*(60*24), (b*60)*24)
324 eq(b*(60*24), (60*b)*24)
325 eq(c*1000, td(0, 1))
326 eq(1000*c, td(0, 1))
327 eq(a//7, td(1))
328 eq(b//10, td(0, 6))
329 eq(c//1000, td(0, 0, 1))
330 eq(a//10, td(0, 7*24*360))
331 eq(a//3600000, td(0, 0, 7*24*1000))
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000332 eq(a/0.5, td(14))
333 eq(b/0.5, td(0, 120))
334 eq(a/7, td(1))
335 eq(b/10, td(0, 6))
336 eq(c/1000, td(0, 0, 1))
337 eq(a/10, td(0, 7*24*360))
338 eq(a/3600000, td(0, 0, 7*24*1000))
339
340 # Multiplication by float
341 us = td(microseconds=1)
342 eq((3*us) * 0.5, 2*us)
343 eq((5*us) * 0.5, 2*us)
344 eq(0.5 * (3*us), 2*us)
345 eq(0.5 * (5*us), 2*us)
346 eq((-3*us) * 0.5, -2*us)
347 eq((-5*us) * 0.5, -2*us)
348
349 # Division by int and float
350 eq((3*us) / 2, 2*us)
351 eq((5*us) / 2, 2*us)
352 eq((-3*us) / 2.0, -2*us)
353 eq((-5*us) / 2.0, -2*us)
354 eq((3*us) / -2, -2*us)
355 eq((5*us) / -2, -2*us)
356 eq((3*us) / -2.0, -2*us)
357 eq((5*us) / -2.0, -2*us)
358 for i in range(-10, 10):
359 eq((i*us/3)//us, round(i/3))
360 for i in range(-10, 10):
361 eq((i*us/-3)//us, round(i/-3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000362
363 def test_disallowed_computations(self):
364 a = timedelta(42)
365
Mark Dickinson5c2db372009-12-05 20:28:34 +0000366 # Add/sub ints or floats should be illegal
367 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000368 self.assertRaises(TypeError, lambda: a+i)
369 self.assertRaises(TypeError, lambda: a-i)
370 self.assertRaises(TypeError, lambda: i+a)
371 self.assertRaises(TypeError, lambda: i-a)
372
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000373 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000374 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000375 zero = 0
376 self.assertRaises(TypeError, lambda: zero // a)
377 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000378 self.assertRaises(ZeroDivisionError, lambda: a / zero)
379 self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
380
381 @requires_IEEE_754
382 def test_disallowed_special(self):
383 a = timedelta(42)
384 self.assertRaises(ValueError, a.__mul__, NAN)
385 self.assertRaises(ValueError, a.__truediv__, NAN)
Tim Peters2a799bf2002-12-16 20:18:38 +0000386
387 def test_basic_attributes(self):
388 days, seconds, us = 1, 7, 31
389 td = timedelta(days, seconds, us)
390 self.assertEqual(td.days, days)
391 self.assertEqual(td.seconds, seconds)
392 self.assertEqual(td.microseconds, us)
393
Antoine Pitroube6859d2009-11-25 23:02:32 +0000394 def test_total_seconds(self):
395 td = timedelta(days=365)
396 self.assertEqual(td.total_seconds(), 31536000.0)
397 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
398 td = timedelta(seconds=total_seconds)
399 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson0381e3f2010-05-08 14:35:02 +0000400 # Issue8644: Test that td.total_seconds() has the same
401 # accuracy as td / timedelta(seconds=1).
402 for ms in [-1, -2, -123]:
403 td = timedelta(microseconds=ms)
404 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
Antoine Pitroube6859d2009-11-25 23:02:32 +0000405
Tim Peters2a799bf2002-12-16 20:18:38 +0000406 def test_carries(self):
407 t1 = timedelta(days=100,
408 weeks=-7,
409 hours=-24*(100-49),
410 minutes=-3,
411 seconds=12,
412 microseconds=(3*60 - 12) * 1e6 + 1)
413 t2 = timedelta(microseconds=1)
414 self.assertEqual(t1, t2)
415
416 def test_hash_equality(self):
417 t1 = timedelta(days=100,
418 weeks=-7,
419 hours=-24*(100-49),
420 minutes=-3,
421 seconds=12,
422 microseconds=(3*60 - 12) * 1000000)
423 t2 = timedelta()
424 self.assertEqual(hash(t1), hash(t2))
425
426 t1 += timedelta(weeks=7)
427 t2 += timedelta(days=7*7)
428 self.assertEqual(t1, t2)
429 self.assertEqual(hash(t1), hash(t2))
430
431 d = {t1: 1}
432 d[t2] = 2
433 self.assertEqual(len(d), 1)
434 self.assertEqual(d[t1], 2)
435
436 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000437 args = 12, 34, 56
438 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000439 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000440 green = pickler.dumps(orig, proto)
441 derived = unpickler.loads(green)
442 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000443
444 def test_compare(self):
445 t1 = timedelta(2, 3, 4)
446 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000447 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000448 self.assertTrue(t1 <= t2)
449 self.assertTrue(t1 >= t2)
450 self.assertTrue(not t1 != t2)
451 self.assertTrue(not t1 < t2)
452 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000453
454 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
455 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000456 self.assertTrue(t1 < t2)
457 self.assertTrue(t2 > t1)
458 self.assertTrue(t1 <= t2)
459 self.assertTrue(t2 >= t1)
460 self.assertTrue(t1 != t2)
461 self.assertTrue(t2 != t1)
462 self.assertTrue(not t1 == t2)
463 self.assertTrue(not t2 == t1)
464 self.assertTrue(not t1 > t2)
465 self.assertTrue(not t2 < t1)
466 self.assertTrue(not t1 >= t2)
467 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000468
Tim Peters68124bb2003-02-08 03:46:31 +0000469 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000470 self.assertEqual(t1 == badarg, False)
471 self.assertEqual(t1 != badarg, True)
472 self.assertEqual(badarg == t1, False)
473 self.assertEqual(badarg != t1, True)
474
Tim Peters2a799bf2002-12-16 20:18:38 +0000475 self.assertRaises(TypeError, lambda: t1 <= badarg)
476 self.assertRaises(TypeError, lambda: t1 < badarg)
477 self.assertRaises(TypeError, lambda: t1 > badarg)
478 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000479 self.assertRaises(TypeError, lambda: badarg <= t1)
480 self.assertRaises(TypeError, lambda: badarg < t1)
481 self.assertRaises(TypeError, lambda: badarg > t1)
482 self.assertRaises(TypeError, lambda: badarg >= t1)
483
484 def test_str(self):
485 td = timedelta
486 eq = self.assertEqual
487
488 eq(str(td(1)), "1 day, 0:00:00")
489 eq(str(td(-1)), "-1 day, 0:00:00")
490 eq(str(td(2)), "2 days, 0:00:00")
491 eq(str(td(-2)), "-2 days, 0:00:00")
492
493 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
494 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
495 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
496 "-210 days, 23:12:34")
497
498 eq(str(td(milliseconds=1)), "0:00:00.001000")
499 eq(str(td(microseconds=3)), "0:00:00.000003")
500
501 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
502 microseconds=999999)),
503 "999999999 days, 23:59:59.999999")
504
505 def test_roundtrip(self):
506 for td in (timedelta(days=999999999, hours=23, minutes=59,
507 seconds=59, microseconds=999999),
508 timedelta(days=-999999999),
509 timedelta(days=1, seconds=2, microseconds=3)):
510
511 # Verify td -> string -> td identity.
512 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000513 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000514 s = s[9:]
515 td2 = eval(s)
516 self.assertEqual(td, td2)
517
518 # Verify identity via reconstructing from pieces.
519 td2 = timedelta(td.days, td.seconds, td.microseconds)
520 self.assertEqual(td, td2)
521
522 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000523 self.assertIsInstance(timedelta.min, timedelta)
524 self.assertIsInstance(timedelta.max, timedelta)
525 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000526 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000527 self.assertEqual(timedelta.min, timedelta(-999999999))
528 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
529 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
530
531 def test_overflow(self):
532 tiny = timedelta.resolution
533
534 td = timedelta.min + tiny
535 td -= tiny # no problem
536 self.assertRaises(OverflowError, td.__sub__, tiny)
537 self.assertRaises(OverflowError, td.__add__, -tiny)
538
539 td = timedelta.max - tiny
540 td += tiny # no problem
541 self.assertRaises(OverflowError, td.__add__, tiny)
542 self.assertRaises(OverflowError, td.__sub__, -tiny)
543
544 self.assertRaises(OverflowError, lambda: -timedelta.max)
545
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000546 day = timedelta(1)
547 self.assertRaises(OverflowError, day.__mul__, 10**9)
548 self.assertRaises(OverflowError, day.__mul__, 1e9)
549 self.assertRaises(OverflowError, day.__truediv__, 1e-20)
550 self.assertRaises(OverflowError, day.__truediv__, 1e-10)
551 self.assertRaises(OverflowError, day.__truediv__, 9e-10)
552
553 @requires_IEEE_754
554 def _test_overflow_special(self):
555 day = timedelta(1)
556 self.assertRaises(OverflowError, day.__mul__, INF)
557 self.assertRaises(OverflowError, day.__mul__, -INF)
558
Tim Peters2a799bf2002-12-16 20:18:38 +0000559 def test_microsecond_rounding(self):
560 td = timedelta
561 eq = self.assertEqual
562
563 # Single-field rounding.
564 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
565 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
566 eq(td(milliseconds=0.6/1000), td(microseconds=1))
567 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
568
569 # Rounding due to contributions from more than one field.
570 us_per_hour = 3600e6
571 us_per_day = us_per_hour * 24
572 eq(td(days=.4/us_per_day), td(0))
573 eq(td(hours=.2/us_per_hour), td(0))
574 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
575
576 eq(td(days=-.4/us_per_day), td(0))
577 eq(td(hours=-.2/us_per_hour), td(0))
578 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
579
580 def test_massive_normalization(self):
581 td = timedelta(microseconds=-1)
582 self.assertEqual((td.days, td.seconds, td.microseconds),
583 (-1, 24*3600-1, 999999))
584
585 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000586 self.assertTrue(timedelta(1))
587 self.assertTrue(timedelta(0, 1))
588 self.assertTrue(timedelta(0, 0, 1))
589 self.assertTrue(timedelta(microseconds=1))
590 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000591
Tim Petersb0c854d2003-05-17 15:57:00 +0000592 def test_subclass_timedelta(self):
593
594 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000595 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000596 def from_td(td):
597 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000598
599 def as_hours(self):
600 sum = (self.days * 24 +
601 self.seconds / 3600.0 +
602 self.microseconds / 3600e6)
603 return round(sum)
604
605 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000606 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000607 self.assertEqual(t1.as_hours(), 24)
608
609 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000610 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000611 self.assertEqual(t2.as_hours(), -25)
612
613 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000614 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000615 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000616 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000617 self.assertEqual(t3.days, t4.days)
618 self.assertEqual(t3.seconds, t4.seconds)
619 self.assertEqual(t3.microseconds, t4.microseconds)
620 self.assertEqual(str(t3), str(t4))
621 self.assertEqual(t4.as_hours(), -1)
622
Mark Dickinson7c186e22010-04-20 22:32:49 +0000623 def test_division(self):
624 t = timedelta(hours=1, minutes=24, seconds=19)
625 second = timedelta(seconds=1)
626 self.assertEqual(t / second, 5059.0)
627 self.assertEqual(t // second, 5059)
628
629 t = timedelta(minutes=2, seconds=30)
630 minute = timedelta(minutes=1)
631 self.assertEqual(t / minute, 2.5)
632 self.assertEqual(t // minute, 2)
633
634 zerotd = timedelta(0)
635 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
636 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
637
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000638 # self.assertRaises(TypeError, truediv, t, 2)
Mark Dickinson7c186e22010-04-20 22:32:49 +0000639 # note: floor division of a timedelta by an integer *is*
640 # currently permitted.
641
642 def test_remainder(self):
643 t = timedelta(minutes=2, seconds=30)
644 minute = timedelta(minutes=1)
645 r = t % minute
646 self.assertEqual(r, timedelta(seconds=30))
647
648 t = timedelta(minutes=-2, seconds=30)
649 r = t % minute
650 self.assertEqual(r, timedelta(seconds=30))
651
652 zerotd = timedelta(0)
653 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
654
655 self.assertRaises(TypeError, mod, t, 10)
656
657 def test_divmod(self):
658 t = timedelta(minutes=2, seconds=30)
659 minute = timedelta(minutes=1)
660 q, r = divmod(t, minute)
661 self.assertEqual(q, 2)
662 self.assertEqual(r, timedelta(seconds=30))
663
664 t = timedelta(minutes=-2, seconds=30)
665 q, r = divmod(t, minute)
666 self.assertEqual(q, -2)
667 self.assertEqual(r, timedelta(seconds=30))
668
669 zerotd = timedelta(0)
670 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
671
672 self.assertRaises(TypeError, divmod, t, 10)
673
674
Tim Peters2a799bf2002-12-16 20:18:38 +0000675#############################################################################
676# date tests
677
678class TestDateOnly(unittest.TestCase):
679 # Tests here won't pass if also run on datetime objects, so don't
680 # subclass this to test datetimes too.
681
682 def test_delta_non_days_ignored(self):
683 dt = date(2000, 1, 2)
684 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
685 microseconds=5)
686 days = timedelta(delta.days)
687 self.assertEqual(days, timedelta(1))
688
689 dt2 = dt + delta
690 self.assertEqual(dt2, dt + days)
691
692 dt2 = delta + dt
693 self.assertEqual(dt2, dt + days)
694
695 dt2 = dt - delta
696 self.assertEqual(dt2, dt - days)
697
698 delta = -delta
699 days = timedelta(delta.days)
700 self.assertEqual(days, timedelta(-2))
701
702 dt2 = dt + delta
703 self.assertEqual(dt2, dt + days)
704
705 dt2 = delta + dt
706 self.assertEqual(dt2, dt + days)
707
708 dt2 = dt - delta
709 self.assertEqual(dt2, dt - days)
710
Tim Peters604c0132004-06-07 23:04:33 +0000711class SubclassDate(date):
712 sub_var = 1
713
Guido van Rossumd8faa362007-04-27 19:54:29 +0000714class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000715 # Tests here should pass for both dates and datetimes, except for a
716 # few tests that TestDateTime overrides.
717
718 theclass = date
719
720 def test_basic_attributes(self):
721 dt = self.theclass(2002, 3, 1)
722 self.assertEqual(dt.year, 2002)
723 self.assertEqual(dt.month, 3)
724 self.assertEqual(dt.day, 1)
725
726 def test_roundtrip(self):
727 for dt in (self.theclass(1, 2, 3),
728 self.theclass.today()):
729 # Verify dt -> string -> date identity.
730 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000731 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000732 s = s[9:]
733 dt2 = eval(s)
734 self.assertEqual(dt, dt2)
735
736 # Verify identity via reconstructing from pieces.
737 dt2 = self.theclass(dt.year, dt.month, dt.day)
738 self.assertEqual(dt, dt2)
739
740 def test_ordinal_conversions(self):
741 # Check some fixed values.
742 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
743 (1, 12, 31, 365),
744 (2, 1, 1, 366),
745 # first example from "Calendrical Calculations"
746 (1945, 11, 12, 710347)]:
747 d = self.theclass(y, m, d)
748 self.assertEqual(n, d.toordinal())
749 fromord = self.theclass.fromordinal(n)
750 self.assertEqual(d, fromord)
751 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000752 # if we're checking something fancier than a date, verify
753 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000754 self.assertEqual(fromord.hour, 0)
755 self.assertEqual(fromord.minute, 0)
756 self.assertEqual(fromord.second, 0)
757 self.assertEqual(fromord.microsecond, 0)
758
Tim Peters0bf60bd2003-01-08 20:40:01 +0000759 # Check first and last days of year spottily across the whole
760 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000761 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000762 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
763 d = self.theclass(year, 1, 1)
764 n = d.toordinal()
765 d2 = self.theclass.fromordinal(n)
766 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000767 # Verify that moving back a day gets to the end of year-1.
768 if year > 1:
769 d = self.theclass.fromordinal(n-1)
770 d2 = self.theclass(year-1, 12, 31)
771 self.assertEqual(d, d2)
772 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000773
774 # Test every day in a leap-year and a non-leap year.
775 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
776 for year, isleap in (2000, True), (2002, False):
777 n = self.theclass(year, 1, 1).toordinal()
778 for month, maxday in zip(range(1, 13), dim):
779 if month == 2 and isleap:
780 maxday += 1
781 for day in range(1, maxday+1):
782 d = self.theclass(year, month, day)
783 self.assertEqual(d.toordinal(), n)
784 self.assertEqual(d, self.theclass.fromordinal(n))
785 n += 1
786
787 def test_extreme_ordinals(self):
788 a = self.theclass.min
789 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
790 aord = a.toordinal()
791 b = a.fromordinal(aord)
792 self.assertEqual(a, b)
793
794 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
795
796 b = a + timedelta(days=1)
797 self.assertEqual(b.toordinal(), aord + 1)
798 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
799
800 a = self.theclass.max
801 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
802 aord = a.toordinal()
803 b = a.fromordinal(aord)
804 self.assertEqual(a, b)
805
806 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
807
808 b = a - timedelta(days=1)
809 self.assertEqual(b.toordinal(), aord - 1)
810 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
811
812 def test_bad_constructor_arguments(self):
813 # bad years
814 self.theclass(MINYEAR, 1, 1) # no exception
815 self.theclass(MAXYEAR, 1, 1) # no exception
816 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
817 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
818 # bad months
819 self.theclass(2000, 1, 1) # no exception
820 self.theclass(2000, 12, 1) # no exception
821 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
822 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
823 # bad days
824 self.theclass(2000, 2, 29) # no exception
825 self.theclass(2004, 2, 29) # no exception
826 self.theclass(2400, 2, 29) # no exception
827 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
828 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
829 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
830 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
831 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
832 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
833
834 def test_hash_equality(self):
835 d = self.theclass(2000, 12, 31)
836 # same thing
837 e = self.theclass(2000, 12, 31)
838 self.assertEqual(d, e)
839 self.assertEqual(hash(d), hash(e))
840
841 dic = {d: 1}
842 dic[e] = 2
843 self.assertEqual(len(dic), 1)
844 self.assertEqual(dic[d], 2)
845 self.assertEqual(dic[e], 2)
846
847 d = self.theclass(2001, 1, 1)
848 # same thing
849 e = self.theclass(2001, 1, 1)
850 self.assertEqual(d, e)
851 self.assertEqual(hash(d), hash(e))
852
853 dic = {d: 1}
854 dic[e] = 2
855 self.assertEqual(len(dic), 1)
856 self.assertEqual(dic[d], 2)
857 self.assertEqual(dic[e], 2)
858
859 def test_computations(self):
860 a = self.theclass(2002, 1, 31)
861 b = self.theclass(1956, 1, 31)
862
863 diff = a-b
864 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
865 self.assertEqual(diff.seconds, 0)
866 self.assertEqual(diff.microseconds, 0)
867
868 day = timedelta(1)
869 week = timedelta(7)
870 a = self.theclass(2002, 3, 2)
871 self.assertEqual(a + day, self.theclass(2002, 3, 3))
872 self.assertEqual(day + a, self.theclass(2002, 3, 3))
873 self.assertEqual(a - day, self.theclass(2002, 3, 1))
874 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
875 self.assertEqual(a + week, self.theclass(2002, 3, 9))
876 self.assertEqual(a - week, self.theclass(2002, 2, 23))
877 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
878 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
879 self.assertEqual((a + week) - a, week)
880 self.assertEqual((a + day) - a, day)
881 self.assertEqual((a - week) - a, -week)
882 self.assertEqual((a - day) - a, -day)
883 self.assertEqual(a - (a + week), -week)
884 self.assertEqual(a - (a + day), -day)
885 self.assertEqual(a - (a - week), week)
886 self.assertEqual(a - (a - day), day)
887
Mark Dickinson5c2db372009-12-05 20:28:34 +0000888 # Add/sub ints or floats should be illegal
889 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000890 self.assertRaises(TypeError, lambda: a+i)
891 self.assertRaises(TypeError, lambda: a-i)
892 self.assertRaises(TypeError, lambda: i+a)
893 self.assertRaises(TypeError, lambda: i-a)
894
895 # delta - date is senseless.
896 self.assertRaises(TypeError, lambda: day - a)
897 # mixing date and (delta or date) via * or // is senseless
898 self.assertRaises(TypeError, lambda: day * a)
899 self.assertRaises(TypeError, lambda: a * day)
900 self.assertRaises(TypeError, lambda: day // a)
901 self.assertRaises(TypeError, lambda: a // day)
902 self.assertRaises(TypeError, lambda: a * a)
903 self.assertRaises(TypeError, lambda: a // a)
904 # date + date is senseless
905 self.assertRaises(TypeError, lambda: a + a)
906
907 def test_overflow(self):
908 tiny = self.theclass.resolution
909
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000910 for delta in [tiny, timedelta(1), timedelta(2)]:
911 dt = self.theclass.min + delta
912 dt -= delta # no problem
913 self.assertRaises(OverflowError, dt.__sub__, delta)
914 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000915
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000916 dt = self.theclass.max - delta
917 dt += delta # no problem
918 self.assertRaises(OverflowError, dt.__add__, delta)
919 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000920
921 def test_fromtimestamp(self):
922 import time
923
924 # Try an arbitrary fixed value.
925 year, month, day = 1999, 9, 19
926 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
927 d = self.theclass.fromtimestamp(ts)
928 self.assertEqual(d.year, year)
929 self.assertEqual(d.month, month)
930 self.assertEqual(d.day, day)
931
Tim Peters1b6f7a92004-06-20 02:50:16 +0000932 def test_insane_fromtimestamp(self):
933 # It's possible that some platform maps time_t to double,
934 # and that this test will fail there. This test should
935 # exempt such platforms (provided they return reasonable
936 # results!).
937 for insane in -1e200, 1e200:
938 self.assertRaises(ValueError, self.theclass.fromtimestamp,
939 insane)
940
Tim Peters2a799bf2002-12-16 20:18:38 +0000941 def test_today(self):
942 import time
943
944 # We claim that today() is like fromtimestamp(time.time()), so
945 # prove it.
946 for dummy in range(3):
947 today = self.theclass.today()
948 ts = time.time()
949 todayagain = self.theclass.fromtimestamp(ts)
950 if today == todayagain:
951 break
952 # There are several legit reasons that could fail:
953 # 1. It recently became midnight, between the today() and the
954 # time() calls.
955 # 2. The platform time() has such fine resolution that we'll
956 # never get the same value twice.
957 # 3. The platform time() has poor resolution, and we just
958 # happened to call today() right before a resolution quantum
959 # boundary.
960 # 4. The system clock got fiddled between calls.
961 # In any case, wait a little while and try again.
962 time.sleep(0.1)
963
964 # It worked or it didn't. If it didn't, assume it's reason #2, and
965 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000966 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000967 abs(todayagain - today) < timedelta(seconds=0.5))
968
969 def test_weekday(self):
970 for i in range(7):
971 # March 4, 2002 is a Monday
972 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
973 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
974 # January 2, 1956 is a Monday
975 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
976 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
977
978 def test_isocalendar(self):
979 # Check examples from
980 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
981 for i in range(7):
982 d = self.theclass(2003, 12, 22+i)
983 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
984 d = self.theclass(2003, 12, 29) + timedelta(i)
985 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
986 d = self.theclass(2004, 1, 5+i)
987 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
988 d = self.theclass(2009, 12, 21+i)
989 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
990 d = self.theclass(2009, 12, 28) + timedelta(i)
991 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
992 d = self.theclass(2010, 1, 4+i)
993 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
994
995 def test_iso_long_years(self):
996 # Calculate long ISO years and compare to table from
997 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
998 ISO_LONG_YEARS_TABLE = """
999 4 32 60 88
1000 9 37 65 93
1001 15 43 71 99
1002 20 48 76
1003 26 54 82
1004
1005 105 133 161 189
1006 111 139 167 195
1007 116 144 172
1008 122 150 178
1009 128 156 184
1010
1011 201 229 257 285
1012 207 235 263 291
1013 212 240 268 296
1014 218 246 274
1015 224 252 280
1016
1017 303 331 359 387
1018 308 336 364 392
1019 314 342 370 398
1020 320 348 376
1021 325 353 381
1022 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +00001023 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +00001024 L = []
1025 for i in range(400):
1026 d = self.theclass(2000+i, 12, 31)
1027 d1 = self.theclass(1600+i, 12, 31)
1028 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
1029 if d.isocalendar()[1] == 53:
1030 L.append(i)
1031 self.assertEqual(L, iso_long_years)
1032
1033 def test_isoformat(self):
1034 t = self.theclass(2, 3, 2)
1035 self.assertEqual(t.isoformat(), "0002-03-02")
1036
1037 def test_ctime(self):
1038 t = self.theclass(2002, 3, 2)
1039 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
1040
1041 def test_strftime(self):
1042 t = self.theclass(2005, 3, 2)
1043 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +00001044 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +00001045 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +00001046
1047 self.assertRaises(TypeError, t.strftime) # needs an arg
1048 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
1049 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
1050
Georg Brandlf78e02b2008-06-10 17:40:04 +00001051 # test that unicode input is allowed (issue 2782)
1052 self.assertEqual(t.strftime("%m"), "03")
1053
Tim Peters2a799bf2002-12-16 20:18:38 +00001054 # A naive object replaces %z and %Z w/ empty strings.
1055 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1056
Benjamin Petersone1cdfd72009-01-18 21:02:37 +00001057 #make sure that invalid format specifiers are handled correctly
1058 #self.assertRaises(ValueError, t.strftime, "%e")
1059 #self.assertRaises(ValueError, t.strftime, "%")
1060 #self.assertRaises(ValueError, t.strftime, "%#")
1061
1062 #oh well, some systems just ignore those invalid ones.
1063 #at least, excercise them to make sure that no crashes
1064 #are generated
1065 for f in ["%e", "%", "%#"]:
1066 try:
1067 t.strftime(f)
1068 except ValueError:
1069 pass
1070
1071 #check that this standard extension works
1072 t.strftime("%f")
1073
Georg Brandlf78e02b2008-06-10 17:40:04 +00001074
Eric Smith1ba31142007-09-11 18:06:02 +00001075 def test_format(self):
1076 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001077 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001078
1079 # check that a derived class's __str__() gets called
1080 class A(self.theclass):
1081 def __str__(self):
1082 return 'A'
1083 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001084 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001085
1086 # check that a derived class's strftime gets called
1087 class B(self.theclass):
1088 def strftime(self, format_spec):
1089 return 'B'
1090 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001091 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001092
1093 for fmt in ["m:%m d:%d y:%y",
1094 "m:%m d:%d y:%y H:%H M:%M S:%S",
1095 "%z %Z",
1096 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001097 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1098 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1099 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001100
Tim Peters2a799bf2002-12-16 20:18:38 +00001101 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00001102 self.assertIsInstance(self.theclass.min, self.theclass)
1103 self.assertIsInstance(self.theclass.max, self.theclass)
1104 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001105 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001106
1107 def test_extreme_timedelta(self):
1108 big = self.theclass.max - self.theclass.min
1109 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1110 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1111 # n == 315537897599999999 ~= 2**58.13
1112 justasbig = timedelta(0, 0, n)
1113 self.assertEqual(big, justasbig)
1114 self.assertEqual(self.theclass.min + big, self.theclass.max)
1115 self.assertEqual(self.theclass.max - big, self.theclass.min)
1116
1117 def test_timetuple(self):
1118 for i in range(7):
1119 # January 2, 1956 is a Monday (0)
1120 d = self.theclass(1956, 1, 2+i)
1121 t = d.timetuple()
1122 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1123 # February 1, 1956 is a Wednesday (2)
1124 d = self.theclass(1956, 2, 1+i)
1125 t = d.timetuple()
1126 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1127 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1128 # of the year.
1129 d = self.theclass(1956, 3, 1+i)
1130 t = d.timetuple()
1131 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1132 self.assertEqual(t.tm_year, 1956)
1133 self.assertEqual(t.tm_mon, 3)
1134 self.assertEqual(t.tm_mday, 1+i)
1135 self.assertEqual(t.tm_hour, 0)
1136 self.assertEqual(t.tm_min, 0)
1137 self.assertEqual(t.tm_sec, 0)
1138 self.assertEqual(t.tm_wday, (3+i)%7)
1139 self.assertEqual(t.tm_yday, 61+i)
1140 self.assertEqual(t.tm_isdst, -1)
1141
1142 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001143 args = 6, 7, 23
1144 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001145 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001146 green = pickler.dumps(orig, proto)
1147 derived = unpickler.loads(green)
1148 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001149
1150 def test_compare(self):
1151 t1 = self.theclass(2, 3, 4)
1152 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001153 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001154 self.assertTrue(t1 <= t2)
1155 self.assertTrue(t1 >= t2)
1156 self.assertTrue(not t1 != t2)
1157 self.assertTrue(not t1 < t2)
1158 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001159
1160 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1161 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001162 self.assertTrue(t1 < t2)
1163 self.assertTrue(t2 > t1)
1164 self.assertTrue(t1 <= t2)
1165 self.assertTrue(t2 >= t1)
1166 self.assertTrue(t1 != t2)
1167 self.assertTrue(t2 != t1)
1168 self.assertTrue(not t1 == t2)
1169 self.assertTrue(not t2 == t1)
1170 self.assertTrue(not t1 > t2)
1171 self.assertTrue(not t2 < t1)
1172 self.assertTrue(not t1 >= t2)
1173 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001174
Tim Peters68124bb2003-02-08 03:46:31 +00001175 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001176 self.assertEqual(t1 == badarg, False)
1177 self.assertEqual(t1 != badarg, True)
1178 self.assertEqual(badarg == t1, False)
1179 self.assertEqual(badarg != t1, True)
1180
Tim Peters2a799bf2002-12-16 20:18:38 +00001181 self.assertRaises(TypeError, lambda: t1 < badarg)
1182 self.assertRaises(TypeError, lambda: t1 > badarg)
1183 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001184 self.assertRaises(TypeError, lambda: badarg <= t1)
1185 self.assertRaises(TypeError, lambda: badarg < t1)
1186 self.assertRaises(TypeError, lambda: badarg > t1)
1187 self.assertRaises(TypeError, lambda: badarg >= t1)
1188
Tim Peters8d81a012003-01-24 22:36:34 +00001189 def test_mixed_compare(self):
1190 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001191
1192 # Our class can be compared for equality to other classes
1193 self.assertEqual(our == 1, False)
1194 self.assertEqual(1 == our, False)
1195 self.assertEqual(our != 1, True)
1196 self.assertEqual(1 != our, True)
1197
1198 # But the ordering is undefined
1199 self.assertRaises(TypeError, lambda: our < 1)
1200 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001201
Guido van Rossum19960592006-08-24 17:29:38 +00001202 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001203
Guido van Rossum19960592006-08-24 17:29:38 +00001204 class SomeClass:
1205 pass
1206
1207 their = SomeClass()
1208 self.assertEqual(our == their, False)
1209 self.assertEqual(their == our, False)
1210 self.assertEqual(our != their, True)
1211 self.assertEqual(their != our, True)
1212 self.assertRaises(TypeError, lambda: our < their)
1213 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001214
Guido van Rossum19960592006-08-24 17:29:38 +00001215 # However, if the other class explicitly defines ordering
1216 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001217
Guido van Rossum19960592006-08-24 17:29:38 +00001218 class LargerThanAnything:
1219 def __lt__(self, other):
1220 return False
1221 def __le__(self, other):
1222 return isinstance(other, LargerThanAnything)
1223 def __eq__(self, other):
1224 return isinstance(other, LargerThanAnything)
1225 def __ne__(self, other):
1226 return not isinstance(other, LargerThanAnything)
1227 def __gt__(self, other):
1228 return not isinstance(other, LargerThanAnything)
1229 def __ge__(self, other):
1230 return True
1231
1232 their = LargerThanAnything()
1233 self.assertEqual(our == their, False)
1234 self.assertEqual(their == our, False)
1235 self.assertEqual(our != their, True)
1236 self.assertEqual(their != our, True)
1237 self.assertEqual(our < their, True)
1238 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001239
Tim Peters2a799bf2002-12-16 20:18:38 +00001240 def test_bool(self):
1241 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001242 self.assertTrue(self.theclass.min)
1243 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001244
Guido van Rossum04110fb2007-08-24 16:32:05 +00001245 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001246 # For nasty technical reasons, we can't handle years before 1900.
1247 cls = self.theclass
1248 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1249 for y in 1, 49, 51, 99, 100, 1000, 1899:
1250 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001251
1252 def test_replace(self):
1253 cls = self.theclass
1254 args = [1, 2, 3]
1255 base = cls(*args)
1256 self.assertEqual(base, base.replace())
1257
1258 i = 0
1259 for name, newval in (("year", 2),
1260 ("month", 3),
1261 ("day", 4)):
1262 newargs = args[:]
1263 newargs[i] = newval
1264 expected = cls(*newargs)
1265 got = base.replace(**{name: newval})
1266 self.assertEqual(expected, got)
1267 i += 1
1268
1269 # Out of bounds.
1270 base = cls(2000, 2, 29)
1271 self.assertRaises(ValueError, base.replace, year=2001)
1272
Tim Petersa98924a2003-05-17 05:55:19 +00001273 def test_subclass_date(self):
1274
1275 class C(self.theclass):
1276 theAnswer = 42
1277
1278 def __new__(cls, *args, **kws):
1279 temp = kws.copy()
1280 extra = temp.pop('extra')
1281 result = self.theclass.__new__(cls, *args, **temp)
1282 result.extra = extra
1283 return result
1284
1285 def newmeth(self, start):
1286 return start + self.year + self.month
1287
1288 args = 2003, 4, 14
1289
1290 dt1 = self.theclass(*args)
1291 dt2 = C(*args, **{'extra': 7})
1292
1293 self.assertEqual(dt2.__class__, C)
1294 self.assertEqual(dt2.theAnswer, 42)
1295 self.assertEqual(dt2.extra, 7)
1296 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1297 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1298
Tim Peters604c0132004-06-07 23:04:33 +00001299 def test_pickling_subclass_date(self):
1300
1301 args = 6, 7, 23
1302 orig = SubclassDate(*args)
1303 for pickler, unpickler, proto in pickle_choices:
1304 green = pickler.dumps(orig, proto)
1305 derived = unpickler.loads(green)
1306 self.assertEqual(orig, derived)
1307
Tim Peters3f606292004-03-21 23:38:41 +00001308 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001309 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001310 # This is a low-overhead backdoor. A user can (by intent or
1311 # mistake) pass a string directly, which (if it's the right length)
1312 # will get treated like a pickle, and bypass the normal sanity
1313 # checks in the constructor. This can create insane objects.
1314 # The constructor doesn't want to burn the time to validate all
1315 # fields, but does check the month field. This stops, e.g.,
1316 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001317 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001318 if not issubclass(self.theclass, datetime):
1319 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001320 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001321 self.assertRaises(TypeError, self.theclass,
1322 base[:2] + month_byte + base[3:])
1323 for ord_byte in range(1, 13):
1324 # This shouldn't blow up because of the month byte alone. If
1325 # the implementation changes to do more-careful checking, it may
1326 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001327 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001328
Tim Peters2a799bf2002-12-16 20:18:38 +00001329#############################################################################
1330# datetime tests
1331
Tim Peters604c0132004-06-07 23:04:33 +00001332class SubclassDatetime(datetime):
1333 sub_var = 1
1334
Tim Peters2a799bf2002-12-16 20:18:38 +00001335class TestDateTime(TestDate):
1336
1337 theclass = datetime
1338
1339 def test_basic_attributes(self):
1340 dt = self.theclass(2002, 3, 1, 12, 0)
1341 self.assertEqual(dt.year, 2002)
1342 self.assertEqual(dt.month, 3)
1343 self.assertEqual(dt.day, 1)
1344 self.assertEqual(dt.hour, 12)
1345 self.assertEqual(dt.minute, 0)
1346 self.assertEqual(dt.second, 0)
1347 self.assertEqual(dt.microsecond, 0)
1348
1349 def test_basic_attributes_nonzero(self):
1350 # Make sure all attributes are non-zero so bugs in
1351 # bit-shifting access show up.
1352 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1353 self.assertEqual(dt.year, 2002)
1354 self.assertEqual(dt.month, 3)
1355 self.assertEqual(dt.day, 1)
1356 self.assertEqual(dt.hour, 12)
1357 self.assertEqual(dt.minute, 59)
1358 self.assertEqual(dt.second, 59)
1359 self.assertEqual(dt.microsecond, 8000)
1360
1361 def test_roundtrip(self):
1362 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1363 self.theclass.now()):
1364 # Verify dt -> string -> datetime identity.
1365 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001366 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001367 s = s[9:]
1368 dt2 = eval(s)
1369 self.assertEqual(dt, dt2)
1370
1371 # Verify identity via reconstructing from pieces.
1372 dt2 = self.theclass(dt.year, dt.month, dt.day,
1373 dt.hour, dt.minute, dt.second,
1374 dt.microsecond)
1375 self.assertEqual(dt, dt2)
1376
1377 def test_isoformat(self):
1378 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1379 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1380 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1381 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001382 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001383 # str is ISO format with the separator forced to a blank.
1384 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1385
1386 t = self.theclass(2, 3, 2)
1387 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1388 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1389 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1390 # str is ISO format with the separator forced to a blank.
1391 self.assertEqual(str(t), "0002-03-02 00:00:00")
1392
Eric Smith1ba31142007-09-11 18:06:02 +00001393 def test_format(self):
1394 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001395 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001396
1397 # check that a derived class's __str__() gets called
1398 class A(self.theclass):
1399 def __str__(self):
1400 return 'A'
1401 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001402 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001403
1404 # check that a derived class's strftime gets called
1405 class B(self.theclass):
1406 def strftime(self, format_spec):
1407 return 'B'
1408 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001409 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001410
1411 for fmt in ["m:%m d:%d y:%y",
1412 "m:%m d:%d y:%y H:%H M:%M S:%S",
1413 "%z %Z",
1414 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001415 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1416 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1417 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001418
Tim Peters2a799bf2002-12-16 20:18:38 +00001419 def test_more_ctime(self):
1420 # Test fields that TestDate doesn't touch.
1421 import time
1422
1423 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1424 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1425 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1426 # out. The difference is that t.ctime() produces " 2" for the day,
1427 # but platform ctime() produces "02" for the day. According to
1428 # C99, t.ctime() is correct here.
1429 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1430
1431 # So test a case where that difference doesn't matter.
1432 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1433 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1434
1435 def test_tz_independent_comparing(self):
1436 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1437 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1438 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1439 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001440 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001441
1442 # Make sure comparison doesn't forget microseconds, and isn't done
1443 # via comparing a float timestamp (an IEEE double doesn't have enough
1444 # precision to span microsecond resolution across years 1 thru 9999,
1445 # so comparing via timestamp necessarily calls some distinct values
1446 # equal).
1447 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1448 us = timedelta(microseconds=1)
1449 dt2 = dt1 + us
1450 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001451 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001452
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001453 def test_strftime_with_bad_tzname_replace(self):
1454 # verify ok if tzinfo.tzname().replace() returns a non-string
1455 class MyTzInfo(FixedOffset):
1456 def tzname(self, dt):
1457 class MyStr(str):
1458 def replace(self, *args):
1459 return None
1460 return MyStr('name')
1461 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1462 self.assertRaises(TypeError, t.strftime, '%Z')
1463
Tim Peters2a799bf2002-12-16 20:18:38 +00001464 def test_bad_constructor_arguments(self):
1465 # bad years
1466 self.theclass(MINYEAR, 1, 1) # no exception
1467 self.theclass(MAXYEAR, 1, 1) # no exception
1468 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1469 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1470 # bad months
1471 self.theclass(2000, 1, 1) # no exception
1472 self.theclass(2000, 12, 1) # no exception
1473 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1474 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1475 # bad days
1476 self.theclass(2000, 2, 29) # no exception
1477 self.theclass(2004, 2, 29) # no exception
1478 self.theclass(2400, 2, 29) # no exception
1479 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1480 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1481 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1482 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1483 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1484 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1485 # bad hours
1486 self.theclass(2000, 1, 31, 0) # no exception
1487 self.theclass(2000, 1, 31, 23) # no exception
1488 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1489 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1490 # bad minutes
1491 self.theclass(2000, 1, 31, 23, 0) # no exception
1492 self.theclass(2000, 1, 31, 23, 59) # no exception
1493 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1494 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1495 # bad seconds
1496 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1497 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1498 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1499 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1500 # bad microseconds
1501 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1502 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1503 self.assertRaises(ValueError, self.theclass,
1504 2000, 1, 31, 23, 59, 59, -1)
1505 self.assertRaises(ValueError, self.theclass,
1506 2000, 1, 31, 23, 59, 59,
1507 1000000)
1508
1509 def test_hash_equality(self):
1510 d = self.theclass(2000, 12, 31, 23, 30, 17)
1511 e = self.theclass(2000, 12, 31, 23, 30, 17)
1512 self.assertEqual(d, e)
1513 self.assertEqual(hash(d), hash(e))
1514
1515 dic = {d: 1}
1516 dic[e] = 2
1517 self.assertEqual(len(dic), 1)
1518 self.assertEqual(dic[d], 2)
1519 self.assertEqual(dic[e], 2)
1520
1521 d = self.theclass(2001, 1, 1, 0, 5, 17)
1522 e = self.theclass(2001, 1, 1, 0, 5, 17)
1523 self.assertEqual(d, e)
1524 self.assertEqual(hash(d), hash(e))
1525
1526 dic = {d: 1}
1527 dic[e] = 2
1528 self.assertEqual(len(dic), 1)
1529 self.assertEqual(dic[d], 2)
1530 self.assertEqual(dic[e], 2)
1531
1532 def test_computations(self):
1533 a = self.theclass(2002, 1, 31)
1534 b = self.theclass(1956, 1, 31)
1535 diff = a-b
1536 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1537 self.assertEqual(diff.seconds, 0)
1538 self.assertEqual(diff.microseconds, 0)
1539 a = self.theclass(2002, 3, 2, 17, 6)
1540 millisec = timedelta(0, 0, 1000)
1541 hour = timedelta(0, 3600)
1542 day = timedelta(1)
1543 week = timedelta(7)
1544 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1545 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1546 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1547 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1548 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1549 self.assertEqual(a - hour, a + -hour)
1550 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1551 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1552 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1553 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1554 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1555 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1556 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1557 self.assertEqual((a + week) - a, week)
1558 self.assertEqual((a + day) - a, day)
1559 self.assertEqual((a + hour) - a, hour)
1560 self.assertEqual((a + millisec) - a, millisec)
1561 self.assertEqual((a - week) - a, -week)
1562 self.assertEqual((a - day) - a, -day)
1563 self.assertEqual((a - hour) - a, -hour)
1564 self.assertEqual((a - millisec) - a, -millisec)
1565 self.assertEqual(a - (a + week), -week)
1566 self.assertEqual(a - (a + day), -day)
1567 self.assertEqual(a - (a + hour), -hour)
1568 self.assertEqual(a - (a + millisec), -millisec)
1569 self.assertEqual(a - (a - week), week)
1570 self.assertEqual(a - (a - day), day)
1571 self.assertEqual(a - (a - hour), hour)
1572 self.assertEqual(a - (a - millisec), millisec)
1573 self.assertEqual(a + (week + day + hour + millisec),
1574 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1575 self.assertEqual(a + (week + day + hour + millisec),
1576 (((a + week) + day) + hour) + millisec)
1577 self.assertEqual(a - (week + day + hour + millisec),
1578 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1579 self.assertEqual(a - (week + day + hour + millisec),
1580 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001581 # Add/sub ints or floats should be illegal
1582 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001583 self.assertRaises(TypeError, lambda: a+i)
1584 self.assertRaises(TypeError, lambda: a-i)
1585 self.assertRaises(TypeError, lambda: i+a)
1586 self.assertRaises(TypeError, lambda: i-a)
1587
1588 # delta - datetime is senseless.
1589 self.assertRaises(TypeError, lambda: day - a)
1590 # mixing datetime and (delta or datetime) via * or // is senseless
1591 self.assertRaises(TypeError, lambda: day * a)
1592 self.assertRaises(TypeError, lambda: a * day)
1593 self.assertRaises(TypeError, lambda: day // a)
1594 self.assertRaises(TypeError, lambda: a // day)
1595 self.assertRaises(TypeError, lambda: a * a)
1596 self.assertRaises(TypeError, lambda: a // a)
1597 # datetime + datetime is senseless
1598 self.assertRaises(TypeError, lambda: a + a)
1599
1600 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001601 args = 6, 7, 23, 20, 59, 1, 64**2
1602 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001603 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001604 green = pickler.dumps(orig, proto)
1605 derived = unpickler.loads(green)
1606 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001607
Guido van Rossum275666f2003-02-07 21:49:01 +00001608 def test_more_pickling(self):
1609 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1610 s = pickle.dumps(a)
1611 b = pickle.loads(s)
1612 self.assertEqual(b.year, 2003)
1613 self.assertEqual(b.month, 2)
1614 self.assertEqual(b.day, 7)
1615
Tim Peters604c0132004-06-07 23:04:33 +00001616 def test_pickling_subclass_datetime(self):
1617 args = 6, 7, 23, 20, 59, 1, 64**2
1618 orig = SubclassDatetime(*args)
1619 for pickler, unpickler, proto in pickle_choices:
1620 green = pickler.dumps(orig, proto)
1621 derived = unpickler.loads(green)
1622 self.assertEqual(orig, derived)
1623
Tim Peters2a799bf2002-12-16 20:18:38 +00001624 def test_more_compare(self):
1625 # The test_compare() inherited from TestDate covers the error cases.
1626 # We just want to test lexicographic ordering on the members datetime
1627 # has that date lacks.
1628 args = [2000, 11, 29, 20, 58, 16, 999998]
1629 t1 = self.theclass(*args)
1630 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001631 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001632 self.assertTrue(t1 <= t2)
1633 self.assertTrue(t1 >= t2)
1634 self.assertTrue(not t1 != t2)
1635 self.assertTrue(not t1 < t2)
1636 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001637
1638 for i in range(len(args)):
1639 newargs = args[:]
1640 newargs[i] = args[i] + 1
1641 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001642 self.assertTrue(t1 < t2)
1643 self.assertTrue(t2 > t1)
1644 self.assertTrue(t1 <= t2)
1645 self.assertTrue(t2 >= t1)
1646 self.assertTrue(t1 != t2)
1647 self.assertTrue(t2 != t1)
1648 self.assertTrue(not t1 == t2)
1649 self.assertTrue(not t2 == t1)
1650 self.assertTrue(not t1 > t2)
1651 self.assertTrue(not t2 < t1)
1652 self.assertTrue(not t1 >= t2)
1653 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001654
1655
1656 # A helper for timestamp constructor tests.
1657 def verify_field_equality(self, expected, got):
1658 self.assertEqual(expected.tm_year, got.year)
1659 self.assertEqual(expected.tm_mon, got.month)
1660 self.assertEqual(expected.tm_mday, got.day)
1661 self.assertEqual(expected.tm_hour, got.hour)
1662 self.assertEqual(expected.tm_min, got.minute)
1663 self.assertEqual(expected.tm_sec, got.second)
1664
1665 def test_fromtimestamp(self):
1666 import time
1667
1668 ts = time.time()
1669 expected = time.localtime(ts)
1670 got = self.theclass.fromtimestamp(ts)
1671 self.verify_field_equality(expected, got)
1672
1673 def test_utcfromtimestamp(self):
1674 import time
1675
1676 ts = time.time()
1677 expected = time.gmtime(ts)
1678 got = self.theclass.utcfromtimestamp(ts)
1679 self.verify_field_equality(expected, got)
1680
Thomas Wouters477c8d52006-05-27 19:21:47 +00001681 def test_microsecond_rounding(self):
1682 # Test whether fromtimestamp "rounds up" floats that are less
1683 # than one microsecond smaller than an integer.
1684 self.assertEquals(self.theclass.fromtimestamp(0.9999999),
1685 self.theclass.fromtimestamp(1))
1686
Tim Peters1b6f7a92004-06-20 02:50:16 +00001687 def test_insane_fromtimestamp(self):
1688 # It's possible that some platform maps time_t to double,
1689 # and that this test will fail there. This test should
1690 # exempt such platforms (provided they return reasonable
1691 # results!).
1692 for insane in -1e200, 1e200:
1693 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1694 insane)
1695
1696 def test_insane_utcfromtimestamp(self):
1697 # It's possible that some platform maps time_t to double,
1698 # and that this test will fail there. This test should
1699 # exempt such platforms (provided they return reasonable
1700 # results!).
1701 for insane in -1e200, 1e200:
1702 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1703 insane)
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001704 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001705 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001706 # The result is tz-dependent; at least test that this doesn't
1707 # fail (like it did before bug 1646728 was fixed).
1708 self.theclass.fromtimestamp(-1.05)
1709
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001710 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001711 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001712 d = self.theclass.utcfromtimestamp(-1.05)
1713 self.assertEquals(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1714
Tim Peters2a799bf2002-12-16 20:18:38 +00001715 def test_utcnow(self):
1716 import time
1717
1718 # Call it a success if utcnow() and utcfromtimestamp() are within
1719 # a second of each other.
1720 tolerance = timedelta(seconds=1)
1721 for dummy in range(3):
1722 from_now = self.theclass.utcnow()
1723 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1724 if abs(from_timestamp - from_now) <= tolerance:
1725 break
1726 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001727 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001728
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001729 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001730 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001731
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001732 string = '2004-12-01 13:02:47.197'
1733 format = '%Y-%m-%d %H:%M:%S.%f'
1734 result, frac = _strptime._strptime(string, format)
1735 expected = self.theclass(*(result[0:6]+(frac,)))
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001736 got = self.theclass.strptime(string, format)
1737 self.assertEqual(expected, got)
1738
Tim Peters2a799bf2002-12-16 20:18:38 +00001739 def test_more_timetuple(self):
1740 # This tests fields beyond those tested by the TestDate.test_timetuple.
1741 t = self.theclass(2004, 12, 31, 6, 22, 33)
1742 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1743 self.assertEqual(t.timetuple(),
1744 (t.year, t.month, t.day,
1745 t.hour, t.minute, t.second,
1746 t.weekday(),
1747 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1748 -1))
1749 tt = t.timetuple()
1750 self.assertEqual(tt.tm_year, t.year)
1751 self.assertEqual(tt.tm_mon, t.month)
1752 self.assertEqual(tt.tm_mday, t.day)
1753 self.assertEqual(tt.tm_hour, t.hour)
1754 self.assertEqual(tt.tm_min, t.minute)
1755 self.assertEqual(tt.tm_sec, t.second)
1756 self.assertEqual(tt.tm_wday, t.weekday())
1757 self.assertEqual(tt.tm_yday, t.toordinal() -
1758 date(t.year, 1, 1).toordinal() + 1)
1759 self.assertEqual(tt.tm_isdst, -1)
1760
1761 def test_more_strftime(self):
1762 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001763 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1764 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1765 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001766
1767 def test_extract(self):
1768 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1769 self.assertEqual(dt.date(), date(2002, 3, 4))
1770 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1771
1772 def test_combine(self):
1773 d = date(2002, 3, 4)
1774 t = time(18, 45, 3, 1234)
1775 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1776 combine = self.theclass.combine
1777 dt = combine(d, t)
1778 self.assertEqual(dt, expected)
1779
1780 dt = combine(time=t, date=d)
1781 self.assertEqual(dt, expected)
1782
1783 self.assertEqual(d, dt.date())
1784 self.assertEqual(t, dt.time())
1785 self.assertEqual(dt, combine(dt.date(), dt.time()))
1786
1787 self.assertRaises(TypeError, combine) # need an arg
1788 self.assertRaises(TypeError, combine, d) # need two args
1789 self.assertRaises(TypeError, combine, t, d) # args reversed
1790 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1791 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1792
Tim Peters12bf3392002-12-24 05:41:27 +00001793 def test_replace(self):
1794 cls = self.theclass
1795 args = [1, 2, 3, 4, 5, 6, 7]
1796 base = cls(*args)
1797 self.assertEqual(base, base.replace())
1798
1799 i = 0
1800 for name, newval in (("year", 2),
1801 ("month", 3),
1802 ("day", 4),
1803 ("hour", 5),
1804 ("minute", 6),
1805 ("second", 7),
1806 ("microsecond", 8)):
1807 newargs = args[:]
1808 newargs[i] = newval
1809 expected = cls(*newargs)
1810 got = base.replace(**{name: newval})
1811 self.assertEqual(expected, got)
1812 i += 1
1813
1814 # Out of bounds.
1815 base = cls(2000, 2, 29)
1816 self.assertRaises(ValueError, base.replace, year=2001)
1817
Tim Peters80475bb2002-12-25 07:40:55 +00001818 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001819 # Pretty boring! The TZ test is more interesting here. astimezone()
1820 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001821 dt = self.theclass.now()
1822 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001823 self.assertRaises(TypeError, dt.astimezone) # not enough args
1824 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1825 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001826 self.assertRaises(ValueError, dt.astimezone, f) # naive
1827 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001828
Tim Peters52dcce22003-01-23 16:36:11 +00001829 class Bogus(tzinfo):
1830 def utcoffset(self, dt): return None
1831 def dst(self, dt): return timedelta(0)
1832 bog = Bogus()
1833 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1834
1835 class AlsoBogus(tzinfo):
1836 def utcoffset(self, dt): return timedelta(0)
1837 def dst(self, dt): return None
1838 alsobog = AlsoBogus()
1839 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001840
Tim Petersa98924a2003-05-17 05:55:19 +00001841 def test_subclass_datetime(self):
1842
1843 class C(self.theclass):
1844 theAnswer = 42
1845
1846 def __new__(cls, *args, **kws):
1847 temp = kws.copy()
1848 extra = temp.pop('extra')
1849 result = self.theclass.__new__(cls, *args, **temp)
1850 result.extra = extra
1851 return result
1852
1853 def newmeth(self, start):
1854 return start + self.year + self.month + self.second
1855
1856 args = 2003, 4, 14, 12, 13, 41
1857
1858 dt1 = self.theclass(*args)
1859 dt2 = C(*args, **{'extra': 7})
1860
1861 self.assertEqual(dt2.__class__, C)
1862 self.assertEqual(dt2.theAnswer, 42)
1863 self.assertEqual(dt2.extra, 7)
1864 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1865 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1866 dt1.second - 7)
1867
Tim Peters604c0132004-06-07 23:04:33 +00001868class SubclassTime(time):
1869 sub_var = 1
1870
Guido van Rossumd8faa362007-04-27 19:54:29 +00001871class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001872
1873 theclass = time
1874
1875 def test_basic_attributes(self):
1876 t = self.theclass(12, 0)
1877 self.assertEqual(t.hour, 12)
1878 self.assertEqual(t.minute, 0)
1879 self.assertEqual(t.second, 0)
1880 self.assertEqual(t.microsecond, 0)
1881
1882 def test_basic_attributes_nonzero(self):
1883 # Make sure all attributes are non-zero so bugs in
1884 # bit-shifting access show up.
1885 t = self.theclass(12, 59, 59, 8000)
1886 self.assertEqual(t.hour, 12)
1887 self.assertEqual(t.minute, 59)
1888 self.assertEqual(t.second, 59)
1889 self.assertEqual(t.microsecond, 8000)
1890
1891 def test_roundtrip(self):
1892 t = self.theclass(1, 2, 3, 4)
1893
1894 # Verify t -> string -> time identity.
1895 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001896 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001897 s = s[9:]
1898 t2 = eval(s)
1899 self.assertEqual(t, t2)
1900
1901 # Verify identity via reconstructing from pieces.
1902 t2 = self.theclass(t.hour, t.minute, t.second,
1903 t.microsecond)
1904 self.assertEqual(t, t2)
1905
1906 def test_comparing(self):
1907 args = [1, 2, 3, 4]
1908 t1 = self.theclass(*args)
1909 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001910 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001911 self.assertTrue(t1 <= t2)
1912 self.assertTrue(t1 >= t2)
1913 self.assertTrue(not t1 != t2)
1914 self.assertTrue(not t1 < t2)
1915 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001916
1917 for i in range(len(args)):
1918 newargs = args[:]
1919 newargs[i] = args[i] + 1
1920 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001921 self.assertTrue(t1 < t2)
1922 self.assertTrue(t2 > t1)
1923 self.assertTrue(t1 <= t2)
1924 self.assertTrue(t2 >= t1)
1925 self.assertTrue(t1 != t2)
1926 self.assertTrue(t2 != t1)
1927 self.assertTrue(not t1 == t2)
1928 self.assertTrue(not t2 == t1)
1929 self.assertTrue(not t1 > t2)
1930 self.assertTrue(not t2 < t1)
1931 self.assertTrue(not t1 >= t2)
1932 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001933
Tim Peters68124bb2003-02-08 03:46:31 +00001934 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001935 self.assertEqual(t1 == badarg, False)
1936 self.assertEqual(t1 != badarg, True)
1937 self.assertEqual(badarg == t1, False)
1938 self.assertEqual(badarg != t1, True)
1939
Tim Peters2a799bf2002-12-16 20:18:38 +00001940 self.assertRaises(TypeError, lambda: t1 <= badarg)
1941 self.assertRaises(TypeError, lambda: t1 < badarg)
1942 self.assertRaises(TypeError, lambda: t1 > badarg)
1943 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001944 self.assertRaises(TypeError, lambda: badarg <= t1)
1945 self.assertRaises(TypeError, lambda: badarg < t1)
1946 self.assertRaises(TypeError, lambda: badarg > t1)
1947 self.assertRaises(TypeError, lambda: badarg >= t1)
1948
1949 def test_bad_constructor_arguments(self):
1950 # bad hours
1951 self.theclass(0, 0) # no exception
1952 self.theclass(23, 0) # no exception
1953 self.assertRaises(ValueError, self.theclass, -1, 0)
1954 self.assertRaises(ValueError, self.theclass, 24, 0)
1955 # bad minutes
1956 self.theclass(23, 0) # no exception
1957 self.theclass(23, 59) # no exception
1958 self.assertRaises(ValueError, self.theclass, 23, -1)
1959 self.assertRaises(ValueError, self.theclass, 23, 60)
1960 # bad seconds
1961 self.theclass(23, 59, 0) # no exception
1962 self.theclass(23, 59, 59) # no exception
1963 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1964 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1965 # bad microseconds
1966 self.theclass(23, 59, 59, 0) # no exception
1967 self.theclass(23, 59, 59, 999999) # no exception
1968 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
1969 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
1970
1971 def test_hash_equality(self):
1972 d = self.theclass(23, 30, 17)
1973 e = self.theclass(23, 30, 17)
1974 self.assertEqual(d, e)
1975 self.assertEqual(hash(d), hash(e))
1976
1977 dic = {d: 1}
1978 dic[e] = 2
1979 self.assertEqual(len(dic), 1)
1980 self.assertEqual(dic[d], 2)
1981 self.assertEqual(dic[e], 2)
1982
1983 d = self.theclass(0, 5, 17)
1984 e = self.theclass(0, 5, 17)
1985 self.assertEqual(d, e)
1986 self.assertEqual(hash(d), hash(e))
1987
1988 dic = {d: 1}
1989 dic[e] = 2
1990 self.assertEqual(len(dic), 1)
1991 self.assertEqual(dic[d], 2)
1992 self.assertEqual(dic[e], 2)
1993
1994 def test_isoformat(self):
1995 t = self.theclass(4, 5, 1, 123)
1996 self.assertEqual(t.isoformat(), "04:05:01.000123")
1997 self.assertEqual(t.isoformat(), str(t))
1998
1999 t = self.theclass()
2000 self.assertEqual(t.isoformat(), "00:00:00")
2001 self.assertEqual(t.isoformat(), str(t))
2002
2003 t = self.theclass(microsecond=1)
2004 self.assertEqual(t.isoformat(), "00:00:00.000001")
2005 self.assertEqual(t.isoformat(), str(t))
2006
2007 t = self.theclass(microsecond=10)
2008 self.assertEqual(t.isoformat(), "00:00:00.000010")
2009 self.assertEqual(t.isoformat(), str(t))
2010
2011 t = self.theclass(microsecond=100)
2012 self.assertEqual(t.isoformat(), "00:00:00.000100")
2013 self.assertEqual(t.isoformat(), str(t))
2014
2015 t = self.theclass(microsecond=1000)
2016 self.assertEqual(t.isoformat(), "00:00:00.001000")
2017 self.assertEqual(t.isoformat(), str(t))
2018
2019 t = self.theclass(microsecond=10000)
2020 self.assertEqual(t.isoformat(), "00:00:00.010000")
2021 self.assertEqual(t.isoformat(), str(t))
2022
2023 t = self.theclass(microsecond=100000)
2024 self.assertEqual(t.isoformat(), "00:00:00.100000")
2025 self.assertEqual(t.isoformat(), str(t))
2026
Thomas Wouterscf297e42007-02-23 15:07:44 +00002027 def test_1653736(self):
2028 # verify it doesn't accept extra keyword arguments
2029 t = self.theclass(second=1)
2030 self.assertRaises(TypeError, t.isoformat, foo=3)
2031
Tim Peters2a799bf2002-12-16 20:18:38 +00002032 def test_strftime(self):
2033 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00002034 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00002035 # A naive object replaces %z and %Z with empty strings.
2036 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2037
Eric Smith1ba31142007-09-11 18:06:02 +00002038 def test_format(self):
2039 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002040 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002041
2042 # check that a derived class's __str__() gets called
2043 class A(self.theclass):
2044 def __str__(self):
2045 return 'A'
2046 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002047 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00002048
2049 # check that a derived class's strftime gets called
2050 class B(self.theclass):
2051 def strftime(self, format_spec):
2052 return 'B'
2053 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002054 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002055
2056 for fmt in ['%H %M %S',
2057 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00002058 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2059 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2060 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00002061
Tim Peters2a799bf2002-12-16 20:18:38 +00002062 def test_str(self):
2063 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2064 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2065 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2066 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2067 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2068
2069 def test_repr(self):
2070 name = 'datetime.' + self.theclass.__name__
2071 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2072 "%s(1, 2, 3, 4)" % name)
2073 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2074 "%s(10, 2, 3, 4000)" % name)
2075 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2076 "%s(0, 2, 3, 400000)" % name)
2077 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2078 "%s(12, 2, 3)" % name)
2079 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2080 "%s(23, 15)" % name)
2081
2082 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00002083 self.assertIsInstance(self.theclass.min, self.theclass)
2084 self.assertIsInstance(self.theclass.max, self.theclass)
2085 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002086 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00002087
2088 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002089 args = 20, 59, 16, 64**2
2090 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002091 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002092 green = pickler.dumps(orig, proto)
2093 derived = unpickler.loads(green)
2094 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002095
Tim Peters604c0132004-06-07 23:04:33 +00002096 def test_pickling_subclass_time(self):
2097 args = 20, 59, 16, 64**2
2098 orig = SubclassTime(*args)
2099 for pickler, unpickler, proto in pickle_choices:
2100 green = pickler.dumps(orig, proto)
2101 derived = unpickler.loads(green)
2102 self.assertEqual(orig, derived)
2103
Tim Peters2a799bf2002-12-16 20:18:38 +00002104 def test_bool(self):
2105 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002106 self.assertTrue(cls(1))
2107 self.assertTrue(cls(0, 1))
2108 self.assertTrue(cls(0, 0, 1))
2109 self.assertTrue(cls(0, 0, 0, 1))
2110 self.assertTrue(not cls(0))
2111 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00002112
Tim Peters12bf3392002-12-24 05:41:27 +00002113 def test_replace(self):
2114 cls = self.theclass
2115 args = [1, 2, 3, 4]
2116 base = cls(*args)
2117 self.assertEqual(base, base.replace())
2118
2119 i = 0
2120 for name, newval in (("hour", 5),
2121 ("minute", 6),
2122 ("second", 7),
2123 ("microsecond", 8)):
2124 newargs = args[:]
2125 newargs[i] = newval
2126 expected = cls(*newargs)
2127 got = base.replace(**{name: newval})
2128 self.assertEqual(expected, got)
2129 i += 1
2130
2131 # Out of bounds.
2132 base = cls(1)
2133 self.assertRaises(ValueError, base.replace, hour=24)
2134 self.assertRaises(ValueError, base.replace, minute=-1)
2135 self.assertRaises(ValueError, base.replace, second=100)
2136 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2137
Tim Petersa98924a2003-05-17 05:55:19 +00002138 def test_subclass_time(self):
2139
2140 class C(self.theclass):
2141 theAnswer = 42
2142
2143 def __new__(cls, *args, **kws):
2144 temp = kws.copy()
2145 extra = temp.pop('extra')
2146 result = self.theclass.__new__(cls, *args, **temp)
2147 result.extra = extra
2148 return result
2149
2150 def newmeth(self, start):
2151 return start + self.hour + self.second
2152
2153 args = 4, 5, 6
2154
2155 dt1 = self.theclass(*args)
2156 dt2 = C(*args, **{'extra': 7})
2157
2158 self.assertEqual(dt2.__class__, C)
2159 self.assertEqual(dt2.theAnswer, 42)
2160 self.assertEqual(dt2.extra, 7)
2161 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2162 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2163
Armin Rigof4afb212005-11-07 07:15:48 +00002164 def test_backdoor_resistance(self):
2165 # see TestDate.test_backdoor_resistance().
2166 base = '2:59.0'
2167 for hour_byte in ' ', '9', chr(24), '\xff':
2168 self.assertRaises(TypeError, self.theclass,
2169 hour_byte + base[1:])
2170
Tim Peters855fe882002-12-22 03:43:39 +00002171# A mixin for classes with a tzinfo= argument. Subclasses must define
2172# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002173# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002174class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002175
Tim Petersbad8ff02002-12-30 20:52:32 +00002176 def test_argument_passing(self):
2177 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002178 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002179 class introspective(tzinfo):
2180 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002181 def utcoffset(self, dt):
2182 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002183 dst = utcoffset
2184
2185 obj = cls(1, 2, 3, tzinfo=introspective())
2186
Tim Peters0bf60bd2003-01-08 20:40:01 +00002187 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002188 self.assertEqual(obj.tzname(), expected)
2189
Tim Peters0bf60bd2003-01-08 20:40:01 +00002190 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002191 self.assertEqual(obj.utcoffset(), expected)
2192 self.assertEqual(obj.dst(), expected)
2193
Tim Peters855fe882002-12-22 03:43:39 +00002194 def test_bad_tzinfo_classes(self):
2195 cls = self.theclass
2196 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002197
Tim Peters855fe882002-12-22 03:43:39 +00002198 class NiceTry(object):
2199 def __init__(self): pass
2200 def utcoffset(self, dt): pass
2201 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2202
2203 class BetterTry(tzinfo):
2204 def __init__(self): pass
2205 def utcoffset(self, dt): pass
2206 b = BetterTry()
2207 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002208 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002209
2210 def test_utc_offset_out_of_bounds(self):
2211 class Edgy(tzinfo):
2212 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002213 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002214 def utcoffset(self, dt):
2215 return self.offset
2216
2217 cls = self.theclass
2218 for offset, legit in ((-1440, False),
2219 (-1439, True),
2220 (1439, True),
2221 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002222 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002223 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002224 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002225 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002226 else:
2227 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002228 if legit:
2229 aofs = abs(offset)
2230 h, m = divmod(aofs, 60)
2231 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002232 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002233 t = t.timetz()
2234 self.assertEqual(str(t), "01:02:03" + tag)
2235 else:
2236 self.assertRaises(ValueError, str, t)
2237
2238 def test_tzinfo_classes(self):
2239 cls = self.theclass
2240 class C1(tzinfo):
2241 def utcoffset(self, dt): return None
2242 def dst(self, dt): return None
2243 def tzname(self, dt): return None
2244 for t in (cls(1, 1, 1),
2245 cls(1, 1, 1, tzinfo=None),
2246 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002247 self.assertTrue(t.utcoffset() is None)
2248 self.assertTrue(t.dst() is None)
2249 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002250
Tim Peters855fe882002-12-22 03:43:39 +00002251 class C3(tzinfo):
2252 def utcoffset(self, dt): return timedelta(minutes=-1439)
2253 def dst(self, dt): return timedelta(minutes=1439)
2254 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002255 t = cls(1, 1, 1, tzinfo=C3())
2256 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2257 self.assertEqual(t.dst(), timedelta(minutes=1439))
2258 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002259
2260 # Wrong types.
2261 class C4(tzinfo):
2262 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002263 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002264 def tzname(self, dt): return 0
2265 t = cls(1, 1, 1, tzinfo=C4())
2266 self.assertRaises(TypeError, t.utcoffset)
2267 self.assertRaises(TypeError, t.dst)
2268 self.assertRaises(TypeError, t.tzname)
2269
2270 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002271 class C6(tzinfo):
2272 def utcoffset(self, dt): return timedelta(hours=-24)
2273 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002274 t = cls(1, 1, 1, tzinfo=C6())
2275 self.assertRaises(ValueError, t.utcoffset)
2276 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002277
2278 # Not a whole number of minutes.
2279 class C7(tzinfo):
2280 def utcoffset(self, dt): return timedelta(seconds=61)
2281 def dst(self, dt): return timedelta(microseconds=-81)
2282 t = cls(1, 1, 1, tzinfo=C7())
2283 self.assertRaises(ValueError, t.utcoffset)
2284 self.assertRaises(ValueError, t.dst)
2285
Tim Peters4c0db782002-12-26 05:01:19 +00002286 def test_aware_compare(self):
2287 cls = self.theclass
2288
Tim Peters60c76e42002-12-27 00:41:11 +00002289 # Ensure that utcoffset() gets ignored if the comparands have
2290 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002291 class OperandDependentOffset(tzinfo):
2292 def utcoffset(self, t):
2293 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002294 # d0 and d1 equal after adjustment
2295 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002296 else:
Tim Peters397301e2003-01-02 21:28:08 +00002297 # d2 off in the weeds
2298 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002299
2300 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2301 d0 = base.replace(minute=3)
2302 d1 = base.replace(minute=9)
2303 d2 = base.replace(minute=11)
2304 for x in d0, d1, d2:
2305 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002306 for op in lt, le, gt, ge, eq, ne:
2307 got = op(x, y)
2308 expected = op(x.minute, y.minute)
2309 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002310
2311 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002312 # Note that a time can't actually have an operand-depedent offset,
2313 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2314 # so skip this test for time.
2315 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002316 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2317 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2318 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2319 for x in d0, d1, d2:
2320 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002321 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002322 if (x is d0 or x is d1) and (y is d0 or y is d1):
2323 expected = 0
2324 elif x is y is d2:
2325 expected = 0
2326 elif x is d2:
2327 expected = -1
2328 else:
2329 assert y is d2
2330 expected = 1
2331 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002332
Tim Peters855fe882002-12-22 03:43:39 +00002333
Tim Peters0bf60bd2003-01-08 20:40:01 +00002334# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002335class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002336 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002337
2338 def test_empty(self):
2339 t = self.theclass()
2340 self.assertEqual(t.hour, 0)
2341 self.assertEqual(t.minute, 0)
2342 self.assertEqual(t.second, 0)
2343 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002344 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002345
Tim Peters2a799bf2002-12-16 20:18:38 +00002346 def test_zones(self):
2347 est = FixedOffset(-300, "EST", 1)
2348 utc = FixedOffset(0, "UTC", -2)
2349 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002350 t1 = time( 7, 47, tzinfo=est)
2351 t2 = time(12, 47, tzinfo=utc)
2352 t3 = time(13, 47, tzinfo=met)
2353 t4 = time(microsecond=40)
2354 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002355
2356 self.assertEqual(t1.tzinfo, est)
2357 self.assertEqual(t2.tzinfo, utc)
2358 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002359 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002360 self.assertEqual(t5.tzinfo, utc)
2361
Tim Peters855fe882002-12-22 03:43:39 +00002362 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2363 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2364 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002365 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002366 self.assertRaises(TypeError, t1.utcoffset, "no args")
2367
2368 self.assertEqual(t1.tzname(), "EST")
2369 self.assertEqual(t2.tzname(), "UTC")
2370 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002371 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002372 self.assertRaises(TypeError, t1.tzname, "no args")
2373
Tim Peters855fe882002-12-22 03:43:39 +00002374 self.assertEqual(t1.dst(), timedelta(minutes=1))
2375 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2376 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002377 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002378 self.assertRaises(TypeError, t1.dst, "no args")
2379
2380 self.assertEqual(hash(t1), hash(t2))
2381 self.assertEqual(hash(t1), hash(t3))
2382 self.assertEqual(hash(t2), hash(t3))
2383
2384 self.assertEqual(t1, t2)
2385 self.assertEqual(t1, t3)
2386 self.assertEqual(t2, t3)
2387 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2388 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2389 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2390
2391 self.assertEqual(str(t1), "07:47:00-05:00")
2392 self.assertEqual(str(t2), "12:47:00+00:00")
2393 self.assertEqual(str(t3), "13:47:00+01:00")
2394 self.assertEqual(str(t4), "00:00:00.000040")
2395 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2396
2397 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2398 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2399 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2400 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2401 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2402
Tim Peters0bf60bd2003-01-08 20:40:01 +00002403 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002404 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2405 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2406 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2407 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2408 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2409
2410 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2411 "07:47:00 %Z=EST %z=-0500")
2412 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2413 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2414
2415 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002416 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002417 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2418 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2419
Tim Petersb92bb712002-12-21 17:44:07 +00002420 # Check that an invalid tzname result raises an exception.
2421 class Badtzname(tzinfo):
2422 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002423 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002424 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2425 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002426
2427 def test_hash_edge_cases(self):
2428 # Offsets that overflow a basic time.
2429 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2430 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2431 self.assertEqual(hash(t1), hash(t2))
2432
2433 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2434 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2435 self.assertEqual(hash(t1), hash(t2))
2436
Tim Peters2a799bf2002-12-16 20:18:38 +00002437 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002438 # Try one without a tzinfo.
2439 args = 20, 59, 16, 64**2
2440 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002441 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002442 green = pickler.dumps(orig, proto)
2443 derived = unpickler.loads(green)
2444 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002445
2446 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002447 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002448 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002449 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002450 green = pickler.dumps(orig, proto)
2451 derived = unpickler.loads(green)
2452 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002453 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002454 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2455 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002456
2457 def test_more_bool(self):
2458 # Test cases with non-None tzinfo.
2459 cls = self.theclass
2460
2461 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002462 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002463
2464 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002465 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002466
2467 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002468 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002469
2470 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002471 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002472
2473 # Mostly ensuring this doesn't overflow internally.
2474 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002475 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002476
2477 # But this should yield a value error -- the utcoffset is bogus.
2478 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2479 self.assertRaises(ValueError, lambda: bool(t))
2480
2481 # Likewise.
2482 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2483 self.assertRaises(ValueError, lambda: bool(t))
2484
Tim Peters12bf3392002-12-24 05:41:27 +00002485 def test_replace(self):
2486 cls = self.theclass
2487 z100 = FixedOffset(100, "+100")
2488 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2489 args = [1, 2, 3, 4, z100]
2490 base = cls(*args)
2491 self.assertEqual(base, base.replace())
2492
2493 i = 0
2494 for name, newval in (("hour", 5),
2495 ("minute", 6),
2496 ("second", 7),
2497 ("microsecond", 8),
2498 ("tzinfo", zm200)):
2499 newargs = args[:]
2500 newargs[i] = newval
2501 expected = cls(*newargs)
2502 got = base.replace(**{name: newval})
2503 self.assertEqual(expected, got)
2504 i += 1
2505
2506 # Ensure we can get rid of a tzinfo.
2507 self.assertEqual(base.tzname(), "+100")
2508 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002509 self.assertTrue(base2.tzinfo is None)
2510 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002511
2512 # Ensure we can add one.
2513 base3 = base2.replace(tzinfo=z100)
2514 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002515 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002516
2517 # Out of bounds.
2518 base = cls(1)
2519 self.assertRaises(ValueError, base.replace, hour=24)
2520 self.assertRaises(ValueError, base.replace, minute=-1)
2521 self.assertRaises(ValueError, base.replace, second=100)
2522 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2523
Tim Peters60c76e42002-12-27 00:41:11 +00002524 def test_mixed_compare(self):
2525 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002526 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002527 self.assertEqual(t1, t2)
2528 t2 = t2.replace(tzinfo=None)
2529 self.assertEqual(t1, t2)
2530 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2531 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002532 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2533 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002534
Tim Peters0bf60bd2003-01-08 20:40:01 +00002535 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002536 class Varies(tzinfo):
2537 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002538 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002539 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002540 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002541 return self.offset
2542
2543 v = Varies()
2544 t1 = t2.replace(tzinfo=v)
2545 t2 = t2.replace(tzinfo=v)
2546 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2547 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2548 self.assertEqual(t1, t2)
2549
2550 # But if they're not identical, it isn't ignored.
2551 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002552 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002553
Tim Petersa98924a2003-05-17 05:55:19 +00002554 def test_subclass_timetz(self):
2555
2556 class C(self.theclass):
2557 theAnswer = 42
2558
2559 def __new__(cls, *args, **kws):
2560 temp = kws.copy()
2561 extra = temp.pop('extra')
2562 result = self.theclass.__new__(cls, *args, **temp)
2563 result.extra = extra
2564 return result
2565
2566 def newmeth(self, start):
2567 return start + self.hour + self.second
2568
2569 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2570
2571 dt1 = self.theclass(*args)
2572 dt2 = C(*args, **{'extra': 7})
2573
2574 self.assertEqual(dt2.__class__, C)
2575 self.assertEqual(dt2.theAnswer, 42)
2576 self.assertEqual(dt2.extra, 7)
2577 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2578 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2579
Tim Peters4c0db782002-12-26 05:01:19 +00002580
Tim Peters0bf60bd2003-01-08 20:40:01 +00002581# Testing datetime objects with a non-None tzinfo.
2582
Guido van Rossumd8faa362007-04-27 19:54:29 +00002583class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002584 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002585
2586 def test_trivial(self):
2587 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2588 self.assertEqual(dt.year, 1)
2589 self.assertEqual(dt.month, 2)
2590 self.assertEqual(dt.day, 3)
2591 self.assertEqual(dt.hour, 4)
2592 self.assertEqual(dt.minute, 5)
2593 self.assertEqual(dt.second, 6)
2594 self.assertEqual(dt.microsecond, 7)
2595 self.assertEqual(dt.tzinfo, None)
2596
2597 def test_even_more_compare(self):
2598 # The test_compare() and test_more_compare() inherited from TestDate
2599 # and TestDateTime covered non-tzinfo cases.
2600
2601 # Smallest possible after UTC adjustment.
2602 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2603 # Largest possible after UTC adjustment.
2604 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2605 tzinfo=FixedOffset(-1439, ""))
2606
2607 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002608 self.assertTrue(t1 < t2)
2609 self.assertTrue(t1 != t2)
2610 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002611
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002612 self.assertEqual(t1, t1)
2613 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002614
2615 # Equal afer adjustment.
2616 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2617 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2618 self.assertEqual(t1, t2)
2619
2620 # Change t1 not to subtract a minute, and t1 should be larger.
2621 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002622 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002623
2624 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2625 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002626 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002627
2628 # Back to the original t1, but make seconds resolve it.
2629 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2630 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002631 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002632
2633 # Likewise, but make microseconds resolve it.
2634 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2635 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002636 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002637
2638 # Make t2 naive and it should fail.
2639 t2 = self.theclass.min
2640 self.assertRaises(TypeError, lambda: t1 == t2)
2641 self.assertEqual(t2, t2)
2642
2643 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2644 class Naive(tzinfo):
2645 def utcoffset(self, dt): return None
2646 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2647 self.assertRaises(TypeError, lambda: t1 == t2)
2648 self.assertEqual(t2, t2)
2649
2650 # OTOH, it's OK to compare two of these mixing the two ways of being
2651 # naive.
2652 t1 = self.theclass(5, 6, 7)
2653 self.assertEqual(t1, t2)
2654
2655 # Try a bogus uctoffset.
2656 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002657 def utcoffset(self, dt):
2658 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002659 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2660 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002661 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002662
Tim Peters2a799bf2002-12-16 20:18:38 +00002663 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002664 # Try one without a tzinfo.
2665 args = 6, 7, 23, 20, 59, 1, 64**2
2666 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002667 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002668 green = pickler.dumps(orig, proto)
2669 derived = unpickler.loads(green)
2670 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002671
2672 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002673 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002674 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002675 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002676 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002677 green = pickler.dumps(orig, proto)
2678 derived = unpickler.loads(green)
2679 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002680 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002681 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2682 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002683
2684 def test_extreme_hashes(self):
2685 # If an attempt is made to hash these via subtracting the offset
2686 # then hashing a datetime object, OverflowError results. The
2687 # Python implementation used to blow up here.
2688 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2689 hash(t)
2690 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2691 tzinfo=FixedOffset(-1439, ""))
2692 hash(t)
2693
2694 # OTOH, an OOB offset should blow up.
2695 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2696 self.assertRaises(ValueError, hash, t)
2697
2698 def test_zones(self):
2699 est = FixedOffset(-300, "EST")
2700 utc = FixedOffset(0, "UTC")
2701 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002702 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2703 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2704 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002705 self.assertEqual(t1.tzinfo, est)
2706 self.assertEqual(t2.tzinfo, utc)
2707 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002708 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2709 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2710 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002711 self.assertEqual(t1.tzname(), "EST")
2712 self.assertEqual(t2.tzname(), "UTC")
2713 self.assertEqual(t3.tzname(), "MET")
2714 self.assertEqual(hash(t1), hash(t2))
2715 self.assertEqual(hash(t1), hash(t3))
2716 self.assertEqual(hash(t2), hash(t3))
2717 self.assertEqual(t1, t2)
2718 self.assertEqual(t1, t3)
2719 self.assertEqual(t2, t3)
2720 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2721 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2722 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002723 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002724 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2725 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2726 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2727
2728 def test_combine(self):
2729 met = FixedOffset(60, "MET")
2730 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002731 tz = time(18, 45, 3, 1234, tzinfo=met)
2732 dt = datetime.combine(d, tz)
2733 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002734 tzinfo=met))
2735
2736 def test_extract(self):
2737 met = FixedOffset(60, "MET")
2738 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2739 self.assertEqual(dt.date(), date(2002, 3, 4))
2740 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002741 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002742
2743 def test_tz_aware_arithmetic(self):
2744 import random
2745
2746 now = self.theclass.now()
2747 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002748 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002749 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002750 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002751 self.assertEqual(nowaware.timetz(), timeaware)
2752
2753 # Can't mix aware and non-aware.
2754 self.assertRaises(TypeError, lambda: now - nowaware)
2755 self.assertRaises(TypeError, lambda: nowaware - now)
2756
Tim Peters0bf60bd2003-01-08 20:40:01 +00002757 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002758 self.assertRaises(TypeError, lambda: now + nowaware)
2759 self.assertRaises(TypeError, lambda: nowaware + now)
2760 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2761
2762 # Subtracting should yield 0.
2763 self.assertEqual(now - now, timedelta(0))
2764 self.assertEqual(nowaware - nowaware, timedelta(0))
2765
2766 # Adding a delta should preserve tzinfo.
2767 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2768 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002769 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002770 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002771 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002772 self.assertEqual(nowawareplus, nowawareplus2)
2773
2774 # that - delta should be what we started with, and that - what we
2775 # started with should be delta.
2776 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002777 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002778 self.assertEqual(nowaware, diff)
2779 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2780 self.assertEqual(nowawareplus - nowaware, delta)
2781
2782 # Make up a random timezone.
2783 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002784 # Attach it to nowawareplus.
2785 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002786 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002787 # Make sure the difference takes the timezone adjustments into account.
2788 got = nowaware - nowawareplus
2789 # Expected: (nowaware base - nowaware offset) -
2790 # (nowawareplus base - nowawareplus offset) =
2791 # (nowaware base - nowawareplus base) +
2792 # (nowawareplus offset - nowaware offset) =
2793 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002794 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002795 self.assertEqual(got, expected)
2796
2797 # Try max possible difference.
2798 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2799 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2800 tzinfo=FixedOffset(-1439, "max"))
2801 maxdiff = max - min
2802 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2803 timedelta(minutes=2*1439))
2804
2805 def test_tzinfo_now(self):
2806 meth = self.theclass.now
2807 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2808 base = meth()
2809 # Try with and without naming the keyword.
2810 off42 = FixedOffset(42, "42")
2811 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002812 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002813 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002814 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002815 # Bad argument with and w/o naming the keyword.
2816 self.assertRaises(TypeError, meth, 16)
2817 self.assertRaises(TypeError, meth, tzinfo=16)
2818 # Bad keyword name.
2819 self.assertRaises(TypeError, meth, tinfo=off42)
2820 # Too many args.
2821 self.assertRaises(TypeError, meth, off42, off42)
2822
Tim Peters10cadce2003-01-23 19:58:02 +00002823 # We don't know which time zone we're in, and don't have a tzinfo
2824 # class to represent it, so seeing whether a tz argument actually
2825 # does a conversion is tricky.
Tim Peters10cadce2003-01-23 19:58:02 +00002826 utc = FixedOffset(0, "utc", 0)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +00002827 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
2828 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
2829 for dummy in range(3):
2830 now = datetime.now(weirdtz)
2831 self.assertTrue(now.tzinfo is weirdtz)
2832 utcnow = datetime.utcnow().replace(tzinfo=utc)
2833 now2 = utcnow.astimezone(weirdtz)
2834 if abs(now - now2) < timedelta(seconds=30):
2835 break
2836 # Else the code is broken, or more than 30 seconds passed between
2837 # calls; assuming the latter, just try again.
2838 else:
2839 # Three strikes and we're out.
2840 self.fail("utcnow(), now(tz), or astimezone() may be broken")
Tim Peters10cadce2003-01-23 19:58:02 +00002841
Tim Peters2a799bf2002-12-16 20:18:38 +00002842 def test_tzinfo_fromtimestamp(self):
2843 import time
2844 meth = self.theclass.fromtimestamp
2845 ts = time.time()
2846 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2847 base = meth(ts)
2848 # Try with and without naming the keyword.
2849 off42 = FixedOffset(42, "42")
2850 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002851 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002852 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002853 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002854 # Bad argument with and w/o naming the keyword.
2855 self.assertRaises(TypeError, meth, ts, 16)
2856 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2857 # Bad keyword name.
2858 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2859 # Too many args.
2860 self.assertRaises(TypeError, meth, ts, off42, off42)
2861 # Too few args.
2862 self.assertRaises(TypeError, meth)
2863
Tim Peters2a44a8d2003-01-23 20:53:10 +00002864 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002865 timestamp = 1000000000
2866 utcdatetime = datetime.utcfromtimestamp(timestamp)
2867 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2868 # But on some flavor of Mac, it's nowhere near that. So we can't have
2869 # any idea here what time that actually is, we can only test that
2870 # relative changes match.
2871 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2872 tz = FixedOffset(utcoffset, "tz", 0)
2873 expected = utcdatetime + utcoffset
2874 got = datetime.fromtimestamp(timestamp, tz)
2875 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002876
Tim Peters2a799bf2002-12-16 20:18:38 +00002877 def test_tzinfo_utcnow(self):
2878 meth = self.theclass.utcnow
2879 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2880 base = meth()
2881 # Try with and without naming the keyword; for whatever reason,
2882 # utcnow() doesn't accept a tzinfo argument.
2883 off42 = FixedOffset(42, "42")
2884 self.assertRaises(TypeError, meth, off42)
2885 self.assertRaises(TypeError, meth, tzinfo=off42)
2886
2887 def test_tzinfo_utcfromtimestamp(self):
2888 import time
2889 meth = self.theclass.utcfromtimestamp
2890 ts = time.time()
2891 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2892 base = meth(ts)
2893 # Try with and without naming the keyword; for whatever reason,
2894 # utcfromtimestamp() doesn't accept a tzinfo argument.
2895 off42 = FixedOffset(42, "42")
2896 self.assertRaises(TypeError, meth, ts, off42)
2897 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2898
2899 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002900 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002901 # DST flag.
2902 class DST(tzinfo):
2903 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002904 if isinstance(dstvalue, int):
2905 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002906 self.dstvalue = dstvalue
2907 def dst(self, dt):
2908 return self.dstvalue
2909
2910 cls = self.theclass
2911 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2912 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2913 t = d.timetuple()
2914 self.assertEqual(1, t.tm_year)
2915 self.assertEqual(1, t.tm_mon)
2916 self.assertEqual(1, t.tm_mday)
2917 self.assertEqual(10, t.tm_hour)
2918 self.assertEqual(20, t.tm_min)
2919 self.assertEqual(30, t.tm_sec)
2920 self.assertEqual(0, t.tm_wday)
2921 self.assertEqual(1, t.tm_yday)
2922 self.assertEqual(flag, t.tm_isdst)
2923
2924 # dst() returns wrong type.
2925 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2926
2927 # dst() at the edge.
2928 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2929 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2930
2931 # dst() out of range.
2932 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2933 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2934
2935 def test_utctimetuple(self):
2936 class DST(tzinfo):
2937 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002938 if isinstance(dstvalue, int):
2939 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002940 self.dstvalue = dstvalue
2941 def dst(self, dt):
2942 return self.dstvalue
2943
2944 cls = self.theclass
2945 # This can't work: DST didn't implement utcoffset.
2946 self.assertRaises(NotImplementedError,
2947 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2948
2949 class UOFS(DST):
2950 def __init__(self, uofs, dofs=None):
2951 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002952 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002953 def utcoffset(self, dt):
2954 return self.uofs
2955
2956 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2957 # in effect for a UTC time.
2958 for dstvalue in -33, 33, 0, None:
2959 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2960 t = d.utctimetuple()
2961 self.assertEqual(d.year, t.tm_year)
2962 self.assertEqual(d.month, t.tm_mon)
2963 self.assertEqual(d.day, t.tm_mday)
2964 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2965 self.assertEqual(13, t.tm_min)
2966 self.assertEqual(d.second, t.tm_sec)
2967 self.assertEqual(d.weekday(), t.tm_wday)
2968 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
2969 t.tm_yday)
2970 self.assertEqual(0, t.tm_isdst)
2971
2972 # At the edges, UTC adjustment can normalize into years out-of-range
2973 # for a datetime object. Ensure that a correct timetuple is
2974 # created anyway.
2975 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
2976 # That goes back 1 minute less than a full day.
2977 t = tiny.utctimetuple()
2978 self.assertEqual(t.tm_year, MINYEAR-1)
2979 self.assertEqual(t.tm_mon, 12)
2980 self.assertEqual(t.tm_mday, 31)
2981 self.assertEqual(t.tm_hour, 0)
2982 self.assertEqual(t.tm_min, 1)
2983 self.assertEqual(t.tm_sec, 37)
2984 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
2985 self.assertEqual(t.tm_isdst, 0)
2986
2987 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
2988 # That goes forward 1 minute less than a full day.
2989 t = huge.utctimetuple()
2990 self.assertEqual(t.tm_year, MAXYEAR+1)
2991 self.assertEqual(t.tm_mon, 1)
2992 self.assertEqual(t.tm_mday, 1)
2993 self.assertEqual(t.tm_hour, 23)
2994 self.assertEqual(t.tm_min, 58)
2995 self.assertEqual(t.tm_sec, 37)
2996 self.assertEqual(t.tm_yday, 1)
2997 self.assertEqual(t.tm_isdst, 0)
2998
2999 def test_tzinfo_isoformat(self):
3000 zero = FixedOffset(0, "+00:00")
3001 plus = FixedOffset(220, "+03:40")
3002 minus = FixedOffset(-231, "-03:51")
3003 unknown = FixedOffset(None, "")
3004
3005 cls = self.theclass
3006 datestr = '0001-02-03'
3007 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00003008 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00003009 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3010 timestr = '04:05:59' + (us and '.987001' or '')
3011 ofsstr = ofs is not None and d.tzname() or ''
3012 tailstr = timestr + ofsstr
3013 iso = d.isoformat()
3014 self.assertEqual(iso, datestr + 'T' + tailstr)
3015 self.assertEqual(iso, d.isoformat('T'))
3016 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00003017 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003018 self.assertEqual(str(d), datestr + ' ' + tailstr)
3019
Tim Peters12bf3392002-12-24 05:41:27 +00003020 def test_replace(self):
3021 cls = self.theclass
3022 z100 = FixedOffset(100, "+100")
3023 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3024 args = [1, 2, 3, 4, 5, 6, 7, z100]
3025 base = cls(*args)
3026 self.assertEqual(base, base.replace())
3027
3028 i = 0
3029 for name, newval in (("year", 2),
3030 ("month", 3),
3031 ("day", 4),
3032 ("hour", 5),
3033 ("minute", 6),
3034 ("second", 7),
3035 ("microsecond", 8),
3036 ("tzinfo", zm200)):
3037 newargs = args[:]
3038 newargs[i] = newval
3039 expected = cls(*newargs)
3040 got = base.replace(**{name: newval})
3041 self.assertEqual(expected, got)
3042 i += 1
3043
3044 # Ensure we can get rid of a tzinfo.
3045 self.assertEqual(base.tzname(), "+100")
3046 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003047 self.assertTrue(base2.tzinfo is None)
3048 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00003049
3050 # Ensure we can add one.
3051 base3 = base2.replace(tzinfo=z100)
3052 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003053 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00003054
3055 # Out of bounds.
3056 base = cls(2000, 2, 29)
3057 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00003058
Tim Peters80475bb2002-12-25 07:40:55 +00003059 def test_more_astimezone(self):
3060 # The inherited test_astimezone covered some trivial and error cases.
3061 fnone = FixedOffset(None, "None")
3062 f44m = FixedOffset(44, "44")
3063 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3064
Tim Peters10cadce2003-01-23 19:58:02 +00003065 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003066 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00003067 # Replacing with degenerate tzinfo raises an exception.
3068 self.assertRaises(ValueError, dt.astimezone, fnone)
3069 # Ditto with None tz.
3070 self.assertRaises(TypeError, dt.astimezone, None)
3071 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00003072 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003073 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00003074 self.assertEqual(x.date(), dt.date())
3075 self.assertEqual(x.time(), dt.time())
3076
3077 # Replacing with different tzinfo does adjust.
3078 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003079 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00003080 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3081 expected = dt - dt.utcoffset() # in effect, convert to UTC
3082 expected += fm5h.utcoffset(dt) # and from there to local time
3083 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3084 self.assertEqual(got.date(), expected.date())
3085 self.assertEqual(got.time(), expected.time())
3086 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003087 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00003088 self.assertEqual(got, expected)
3089
Tim Peters4c0db782002-12-26 05:01:19 +00003090 def test_aware_subtract(self):
3091 cls = self.theclass
3092
Tim Peters60c76e42002-12-27 00:41:11 +00003093 # Ensure that utcoffset() is ignored when the operands have the
3094 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00003095 class OperandDependentOffset(tzinfo):
3096 def utcoffset(self, t):
3097 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00003098 # d0 and d1 equal after adjustment
3099 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00003100 else:
Tim Peters397301e2003-01-02 21:28:08 +00003101 # d2 off in the weeds
3102 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00003103
3104 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3105 d0 = base.replace(minute=3)
3106 d1 = base.replace(minute=9)
3107 d2 = base.replace(minute=11)
3108 for x in d0, d1, d2:
3109 for y in d0, d1, d2:
3110 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00003111 expected = timedelta(minutes=x.minute - y.minute)
3112 self.assertEqual(got, expected)
3113
3114 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3115 # ignored.
3116 base = cls(8, 9, 10, 11, 12, 13, 14)
3117 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3118 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3119 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3120 for x in d0, d1, d2:
3121 for y in d0, d1, d2:
3122 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00003123 if (x is d0 or x is d1) and (y is d0 or y is d1):
3124 expected = timedelta(0)
3125 elif x is y is d2:
3126 expected = timedelta(0)
3127 elif x is d2:
3128 expected = timedelta(minutes=(11-59)-0)
3129 else:
3130 assert y is d2
3131 expected = timedelta(minutes=0-(11-59))
3132 self.assertEqual(got, expected)
3133
Tim Peters60c76e42002-12-27 00:41:11 +00003134 def test_mixed_compare(self):
3135 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00003136 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00003137 self.assertEqual(t1, t2)
3138 t2 = t2.replace(tzinfo=None)
3139 self.assertEqual(t1, t2)
3140 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3141 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003142 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3143 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003144
Tim Peters0bf60bd2003-01-08 20:40:01 +00003145 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003146 class Varies(tzinfo):
3147 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003148 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003149 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003150 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003151 return self.offset
3152
3153 v = Varies()
3154 t1 = t2.replace(tzinfo=v)
3155 t2 = t2.replace(tzinfo=v)
3156 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3157 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3158 self.assertEqual(t1, t2)
3159
3160 # But if they're not identical, it isn't ignored.
3161 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003162 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003163
Tim Petersa98924a2003-05-17 05:55:19 +00003164 def test_subclass_datetimetz(self):
3165
3166 class C(self.theclass):
3167 theAnswer = 42
3168
3169 def __new__(cls, *args, **kws):
3170 temp = kws.copy()
3171 extra = temp.pop('extra')
3172 result = self.theclass.__new__(cls, *args, **temp)
3173 result.extra = extra
3174 return result
3175
3176 def newmeth(self, start):
3177 return start + self.hour + self.year
3178
3179 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3180
3181 dt1 = self.theclass(*args)
3182 dt2 = C(*args, **{'extra': 7})
3183
3184 self.assertEqual(dt2.__class__, C)
3185 self.assertEqual(dt2.theAnswer, 42)
3186 self.assertEqual(dt2.extra, 7)
3187 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3188 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3189
Tim Peters621818b2002-12-29 23:44:49 +00003190# Pain to set up DST-aware tzinfo classes.
3191
3192def first_sunday_on_or_after(dt):
3193 days_to_go = 6 - dt.weekday()
3194 if days_to_go:
3195 dt += timedelta(days_to_go)
3196 return dt
3197
3198ZERO = timedelta(0)
3199HOUR = timedelta(hours=1)
3200DAY = timedelta(days=1)
3201# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3202DSTSTART = datetime(1, 4, 1, 2)
3203# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003204# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3205# being standard time on that day, there is no spelling in local time of
3206# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3207DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003208
3209class USTimeZone(tzinfo):
3210
3211 def __init__(self, hours, reprname, stdname, dstname):
3212 self.stdoffset = timedelta(hours=hours)
3213 self.reprname = reprname
3214 self.stdname = stdname
3215 self.dstname = dstname
3216
3217 def __repr__(self):
3218 return self.reprname
3219
3220 def tzname(self, dt):
3221 if self.dst(dt):
3222 return self.dstname
3223 else:
3224 return self.stdname
3225
3226 def utcoffset(self, dt):
3227 return self.stdoffset + self.dst(dt)
3228
3229 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003230 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003231 # An exception instead may be sensible here, in one or more of
3232 # the cases.
3233 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003234 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003235
3236 # Find first Sunday in April.
3237 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3238 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3239
3240 # Find last Sunday in October.
3241 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3242 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3243
Tim Peters621818b2002-12-29 23:44:49 +00003244 # Can't compare naive to aware objects, so strip the timezone from
3245 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003246 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003247 return HOUR
3248 else:
3249 return ZERO
3250
Tim Peters521fc152002-12-31 17:36:56 +00003251Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3252Central = USTimeZone(-6, "Central", "CST", "CDT")
3253Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3254Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003255utc_real = FixedOffset(0, "UTC", 0)
3256# For better test coverage, we want another flavor of UTC that's west of
3257# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003258utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003259
3260class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003261 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003262 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003263 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003264
Tim Peters0bf60bd2003-01-08 20:40:01 +00003265 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003266
Tim Peters521fc152002-12-31 17:36:56 +00003267 # Check a time that's inside DST.
3268 def checkinside(self, dt, tz, utc, dston, dstoff):
3269 self.assertEqual(dt.dst(), HOUR)
3270
3271 # Conversion to our own timezone is always an identity.
3272 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003273
3274 asutc = dt.astimezone(utc)
3275 there_and_back = asutc.astimezone(tz)
3276
3277 # Conversion to UTC and back isn't always an identity here,
3278 # because there are redundant spellings (in local time) of
3279 # UTC time when DST begins: the clock jumps from 1:59:59
3280 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3281 # make sense then. The classes above treat 2:MM:SS as
3282 # daylight time then (it's "after 2am"), really an alias
3283 # for 1:MM:SS standard time. The latter form is what
3284 # conversion back from UTC produces.
3285 if dt.date() == dston.date() and dt.hour == 2:
3286 # We're in the redundant hour, and coming back from
3287 # UTC gives the 1:MM:SS standard-time spelling.
3288 self.assertEqual(there_and_back + HOUR, dt)
3289 # Although during was considered to be in daylight
3290 # time, there_and_back is not.
3291 self.assertEqual(there_and_back.dst(), ZERO)
3292 # They're the same times in UTC.
3293 self.assertEqual(there_and_back.astimezone(utc),
3294 dt.astimezone(utc))
3295 else:
3296 # We're not in the redundant hour.
3297 self.assertEqual(dt, there_and_back)
3298
Tim Peters327098a2003-01-20 22:54:38 +00003299 # Because we have a redundant spelling when DST begins, there is
3300 # (unforunately) an hour when DST ends that can't be spelled at all in
3301 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3302 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3303 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3304 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3305 # expressed in local time. Nevertheless, we want conversion back
3306 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003307 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003308 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003309 if dt.date() == dstoff.date() and dt.hour == 0:
3310 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003311 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003312 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3313 nexthour_utc += HOUR
3314 nexthour_tz = nexthour_utc.astimezone(tz)
3315 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003316 else:
Tim Peters327098a2003-01-20 22:54:38 +00003317 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003318
3319 # Check a time that's outside DST.
3320 def checkoutside(self, dt, tz, utc):
3321 self.assertEqual(dt.dst(), ZERO)
3322
3323 # Conversion to our own timezone is always an identity.
3324 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003325
3326 # Converting to UTC and back is an identity too.
3327 asutc = dt.astimezone(utc)
3328 there_and_back = asutc.astimezone(tz)
3329 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003330
Tim Peters1024bf82002-12-30 17:09:40 +00003331 def convert_between_tz_and_utc(self, tz, utc):
3332 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003333 # Because 1:MM on the day DST ends is taken as being standard time,
3334 # there is no spelling in tz for the last hour of daylight time.
3335 # For purposes of the test, the last hour of DST is 0:MM, which is
3336 # taken as being daylight time (and 1:MM is taken as being standard
3337 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003338 dstoff = self.dstoff.replace(tzinfo=tz)
3339 for delta in (timedelta(weeks=13),
3340 DAY,
3341 HOUR,
3342 timedelta(minutes=1),
3343 timedelta(microseconds=1)):
3344
Tim Peters521fc152002-12-31 17:36:56 +00003345 self.checkinside(dston, tz, utc, dston, dstoff)
3346 for during in dston + delta, dstoff - delta:
3347 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003348
Tim Peters521fc152002-12-31 17:36:56 +00003349 self.checkoutside(dstoff, tz, utc)
3350 for outside in dston - delta, dstoff + delta:
3351 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003352
Tim Peters621818b2002-12-29 23:44:49 +00003353 def test_easy(self):
3354 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003355 self.convert_between_tz_and_utc(Eastern, utc_real)
3356 self.convert_between_tz_and_utc(Pacific, utc_real)
3357 self.convert_between_tz_and_utc(Eastern, utc_fake)
3358 self.convert_between_tz_and_utc(Pacific, utc_fake)
3359 # The next is really dancing near the edge. It works because
3360 # Pacific and Eastern are far enough apart that their "problem
3361 # hours" don't overlap.
3362 self.convert_between_tz_and_utc(Eastern, Pacific)
3363 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003364 # OTOH, these fail! Don't enable them. The difficulty is that
3365 # the edge case tests assume that every hour is representable in
3366 # the "utc" class. This is always true for a fixed-offset tzinfo
3367 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3368 # For these adjacent DST-aware time zones, the range of time offsets
3369 # tested ends up creating hours in the one that aren't representable
3370 # in the other. For the same reason, we would see failures in the
3371 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3372 # offset deltas in convert_between_tz_and_utc().
3373 #
3374 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3375 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003376
Tim Petersf3615152003-01-01 21:51:37 +00003377 def test_tricky(self):
3378 # 22:00 on day before daylight starts.
3379 fourback = self.dston - timedelta(hours=4)
3380 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003381 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003382 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3383 # 2", we should get the 3 spelling.
3384 # If we plug 22:00 the day before into Eastern, it "looks like std
3385 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3386 # to 22:00 lands on 2:00, which makes no sense in local time (the
3387 # local clock jumps from 1 to 3). The point here is to make sure we
3388 # get the 3 spelling.
3389 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003390 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003391 self.assertEqual(expected, got)
3392
3393 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3394 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003395 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003396 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3397 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3398 # spelling.
3399 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003400 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003401 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003402
Tim Petersadf64202003-01-04 06:03:15 +00003403 # Now on the day DST ends, we want "repeat an hour" behavior.
3404 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3405 # EST 23:MM 0:MM 1:MM 2:MM
3406 # EDT 0:MM 1:MM 2:MM 3:MM
3407 # wall 0:MM 1:MM 1:MM 2:MM against these
3408 for utc in utc_real, utc_fake:
3409 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003410 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003411 # Convert that to UTC.
3412 first_std_hour -= tz.utcoffset(None)
3413 # Adjust for possibly fake UTC.
3414 asutc = first_std_hour + utc.utcoffset(None)
3415 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3416 # tz=Eastern.
3417 asutcbase = asutc.replace(tzinfo=utc)
3418 for tzhour in (0, 1, 1, 2):
3419 expectedbase = self.dstoff.replace(hour=tzhour)
3420 for minute in 0, 30, 59:
3421 expected = expectedbase.replace(minute=minute)
3422 asutc = asutcbase.replace(minute=minute)
3423 astz = asutc.astimezone(tz)
3424 self.assertEqual(astz.replace(tzinfo=None), expected)
3425 asutcbase += HOUR
3426
3427
Tim Peters710fb152003-01-02 19:35:54 +00003428 def test_bogus_dst(self):
3429 class ok(tzinfo):
3430 def utcoffset(self, dt): return HOUR
3431 def dst(self, dt): return HOUR
3432
3433 now = self.theclass.now().replace(tzinfo=utc_real)
3434 # Doesn't blow up.
3435 now.astimezone(ok())
3436
3437 # Does blow up.
3438 class notok(ok):
3439 def dst(self, dt): return None
3440 self.assertRaises(ValueError, now.astimezone, notok())
3441
Tim Peters52dcce22003-01-23 16:36:11 +00003442 def test_fromutc(self):
3443 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3444 now = datetime.utcnow().replace(tzinfo=utc_real)
3445 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3446 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3447 enow = Eastern.fromutc(now) # doesn't blow up
3448 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3449 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3450 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3451
3452 # Always converts UTC to standard time.
3453 class FauxUSTimeZone(USTimeZone):
3454 def fromutc(self, dt):
3455 return dt + self.stdoffset
3456 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3457
3458 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3459 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3460 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3461
3462 # Check around DST start.
3463 start = self.dston.replace(hour=4, tzinfo=Eastern)
3464 fstart = start.replace(tzinfo=FEastern)
3465 for wall in 23, 0, 1, 3, 4, 5:
3466 expected = start.replace(hour=wall)
3467 if wall == 23:
3468 expected -= timedelta(days=1)
3469 got = Eastern.fromutc(start)
3470 self.assertEqual(expected, got)
3471
3472 expected = fstart + FEastern.stdoffset
3473 got = FEastern.fromutc(fstart)
3474 self.assertEqual(expected, got)
3475
3476 # Ensure astimezone() calls fromutc() too.
3477 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3478 self.assertEqual(expected, got)
3479
3480 start += HOUR
3481 fstart += HOUR
3482
3483 # Check around DST end.
3484 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3485 fstart = start.replace(tzinfo=FEastern)
3486 for wall in 0, 1, 1, 2, 3, 4:
3487 expected = start.replace(hour=wall)
3488 got = Eastern.fromutc(start)
3489 self.assertEqual(expected, got)
3490
3491 expected = fstart + FEastern.stdoffset
3492 got = FEastern.fromutc(fstart)
3493 self.assertEqual(expected, got)
3494
3495 # Ensure astimezone() calls fromutc() too.
3496 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3497 self.assertEqual(expected, got)
3498
3499 start += HOUR
3500 fstart += HOUR
3501
Tim Peters710fb152003-01-02 19:35:54 +00003502
Tim Peters528ca532004-09-16 01:30:50 +00003503#############################################################################
3504# oddballs
3505
3506class Oddballs(unittest.TestCase):
3507
3508 def test_bug_1028306(self):
3509 # Trying to compare a date to a datetime should act like a mixed-
3510 # type comparison, despite that datetime is a subclass of date.
3511 as_date = date.today()
3512 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003513 self.assertTrue(as_date != as_datetime)
3514 self.assertTrue(as_datetime != as_date)
3515 self.assertTrue(not as_date == as_datetime)
3516 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003517 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3518 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3519 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3520 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3521 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3522 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3523 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3524 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3525
3526 # Neverthelss, comparison should work with the base-class (date)
3527 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003528 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003529 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003530 as_different = as_datetime.replace(day= different_day)
3531 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003532
3533 # And date should compare with other subclasses of date. If a
3534 # subclass wants to stop this, it's up to the subclass to do so.
3535 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3536 self.assertEqual(as_date, date_sc)
3537 self.assertEqual(date_sc, as_date)
3538
3539 # Ditto for datetimes.
3540 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3541 as_date.day, 0, 0, 0)
3542 self.assertEqual(as_datetime, datetime_sc)
3543 self.assertEqual(datetime_sc, as_datetime)
3544
Tim Peters2a799bf2002-12-16 20:18:38 +00003545def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003546 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003547
3548if __name__ == "__main__":
3549 test_main()