blob: b320e1f80292b81c082952dcd81f13d66a984f9e [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
Alexander Belopolskyca94f552010-06-17 18:30:34 +000020import time as _time
Tim Peters0bf60bd2003-01-08 20:40:01 +000021
Guido van Rossumbe6fe542007-07-19 23:55:34 +000022pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
23assert len(pickle_choices) == 3
Guido van Rossum177e41a2003-01-30 22:06:23 +000024
Tim Peters68124bb2003-02-08 03:46:31 +000025# An arbitrary collection of objects of non-datetime types, for testing
26# mixed-type comparisons.
Mark Dickinson5c2db372009-12-05 20:28:34 +000027OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000028
Tim Peters2a799bf2002-12-16 20:18:38 +000029
Alexander Belopolsky1790bc42010-05-31 17:33:47 +000030# XXX Copied from test_float.
31INF = float("inf")
32NAN = float("nan")
33
34# decorator for skipping tests on non-IEEE 754 platforms
35requires_IEEE_754 = unittest.skipUnless(
36 float.__getformat__("double").startswith("IEEE"),
37 "test requires IEEE 754 doubles")
38
39
Tim Peters2a799bf2002-12-16 20:18:38 +000040#############################################################################
41# module tests
42
43class TestModule(unittest.TestCase):
44
45 def test_constants(self):
46 import datetime
47 self.assertEqual(datetime.MINYEAR, 1)
48 self.assertEqual(datetime.MAXYEAR, 9999)
49
50#############################################################################
51# tzinfo tests
52
53class FixedOffset(tzinfo):
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000054
Tim Peters2a799bf2002-12-16 20:18:38 +000055 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000056 if isinstance(offset, int):
57 offset = timedelta(minutes=offset)
58 if isinstance(dstoffset, int):
59 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000060 self.__offset = offset
61 self.__name = name
62 self.__dstoffset = dstoffset
63 def __repr__(self):
64 return self.__name.lower()
65 def utcoffset(self, dt):
66 return self.__offset
67 def tzname(self, dt):
68 return self.__name
69 def dst(self, dt):
70 return self.__dstoffset
71
Tim Petersfb8472c2002-12-21 05:04:42 +000072class PicklableFixedOffset(FixedOffset):
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000073
Tim Petersfb8472c2002-12-21 05:04:42 +000074 def __init__(self, offset=None, name=None, dstoffset=None):
75 FixedOffset.__init__(self, offset, name, dstoffset)
76
Tim Peters2a799bf2002-12-16 20:18:38 +000077class TestTZInfo(unittest.TestCase):
78
79 def test_non_abstractness(self):
80 # In order to allow subclasses to get pickled, the C implementation
81 # wasn't able to get away with having __init__ raise
82 # NotImplementedError.
83 useless = tzinfo()
84 dt = datetime.max
85 self.assertRaises(NotImplementedError, useless.tzname, dt)
86 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
87 self.assertRaises(NotImplementedError, useless.dst, dt)
88
89 def test_subclass_must_override(self):
90 class NotEnough(tzinfo):
91 def __init__(self, offset, name):
92 self.__offset = offset
93 self.__name = name
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000094 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000095 ne = NotEnough(3, "NotByALongShot")
Ezio Melottie9615932010-01-24 19:26:24 +000096 self.assertIsInstance(ne, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000097
98 dt = datetime.now()
99 self.assertRaises(NotImplementedError, ne.tzname, dt)
100 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
101 self.assertRaises(NotImplementedError, ne.dst, dt)
102
103 def test_normal(self):
104 fo = FixedOffset(3, "Three")
Ezio Melottie9615932010-01-24 19:26:24 +0000105 self.assertIsInstance(fo, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000106 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000107 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000108 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000109 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000110
111 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000112 # There's no point to pickling tzinfo objects on their own (they
113 # carry no data), but they need to be picklable anyway else
114 # concrete subclasses can't be pickled.
115 orig = tzinfo.__new__(tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000116 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000117 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000118 green = pickler.dumps(orig, proto)
119 derived = unpickler.loads(green)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000120 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000121
122 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000123 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000124 offset = timedelta(minutes=-300)
125 orig = PicklableFixedOffset(offset, 'cookie')
Ezio Melottie9615932010-01-24 19:26:24 +0000126 self.assertIsInstance(orig, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000127 self.assertTrue(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000128 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000129 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000130 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000131 green = pickler.dumps(orig, proto)
132 derived = unpickler.loads(green)
Ezio Melottie9615932010-01-24 19:26:24 +0000133 self.assertIsInstance(derived, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000134 self.assertTrue(type(derived) is PicklableFixedOffset)
Tim Petersf2715e02003-02-19 02:35:07 +0000135 self.assertEqual(derived.utcoffset(None), offset)
136 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000137
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000138class TestTimeZone(unittest.TestCase):
139
140 def setUp(self):
141 self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
142 self.EST = timezone(-timedelta(hours=5), 'EST')
143 self.DT = datetime(2010, 1, 1)
144
145 def test_str(self):
146 for tz in [self.ACDT, self.EST, timezone.utc,
147 timezone.min, timezone.max]:
148 self.assertEqual(str(tz), tz.tzname(None))
149
150 def test_class_members(self):
151 limit = timedelta(hours=23, minutes=59)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000152 self.assertEqual(timezone.utc.utcoffset(None), ZERO)
153 self.assertEqual(timezone.min.utcoffset(None), -limit)
154 self.assertEqual(timezone.max.utcoffset(None), limit)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000155
156
157 def test_constructor(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000158 self.assertEqual(timezone.utc, timezone(timedelta(0)))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000159 # invalid offsets
160 for invalid in [timedelta(microseconds=1), timedelta(1, 1),
161 timedelta(seconds=1), timedelta(1), -timedelta(1)]:
162 self.assertRaises(ValueError, timezone, invalid)
163 self.assertRaises(ValueError, timezone, -invalid)
164
165 with self.assertRaises(TypeError): timezone(None)
166 with self.assertRaises(TypeError): timezone(42)
167 with self.assertRaises(TypeError): timezone(ZERO, None)
168 with self.assertRaises(TypeError): timezone(ZERO, 42)
169
170 def test_inheritance(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000171 self.assertIsInstance(timezone.utc, tzinfo)
172 self.assertIsInstance(self.EST, tzinfo)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000173
174 def test_utcoffset(self):
175 dummy = self.DT
176 for h in [0, 1.5, 12]:
177 offset = h * HOUR
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000178 self.assertEqual(offset, timezone(offset).utcoffset(dummy))
179 self.assertEqual(-offset, timezone(-offset).utcoffset(dummy))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000180
181 with self.assertRaises(TypeError): self.EST.utcoffset('')
182 with self.assertRaises(TypeError): self.EST.utcoffset(5)
183
184
185 def test_dst(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000186 self.assertEqual(None, timezone.utc.dst(self.DT))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000187
188 with self.assertRaises(TypeError): self.EST.dst('')
189 with self.assertRaises(TypeError): self.EST.dst(5)
190
191 def test_tzname(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000192 self.assertEqual('UTC+00:00', timezone(ZERO).tzname(None))
193 self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None))
194 self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
195 self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
196 self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000197
198 with self.assertRaises(TypeError): self.EST.tzname('')
199 with self.assertRaises(TypeError): self.EST.tzname(5)
200
201 def test_fromutc(self):
202 with self.assertRaises(ValueError):
203 timezone.utc.fromutc(self.DT)
204 for tz in [self.EST, self.ACDT, Eastern]:
205 utctime = self.DT.replace(tzinfo=tz)
206 local = tz.fromutc(utctime)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000207 self.assertEqual(local - utctime, tz.utcoffset(local))
208 self.assertEqual(local,
209 self.DT.replace(tzinfo=timezone.utc))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000210
211 def test_comparison(self):
212 self.assertNotEqual(timezone(ZERO), timezone(HOUR))
213 self.assertEqual(timezone(HOUR), timezone(HOUR))
214 self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
215 with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
216 self.assertIn(timezone(ZERO), {timezone(ZERO)})
217
218 def test_aware_datetime(self):
219 # test that timezone instances can be used by datetime
220 t = datetime(1, 1, 1)
221 for tz in [timezone.min, timezone.max, timezone.utc]:
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000222 self.assertEqual(tz.tzname(t),
223 t.replace(tzinfo=tz).tzname())
224 self.assertEqual(tz.utcoffset(t),
225 t.replace(tzinfo=tz).utcoffset())
226 self.assertEqual(tz.dst(t),
227 t.replace(tzinfo=tz).dst())
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000228
Tim Peters2a799bf2002-12-16 20:18:38 +0000229#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000230# Base clase for testing a particular aspect of timedelta, time, date and
231# datetime comparisons.
232
Guido van Rossumd8faa362007-04-27 19:54:29 +0000233class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000234 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
235
236 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
237 # legit constructor.
238
239 def test_harmless_mixed_comparison(self):
240 me = self.theclass(1, 1, 1)
241
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000242 self.assertFalse(me == ())
243 self.assertTrue(me != ())
244 self.assertFalse(() == me)
245 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000246
Benjamin Peterson577473f2010-01-19 00:09:57 +0000247 self.assertIn(me, [1, 20, [], me])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000248 self.assertIn([], [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000249
250 def test_harmful_mixed_comparison(self):
251 me = self.theclass(1, 1, 1)
252
253 self.assertRaises(TypeError, lambda: me < ())
254 self.assertRaises(TypeError, lambda: me <= ())
255 self.assertRaises(TypeError, lambda: me > ())
256 self.assertRaises(TypeError, lambda: me >= ())
257
258 self.assertRaises(TypeError, lambda: () < me)
259 self.assertRaises(TypeError, lambda: () <= me)
260 self.assertRaises(TypeError, lambda: () > me)
261 self.assertRaises(TypeError, lambda: () >= me)
262
Tim Peters07534a62003-02-07 22:50:28 +0000263#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000264# timedelta tests
265
Guido van Rossumd8faa362007-04-27 19:54:29 +0000266class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000267
268 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000269
270 def test_constructor(self):
271 eq = self.assertEqual
272 td = timedelta
273
274 # Check keyword args to constructor
275 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
276 milliseconds=0, microseconds=0))
277 eq(td(1), td(days=1))
278 eq(td(0, 1), td(seconds=1))
279 eq(td(0, 0, 1), td(microseconds=1))
280 eq(td(weeks=1), td(days=7))
281 eq(td(days=1), td(hours=24))
282 eq(td(hours=1), td(minutes=60))
283 eq(td(minutes=1), td(seconds=60))
284 eq(td(seconds=1), td(milliseconds=1000))
285 eq(td(milliseconds=1), td(microseconds=1000))
286
287 # Check float args to constructor
288 eq(td(weeks=1.0/7), td(days=1))
289 eq(td(days=1.0/24), td(hours=1))
290 eq(td(hours=1.0/60), td(minutes=1))
291 eq(td(minutes=1.0/60), td(seconds=1))
292 eq(td(seconds=0.001), td(milliseconds=1))
293 eq(td(milliseconds=0.001), td(microseconds=1))
294
295 def test_computations(self):
296 eq = self.assertEqual
297 td = timedelta
298
299 a = td(7) # One week
300 b = td(0, 60) # One minute
301 c = td(0, 0, 1000) # One millisecond
302 eq(a+b+c, td(7, 60, 1000))
303 eq(a-b, td(6, 24*3600 - 60))
304 eq(-a, td(-7))
305 eq(+a, td(7))
306 eq(-b, td(-1, 24*3600 - 60))
307 eq(-c, td(-1, 24*3600 - 1, 999000))
308 eq(abs(a), a)
309 eq(abs(-a), a)
310 eq(td(6, 24*3600), a)
311 eq(td(0, 0, 60*1000000), b)
312 eq(a*10, td(70))
313 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000314 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000315 eq(b*10, td(0, 600))
316 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000317 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000318 eq(c*10, td(0, 0, 10000))
319 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000320 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000321 eq(a*-1, -a)
322 eq(b*-2, -b-b)
323 eq(c*-2, -c+-c)
324 eq(b*(60*24), (b*60)*24)
325 eq(b*(60*24), (60*b)*24)
326 eq(c*1000, td(0, 1))
327 eq(1000*c, td(0, 1))
328 eq(a//7, td(1))
329 eq(b//10, td(0, 6))
330 eq(c//1000, td(0, 0, 1))
331 eq(a//10, td(0, 7*24*360))
332 eq(a//3600000, td(0, 0, 7*24*1000))
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000333 eq(a/0.5, td(14))
334 eq(b/0.5, td(0, 120))
335 eq(a/7, td(1))
336 eq(b/10, td(0, 6))
337 eq(c/1000, td(0, 0, 1))
338 eq(a/10, td(0, 7*24*360))
339 eq(a/3600000, td(0, 0, 7*24*1000))
340
341 # Multiplication by float
342 us = td(microseconds=1)
343 eq((3*us) * 0.5, 2*us)
344 eq((5*us) * 0.5, 2*us)
345 eq(0.5 * (3*us), 2*us)
346 eq(0.5 * (5*us), 2*us)
347 eq((-3*us) * 0.5, -2*us)
348 eq((-5*us) * 0.5, -2*us)
349
350 # Division by int and float
351 eq((3*us) / 2, 2*us)
352 eq((5*us) / 2, 2*us)
353 eq((-3*us) / 2.0, -2*us)
354 eq((-5*us) / 2.0, -2*us)
355 eq((3*us) / -2, -2*us)
356 eq((5*us) / -2, -2*us)
357 eq((3*us) / -2.0, -2*us)
358 eq((5*us) / -2.0, -2*us)
359 for i in range(-10, 10):
360 eq((i*us/3)//us, round(i/3))
361 for i in range(-10, 10):
362 eq((i*us/-3)//us, round(i/-3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000363
364 def test_disallowed_computations(self):
365 a = timedelta(42)
366
Mark Dickinson5c2db372009-12-05 20:28:34 +0000367 # Add/sub ints or floats should be illegal
368 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000369 self.assertRaises(TypeError, lambda: a+i)
370 self.assertRaises(TypeError, lambda: a-i)
371 self.assertRaises(TypeError, lambda: i+a)
372 self.assertRaises(TypeError, lambda: i-a)
373
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000374 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000375 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000376 zero = 0
377 self.assertRaises(TypeError, lambda: zero // a)
378 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000379 self.assertRaises(ZeroDivisionError, lambda: a / zero)
380 self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
381
382 @requires_IEEE_754
383 def test_disallowed_special(self):
384 a = timedelta(42)
385 self.assertRaises(ValueError, a.__mul__, NAN)
386 self.assertRaises(ValueError, a.__truediv__, NAN)
Tim Peters2a799bf2002-12-16 20:18:38 +0000387
388 def test_basic_attributes(self):
389 days, seconds, us = 1, 7, 31
390 td = timedelta(days, seconds, us)
391 self.assertEqual(td.days, days)
392 self.assertEqual(td.seconds, seconds)
393 self.assertEqual(td.microseconds, us)
394
Antoine Pitroube6859d2009-11-25 23:02:32 +0000395 def test_total_seconds(self):
396 td = timedelta(days=365)
397 self.assertEqual(td.total_seconds(), 31536000.0)
398 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
399 td = timedelta(seconds=total_seconds)
400 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson0381e3f2010-05-08 14:35:02 +0000401 # Issue8644: Test that td.total_seconds() has the same
402 # accuracy as td / timedelta(seconds=1).
403 for ms in [-1, -2, -123]:
404 td = timedelta(microseconds=ms)
405 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
Antoine Pitroube6859d2009-11-25 23:02:32 +0000406
Tim Peters2a799bf2002-12-16 20:18:38 +0000407 def test_carries(self):
408 t1 = timedelta(days=100,
409 weeks=-7,
410 hours=-24*(100-49),
411 minutes=-3,
412 seconds=12,
413 microseconds=(3*60 - 12) * 1e6 + 1)
414 t2 = timedelta(microseconds=1)
415 self.assertEqual(t1, t2)
416
417 def test_hash_equality(self):
418 t1 = timedelta(days=100,
419 weeks=-7,
420 hours=-24*(100-49),
421 minutes=-3,
422 seconds=12,
423 microseconds=(3*60 - 12) * 1000000)
424 t2 = timedelta()
425 self.assertEqual(hash(t1), hash(t2))
426
427 t1 += timedelta(weeks=7)
428 t2 += timedelta(days=7*7)
429 self.assertEqual(t1, t2)
430 self.assertEqual(hash(t1), hash(t2))
431
432 d = {t1: 1}
433 d[t2] = 2
434 self.assertEqual(len(d), 1)
435 self.assertEqual(d[t1], 2)
436
437 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000438 args = 12, 34, 56
439 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000440 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000441 green = pickler.dumps(orig, proto)
442 derived = unpickler.loads(green)
443 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000444
445 def test_compare(self):
446 t1 = timedelta(2, 3, 4)
447 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000448 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000449 self.assertTrue(t1 <= t2)
450 self.assertTrue(t1 >= t2)
451 self.assertTrue(not t1 != t2)
452 self.assertTrue(not t1 < t2)
453 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000454
455 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
456 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000457 self.assertTrue(t1 < t2)
458 self.assertTrue(t2 > t1)
459 self.assertTrue(t1 <= t2)
460 self.assertTrue(t2 >= t1)
461 self.assertTrue(t1 != t2)
462 self.assertTrue(t2 != t1)
463 self.assertTrue(not t1 == t2)
464 self.assertTrue(not t2 == t1)
465 self.assertTrue(not t1 > t2)
466 self.assertTrue(not t2 < t1)
467 self.assertTrue(not t1 >= t2)
468 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000469
Tim Peters68124bb2003-02-08 03:46:31 +0000470 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000471 self.assertEqual(t1 == badarg, False)
472 self.assertEqual(t1 != badarg, True)
473 self.assertEqual(badarg == t1, False)
474 self.assertEqual(badarg != t1, True)
475
Tim Peters2a799bf2002-12-16 20:18:38 +0000476 self.assertRaises(TypeError, lambda: t1 <= badarg)
477 self.assertRaises(TypeError, lambda: t1 < badarg)
478 self.assertRaises(TypeError, lambda: t1 > badarg)
479 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000480 self.assertRaises(TypeError, lambda: badarg <= t1)
481 self.assertRaises(TypeError, lambda: badarg < t1)
482 self.assertRaises(TypeError, lambda: badarg > t1)
483 self.assertRaises(TypeError, lambda: badarg >= t1)
484
485 def test_str(self):
486 td = timedelta
487 eq = self.assertEqual
488
489 eq(str(td(1)), "1 day, 0:00:00")
490 eq(str(td(-1)), "-1 day, 0:00:00")
491 eq(str(td(2)), "2 days, 0:00:00")
492 eq(str(td(-2)), "-2 days, 0:00:00")
493
494 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
495 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
496 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
497 "-210 days, 23:12:34")
498
499 eq(str(td(milliseconds=1)), "0:00:00.001000")
500 eq(str(td(microseconds=3)), "0:00:00.000003")
501
502 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
503 microseconds=999999)),
504 "999999999 days, 23:59:59.999999")
505
506 def test_roundtrip(self):
507 for td in (timedelta(days=999999999, hours=23, minutes=59,
508 seconds=59, microseconds=999999),
509 timedelta(days=-999999999),
510 timedelta(days=1, seconds=2, microseconds=3)):
511
512 # Verify td -> string -> td identity.
513 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000514 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000515 s = s[9:]
516 td2 = eval(s)
517 self.assertEqual(td, td2)
518
519 # Verify identity via reconstructing from pieces.
520 td2 = timedelta(td.days, td.seconds, td.microseconds)
521 self.assertEqual(td, td2)
522
523 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000524 self.assertIsInstance(timedelta.min, timedelta)
525 self.assertIsInstance(timedelta.max, timedelta)
526 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000527 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000528 self.assertEqual(timedelta.min, timedelta(-999999999))
529 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
530 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
531
532 def test_overflow(self):
533 tiny = timedelta.resolution
534
535 td = timedelta.min + tiny
536 td -= tiny # no problem
537 self.assertRaises(OverflowError, td.__sub__, tiny)
538 self.assertRaises(OverflowError, td.__add__, -tiny)
539
540 td = timedelta.max - tiny
541 td += tiny # no problem
542 self.assertRaises(OverflowError, td.__add__, tiny)
543 self.assertRaises(OverflowError, td.__sub__, -tiny)
544
545 self.assertRaises(OverflowError, lambda: -timedelta.max)
546
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000547 day = timedelta(1)
548 self.assertRaises(OverflowError, day.__mul__, 10**9)
549 self.assertRaises(OverflowError, day.__mul__, 1e9)
550 self.assertRaises(OverflowError, day.__truediv__, 1e-20)
551 self.assertRaises(OverflowError, day.__truediv__, 1e-10)
552 self.assertRaises(OverflowError, day.__truediv__, 9e-10)
553
554 @requires_IEEE_754
555 def _test_overflow_special(self):
556 day = timedelta(1)
557 self.assertRaises(OverflowError, day.__mul__, INF)
558 self.assertRaises(OverflowError, day.__mul__, -INF)
559
Tim Peters2a799bf2002-12-16 20:18:38 +0000560 def test_microsecond_rounding(self):
561 td = timedelta
562 eq = self.assertEqual
563
564 # Single-field rounding.
565 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
566 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
567 eq(td(milliseconds=0.6/1000), td(microseconds=1))
568 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
569
570 # Rounding due to contributions from more than one field.
571 us_per_hour = 3600e6
572 us_per_day = us_per_hour * 24
573 eq(td(days=.4/us_per_day), td(0))
574 eq(td(hours=.2/us_per_hour), td(0))
575 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
576
577 eq(td(days=-.4/us_per_day), td(0))
578 eq(td(hours=-.2/us_per_hour), td(0))
579 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
580
581 def test_massive_normalization(self):
582 td = timedelta(microseconds=-1)
583 self.assertEqual((td.days, td.seconds, td.microseconds),
584 (-1, 24*3600-1, 999999))
585
586 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000587 self.assertTrue(timedelta(1))
588 self.assertTrue(timedelta(0, 1))
589 self.assertTrue(timedelta(0, 0, 1))
590 self.assertTrue(timedelta(microseconds=1))
591 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000592
Tim Petersb0c854d2003-05-17 15:57:00 +0000593 def test_subclass_timedelta(self):
594
595 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000596 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000597 def from_td(td):
598 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000599
600 def as_hours(self):
601 sum = (self.days * 24 +
602 self.seconds / 3600.0 +
603 self.microseconds / 3600e6)
604 return round(sum)
605
606 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000607 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000608 self.assertEqual(t1.as_hours(), 24)
609
610 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000611 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000612 self.assertEqual(t2.as_hours(), -25)
613
614 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000615 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000616 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000617 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000618 self.assertEqual(t3.days, t4.days)
619 self.assertEqual(t3.seconds, t4.seconds)
620 self.assertEqual(t3.microseconds, t4.microseconds)
621 self.assertEqual(str(t3), str(t4))
622 self.assertEqual(t4.as_hours(), -1)
623
Mark Dickinson7c186e22010-04-20 22:32:49 +0000624 def test_division(self):
625 t = timedelta(hours=1, minutes=24, seconds=19)
626 second = timedelta(seconds=1)
627 self.assertEqual(t / second, 5059.0)
628 self.assertEqual(t // second, 5059)
629
630 t = timedelta(minutes=2, seconds=30)
631 minute = timedelta(minutes=1)
632 self.assertEqual(t / minute, 2.5)
633 self.assertEqual(t // minute, 2)
634
635 zerotd = timedelta(0)
636 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
637 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
638
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000639 # self.assertRaises(TypeError, truediv, t, 2)
Mark Dickinson7c186e22010-04-20 22:32:49 +0000640 # note: floor division of a timedelta by an integer *is*
641 # currently permitted.
642
643 def test_remainder(self):
644 t = timedelta(minutes=2, seconds=30)
645 minute = timedelta(minutes=1)
646 r = t % minute
647 self.assertEqual(r, timedelta(seconds=30))
648
649 t = timedelta(minutes=-2, seconds=30)
650 r = t % minute
651 self.assertEqual(r, timedelta(seconds=30))
652
653 zerotd = timedelta(0)
654 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
655
656 self.assertRaises(TypeError, mod, t, 10)
657
658 def test_divmod(self):
659 t = timedelta(minutes=2, seconds=30)
660 minute = timedelta(minutes=1)
661 q, r = divmod(t, minute)
662 self.assertEqual(q, 2)
663 self.assertEqual(r, timedelta(seconds=30))
664
665 t = timedelta(minutes=-2, seconds=30)
666 q, r = divmod(t, minute)
667 self.assertEqual(q, -2)
668 self.assertEqual(r, timedelta(seconds=30))
669
670 zerotd = timedelta(0)
671 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
672
673 self.assertRaises(TypeError, divmod, t, 10)
674
675
Tim Peters2a799bf2002-12-16 20:18:38 +0000676#############################################################################
677# date tests
678
679class TestDateOnly(unittest.TestCase):
680 # Tests here won't pass if also run on datetime objects, so don't
681 # subclass this to test datetimes too.
682
683 def test_delta_non_days_ignored(self):
684 dt = date(2000, 1, 2)
685 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
686 microseconds=5)
687 days = timedelta(delta.days)
688 self.assertEqual(days, timedelta(1))
689
690 dt2 = dt + delta
691 self.assertEqual(dt2, dt + days)
692
693 dt2 = delta + dt
694 self.assertEqual(dt2, dt + days)
695
696 dt2 = dt - delta
697 self.assertEqual(dt2, dt - days)
698
699 delta = -delta
700 days = timedelta(delta.days)
701 self.assertEqual(days, timedelta(-2))
702
703 dt2 = dt + delta
704 self.assertEqual(dt2, dt + days)
705
706 dt2 = delta + dt
707 self.assertEqual(dt2, dt + days)
708
709 dt2 = dt - delta
710 self.assertEqual(dt2, dt - days)
711
Tim Peters604c0132004-06-07 23:04:33 +0000712class SubclassDate(date):
713 sub_var = 1
714
Guido van Rossumd8faa362007-04-27 19:54:29 +0000715class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000716 # Tests here should pass for both dates and datetimes, except for a
717 # few tests that TestDateTime overrides.
718
719 theclass = date
720
721 def test_basic_attributes(self):
722 dt = self.theclass(2002, 3, 1)
723 self.assertEqual(dt.year, 2002)
724 self.assertEqual(dt.month, 3)
725 self.assertEqual(dt.day, 1)
726
727 def test_roundtrip(self):
728 for dt in (self.theclass(1, 2, 3),
729 self.theclass.today()):
730 # Verify dt -> string -> date identity.
731 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000732 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000733 s = s[9:]
734 dt2 = eval(s)
735 self.assertEqual(dt, dt2)
736
737 # Verify identity via reconstructing from pieces.
738 dt2 = self.theclass(dt.year, dt.month, dt.day)
739 self.assertEqual(dt, dt2)
740
741 def test_ordinal_conversions(self):
742 # Check some fixed values.
743 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
744 (1, 12, 31, 365),
745 (2, 1, 1, 366),
746 # first example from "Calendrical Calculations"
747 (1945, 11, 12, 710347)]:
748 d = self.theclass(y, m, d)
749 self.assertEqual(n, d.toordinal())
750 fromord = self.theclass.fromordinal(n)
751 self.assertEqual(d, fromord)
752 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000753 # if we're checking something fancier than a date, verify
754 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000755 self.assertEqual(fromord.hour, 0)
756 self.assertEqual(fromord.minute, 0)
757 self.assertEqual(fromord.second, 0)
758 self.assertEqual(fromord.microsecond, 0)
759
Tim Peters0bf60bd2003-01-08 20:40:01 +0000760 # Check first and last days of year spottily across the whole
761 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000762 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000763 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
764 d = self.theclass(year, 1, 1)
765 n = d.toordinal()
766 d2 = self.theclass.fromordinal(n)
767 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000768 # Verify that moving back a day gets to the end of year-1.
769 if year > 1:
770 d = self.theclass.fromordinal(n-1)
771 d2 = self.theclass(year-1, 12, 31)
772 self.assertEqual(d, d2)
773 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000774
775 # Test every day in a leap-year and a non-leap year.
776 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
777 for year, isleap in (2000, True), (2002, False):
778 n = self.theclass(year, 1, 1).toordinal()
779 for month, maxday in zip(range(1, 13), dim):
780 if month == 2 and isleap:
781 maxday += 1
782 for day in range(1, maxday+1):
783 d = self.theclass(year, month, day)
784 self.assertEqual(d.toordinal(), n)
785 self.assertEqual(d, self.theclass.fromordinal(n))
786 n += 1
787
788 def test_extreme_ordinals(self):
789 a = self.theclass.min
790 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
791 aord = a.toordinal()
792 b = a.fromordinal(aord)
793 self.assertEqual(a, b)
794
795 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
796
797 b = a + timedelta(days=1)
798 self.assertEqual(b.toordinal(), aord + 1)
799 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
800
801 a = self.theclass.max
802 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
803 aord = a.toordinal()
804 b = a.fromordinal(aord)
805 self.assertEqual(a, b)
806
807 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
808
809 b = a - timedelta(days=1)
810 self.assertEqual(b.toordinal(), aord - 1)
811 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
812
813 def test_bad_constructor_arguments(self):
814 # bad years
815 self.theclass(MINYEAR, 1, 1) # no exception
816 self.theclass(MAXYEAR, 1, 1) # no exception
817 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
818 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
819 # bad months
820 self.theclass(2000, 1, 1) # no exception
821 self.theclass(2000, 12, 1) # no exception
822 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
823 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
824 # bad days
825 self.theclass(2000, 2, 29) # no exception
826 self.theclass(2004, 2, 29) # no exception
827 self.theclass(2400, 2, 29) # no exception
828 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
829 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
830 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
831 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
832 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
833 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
834
835 def test_hash_equality(self):
836 d = self.theclass(2000, 12, 31)
837 # same thing
838 e = self.theclass(2000, 12, 31)
839 self.assertEqual(d, e)
840 self.assertEqual(hash(d), hash(e))
841
842 dic = {d: 1}
843 dic[e] = 2
844 self.assertEqual(len(dic), 1)
845 self.assertEqual(dic[d], 2)
846 self.assertEqual(dic[e], 2)
847
848 d = self.theclass(2001, 1, 1)
849 # same thing
850 e = self.theclass(2001, 1, 1)
851 self.assertEqual(d, e)
852 self.assertEqual(hash(d), hash(e))
853
854 dic = {d: 1}
855 dic[e] = 2
856 self.assertEqual(len(dic), 1)
857 self.assertEqual(dic[d], 2)
858 self.assertEqual(dic[e], 2)
859
860 def test_computations(self):
861 a = self.theclass(2002, 1, 31)
862 b = self.theclass(1956, 1, 31)
863
864 diff = a-b
865 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
866 self.assertEqual(diff.seconds, 0)
867 self.assertEqual(diff.microseconds, 0)
868
869 day = timedelta(1)
870 week = timedelta(7)
871 a = self.theclass(2002, 3, 2)
872 self.assertEqual(a + day, self.theclass(2002, 3, 3))
873 self.assertEqual(day + a, self.theclass(2002, 3, 3))
874 self.assertEqual(a - day, self.theclass(2002, 3, 1))
875 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
876 self.assertEqual(a + week, self.theclass(2002, 3, 9))
877 self.assertEqual(a - week, self.theclass(2002, 2, 23))
878 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
879 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
880 self.assertEqual((a + week) - a, week)
881 self.assertEqual((a + day) - a, day)
882 self.assertEqual((a - week) - a, -week)
883 self.assertEqual((a - day) - a, -day)
884 self.assertEqual(a - (a + week), -week)
885 self.assertEqual(a - (a + day), -day)
886 self.assertEqual(a - (a - week), week)
887 self.assertEqual(a - (a - day), day)
888
Mark Dickinson5c2db372009-12-05 20:28:34 +0000889 # Add/sub ints or floats should be illegal
890 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000891 self.assertRaises(TypeError, lambda: a+i)
892 self.assertRaises(TypeError, lambda: a-i)
893 self.assertRaises(TypeError, lambda: i+a)
894 self.assertRaises(TypeError, lambda: i-a)
895
896 # delta - date is senseless.
897 self.assertRaises(TypeError, lambda: day - a)
898 # mixing date and (delta or date) via * or // is senseless
899 self.assertRaises(TypeError, lambda: day * a)
900 self.assertRaises(TypeError, lambda: a * day)
901 self.assertRaises(TypeError, lambda: day // a)
902 self.assertRaises(TypeError, lambda: a // day)
903 self.assertRaises(TypeError, lambda: a * a)
904 self.assertRaises(TypeError, lambda: a // a)
905 # date + date is senseless
906 self.assertRaises(TypeError, lambda: a + a)
907
908 def test_overflow(self):
909 tiny = self.theclass.resolution
910
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000911 for delta in [tiny, timedelta(1), timedelta(2)]:
912 dt = self.theclass.min + delta
913 dt -= delta # no problem
914 self.assertRaises(OverflowError, dt.__sub__, delta)
915 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000916
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000917 dt = self.theclass.max - delta
918 dt += delta # no problem
919 self.assertRaises(OverflowError, dt.__add__, delta)
920 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000921
922 def test_fromtimestamp(self):
923 import time
924
925 # Try an arbitrary fixed value.
926 year, month, day = 1999, 9, 19
927 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
928 d = self.theclass.fromtimestamp(ts)
929 self.assertEqual(d.year, year)
930 self.assertEqual(d.month, month)
931 self.assertEqual(d.day, day)
932
Tim Peters1b6f7a92004-06-20 02:50:16 +0000933 def test_insane_fromtimestamp(self):
934 # It's possible that some platform maps time_t to double,
935 # and that this test will fail there. This test should
936 # exempt such platforms (provided they return reasonable
937 # results!).
938 for insane in -1e200, 1e200:
939 self.assertRaises(ValueError, self.theclass.fromtimestamp,
940 insane)
941
Tim Peters2a799bf2002-12-16 20:18:38 +0000942 def test_today(self):
943 import time
944
945 # We claim that today() is like fromtimestamp(time.time()), so
946 # prove it.
947 for dummy in range(3):
948 today = self.theclass.today()
949 ts = time.time()
950 todayagain = self.theclass.fromtimestamp(ts)
951 if today == todayagain:
952 break
953 # There are several legit reasons that could fail:
954 # 1. It recently became midnight, between the today() and the
955 # time() calls.
956 # 2. The platform time() has such fine resolution that we'll
957 # never get the same value twice.
958 # 3. The platform time() has poor resolution, and we just
959 # happened to call today() right before a resolution quantum
960 # boundary.
961 # 4. The system clock got fiddled between calls.
962 # In any case, wait a little while and try again.
963 time.sleep(0.1)
964
965 # It worked or it didn't. If it didn't, assume it's reason #2, and
966 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000967 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000968 abs(todayagain - today) < timedelta(seconds=0.5))
969
970 def test_weekday(self):
971 for i in range(7):
972 # March 4, 2002 is a Monday
973 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
974 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
975 # January 2, 1956 is a Monday
976 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
977 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
978
979 def test_isocalendar(self):
980 # Check examples from
981 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
982 for i in range(7):
983 d = self.theclass(2003, 12, 22+i)
984 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
985 d = self.theclass(2003, 12, 29) + timedelta(i)
986 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
987 d = self.theclass(2004, 1, 5+i)
988 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
989 d = self.theclass(2009, 12, 21+i)
990 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
991 d = self.theclass(2009, 12, 28) + timedelta(i)
992 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
993 d = self.theclass(2010, 1, 4+i)
994 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
995
996 def test_iso_long_years(self):
997 # Calculate long ISO years and compare to table from
998 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
999 ISO_LONG_YEARS_TABLE = """
1000 4 32 60 88
1001 9 37 65 93
1002 15 43 71 99
1003 20 48 76
1004 26 54 82
1005
1006 105 133 161 189
1007 111 139 167 195
1008 116 144 172
1009 122 150 178
1010 128 156 184
1011
1012 201 229 257 285
1013 207 235 263 291
1014 212 240 268 296
1015 218 246 274
1016 224 252 280
1017
1018 303 331 359 387
1019 308 336 364 392
1020 314 342 370 398
1021 320 348 376
1022 325 353 381
1023 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +00001024 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +00001025 L = []
1026 for i in range(400):
1027 d = self.theclass(2000+i, 12, 31)
1028 d1 = self.theclass(1600+i, 12, 31)
1029 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
1030 if d.isocalendar()[1] == 53:
1031 L.append(i)
1032 self.assertEqual(L, iso_long_years)
1033
1034 def test_isoformat(self):
1035 t = self.theclass(2, 3, 2)
1036 self.assertEqual(t.isoformat(), "0002-03-02")
1037
1038 def test_ctime(self):
1039 t = self.theclass(2002, 3, 2)
1040 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
1041
1042 def test_strftime(self):
1043 t = self.theclass(2005, 3, 2)
1044 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +00001045 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +00001046 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +00001047
1048 self.assertRaises(TypeError, t.strftime) # needs an arg
1049 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
1050 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
1051
Georg Brandlf78e02b2008-06-10 17:40:04 +00001052 # test that unicode input is allowed (issue 2782)
1053 self.assertEqual(t.strftime("%m"), "03")
1054
Tim Peters2a799bf2002-12-16 20:18:38 +00001055 # A naive object replaces %z and %Z w/ empty strings.
1056 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1057
Benjamin Petersone1cdfd72009-01-18 21:02:37 +00001058 #make sure that invalid format specifiers are handled correctly
1059 #self.assertRaises(ValueError, t.strftime, "%e")
1060 #self.assertRaises(ValueError, t.strftime, "%")
1061 #self.assertRaises(ValueError, t.strftime, "%#")
1062
1063 #oh well, some systems just ignore those invalid ones.
1064 #at least, excercise them to make sure that no crashes
1065 #are generated
1066 for f in ["%e", "%", "%#"]:
1067 try:
1068 t.strftime(f)
1069 except ValueError:
1070 pass
1071
1072 #check that this standard extension works
1073 t.strftime("%f")
1074
Georg Brandlf78e02b2008-06-10 17:40:04 +00001075
Eric Smith1ba31142007-09-11 18:06:02 +00001076 def test_format(self):
1077 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001078 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001079
1080 # check that a derived class's __str__() gets called
1081 class A(self.theclass):
1082 def __str__(self):
1083 return 'A'
1084 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001085 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001086
1087 # check that a derived class's strftime gets called
1088 class B(self.theclass):
1089 def strftime(self, format_spec):
1090 return 'B'
1091 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001092 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001093
1094 for fmt in ["m:%m d:%d y:%y",
1095 "m:%m d:%d y:%y H:%H M:%M S:%S",
1096 "%z %Z",
1097 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001098 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1099 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1100 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001101
Tim Peters2a799bf2002-12-16 20:18:38 +00001102 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00001103 self.assertIsInstance(self.theclass.min, self.theclass)
1104 self.assertIsInstance(self.theclass.max, self.theclass)
1105 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001106 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001107
1108 def test_extreme_timedelta(self):
1109 big = self.theclass.max - self.theclass.min
1110 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1111 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1112 # n == 315537897599999999 ~= 2**58.13
1113 justasbig = timedelta(0, 0, n)
1114 self.assertEqual(big, justasbig)
1115 self.assertEqual(self.theclass.min + big, self.theclass.max)
1116 self.assertEqual(self.theclass.max - big, self.theclass.min)
1117
1118 def test_timetuple(self):
1119 for i in range(7):
1120 # January 2, 1956 is a Monday (0)
1121 d = self.theclass(1956, 1, 2+i)
1122 t = d.timetuple()
1123 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1124 # February 1, 1956 is a Wednesday (2)
1125 d = self.theclass(1956, 2, 1+i)
1126 t = d.timetuple()
1127 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1128 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1129 # of the year.
1130 d = self.theclass(1956, 3, 1+i)
1131 t = d.timetuple()
1132 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1133 self.assertEqual(t.tm_year, 1956)
1134 self.assertEqual(t.tm_mon, 3)
1135 self.assertEqual(t.tm_mday, 1+i)
1136 self.assertEqual(t.tm_hour, 0)
1137 self.assertEqual(t.tm_min, 0)
1138 self.assertEqual(t.tm_sec, 0)
1139 self.assertEqual(t.tm_wday, (3+i)%7)
1140 self.assertEqual(t.tm_yday, 61+i)
1141 self.assertEqual(t.tm_isdst, -1)
1142
1143 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001144 args = 6, 7, 23
1145 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001146 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001147 green = pickler.dumps(orig, proto)
1148 derived = unpickler.loads(green)
1149 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001150
1151 def test_compare(self):
1152 t1 = self.theclass(2, 3, 4)
1153 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001154 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001155 self.assertTrue(t1 <= t2)
1156 self.assertTrue(t1 >= t2)
1157 self.assertTrue(not t1 != t2)
1158 self.assertTrue(not t1 < t2)
1159 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001160
1161 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1162 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001163 self.assertTrue(t1 < t2)
1164 self.assertTrue(t2 > t1)
1165 self.assertTrue(t1 <= t2)
1166 self.assertTrue(t2 >= t1)
1167 self.assertTrue(t1 != t2)
1168 self.assertTrue(t2 != t1)
1169 self.assertTrue(not t1 == t2)
1170 self.assertTrue(not t2 == t1)
1171 self.assertTrue(not t1 > t2)
1172 self.assertTrue(not t2 < t1)
1173 self.assertTrue(not t1 >= t2)
1174 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001175
Tim Peters68124bb2003-02-08 03:46:31 +00001176 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001177 self.assertEqual(t1 == badarg, False)
1178 self.assertEqual(t1 != badarg, True)
1179 self.assertEqual(badarg == t1, False)
1180 self.assertEqual(badarg != t1, True)
1181
Tim Peters2a799bf2002-12-16 20:18:38 +00001182 self.assertRaises(TypeError, lambda: t1 < badarg)
1183 self.assertRaises(TypeError, lambda: t1 > badarg)
1184 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001185 self.assertRaises(TypeError, lambda: badarg <= t1)
1186 self.assertRaises(TypeError, lambda: badarg < t1)
1187 self.assertRaises(TypeError, lambda: badarg > t1)
1188 self.assertRaises(TypeError, lambda: badarg >= t1)
1189
Tim Peters8d81a012003-01-24 22:36:34 +00001190 def test_mixed_compare(self):
1191 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001192
1193 # Our class can be compared for equality to other classes
1194 self.assertEqual(our == 1, False)
1195 self.assertEqual(1 == our, False)
1196 self.assertEqual(our != 1, True)
1197 self.assertEqual(1 != our, True)
1198
1199 # But the ordering is undefined
1200 self.assertRaises(TypeError, lambda: our < 1)
1201 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001202
Guido van Rossum19960592006-08-24 17:29:38 +00001203 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001204
Guido van Rossum19960592006-08-24 17:29:38 +00001205 class SomeClass:
1206 pass
1207
1208 their = SomeClass()
1209 self.assertEqual(our == their, False)
1210 self.assertEqual(their == our, False)
1211 self.assertEqual(our != their, True)
1212 self.assertEqual(their != our, True)
1213 self.assertRaises(TypeError, lambda: our < their)
1214 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001215
Guido van Rossum19960592006-08-24 17:29:38 +00001216 # However, if the other class explicitly defines ordering
1217 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001218
Guido van Rossum19960592006-08-24 17:29:38 +00001219 class LargerThanAnything:
1220 def __lt__(self, other):
1221 return False
1222 def __le__(self, other):
1223 return isinstance(other, LargerThanAnything)
1224 def __eq__(self, other):
1225 return isinstance(other, LargerThanAnything)
1226 def __ne__(self, other):
1227 return not isinstance(other, LargerThanAnything)
1228 def __gt__(self, other):
1229 return not isinstance(other, LargerThanAnything)
1230 def __ge__(self, other):
1231 return True
1232
1233 their = LargerThanAnything()
1234 self.assertEqual(our == their, False)
1235 self.assertEqual(their == our, False)
1236 self.assertEqual(our != their, True)
1237 self.assertEqual(their != our, True)
1238 self.assertEqual(our < their, True)
1239 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001240
Tim Peters2a799bf2002-12-16 20:18:38 +00001241 def test_bool(self):
1242 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001243 self.assertTrue(self.theclass.min)
1244 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001245
Guido van Rossum04110fb2007-08-24 16:32:05 +00001246 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001247 # For nasty technical reasons, we can't handle years before 1900.
1248 cls = self.theclass
1249 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1250 for y in 1, 49, 51, 99, 100, 1000, 1899:
1251 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001252
1253 def test_replace(self):
1254 cls = self.theclass
1255 args = [1, 2, 3]
1256 base = cls(*args)
1257 self.assertEqual(base, base.replace())
1258
1259 i = 0
1260 for name, newval in (("year", 2),
1261 ("month", 3),
1262 ("day", 4)):
1263 newargs = args[:]
1264 newargs[i] = newval
1265 expected = cls(*newargs)
1266 got = base.replace(**{name: newval})
1267 self.assertEqual(expected, got)
1268 i += 1
1269
1270 # Out of bounds.
1271 base = cls(2000, 2, 29)
1272 self.assertRaises(ValueError, base.replace, year=2001)
1273
Tim Petersa98924a2003-05-17 05:55:19 +00001274 def test_subclass_date(self):
1275
1276 class C(self.theclass):
1277 theAnswer = 42
1278
1279 def __new__(cls, *args, **kws):
1280 temp = kws.copy()
1281 extra = temp.pop('extra')
1282 result = self.theclass.__new__(cls, *args, **temp)
1283 result.extra = extra
1284 return result
1285
1286 def newmeth(self, start):
1287 return start + self.year + self.month
1288
1289 args = 2003, 4, 14
1290
1291 dt1 = self.theclass(*args)
1292 dt2 = C(*args, **{'extra': 7})
1293
1294 self.assertEqual(dt2.__class__, C)
1295 self.assertEqual(dt2.theAnswer, 42)
1296 self.assertEqual(dt2.extra, 7)
1297 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1298 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1299
Tim Peters604c0132004-06-07 23:04:33 +00001300 def test_pickling_subclass_date(self):
1301
1302 args = 6, 7, 23
1303 orig = SubclassDate(*args)
1304 for pickler, unpickler, proto in pickle_choices:
1305 green = pickler.dumps(orig, proto)
1306 derived = unpickler.loads(green)
1307 self.assertEqual(orig, derived)
1308
Tim Peters3f606292004-03-21 23:38:41 +00001309 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001310 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001311 # This is a low-overhead backdoor. A user can (by intent or
1312 # mistake) pass a string directly, which (if it's the right length)
1313 # will get treated like a pickle, and bypass the normal sanity
1314 # checks in the constructor. This can create insane objects.
1315 # The constructor doesn't want to burn the time to validate all
1316 # fields, but does check the month field. This stops, e.g.,
1317 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001318 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001319 if not issubclass(self.theclass, datetime):
1320 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001321 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001322 self.assertRaises(TypeError, self.theclass,
1323 base[:2] + month_byte + base[3:])
1324 for ord_byte in range(1, 13):
1325 # This shouldn't blow up because of the month byte alone. If
1326 # the implementation changes to do more-careful checking, it may
1327 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001328 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001329
Tim Peters2a799bf2002-12-16 20:18:38 +00001330#############################################################################
1331# datetime tests
1332
Tim Peters604c0132004-06-07 23:04:33 +00001333class SubclassDatetime(datetime):
1334 sub_var = 1
1335
Tim Peters2a799bf2002-12-16 20:18:38 +00001336class TestDateTime(TestDate):
1337
1338 theclass = datetime
1339
1340 def test_basic_attributes(self):
1341 dt = self.theclass(2002, 3, 1, 12, 0)
1342 self.assertEqual(dt.year, 2002)
1343 self.assertEqual(dt.month, 3)
1344 self.assertEqual(dt.day, 1)
1345 self.assertEqual(dt.hour, 12)
1346 self.assertEqual(dt.minute, 0)
1347 self.assertEqual(dt.second, 0)
1348 self.assertEqual(dt.microsecond, 0)
1349
1350 def test_basic_attributes_nonzero(self):
1351 # Make sure all attributes are non-zero so bugs in
1352 # bit-shifting access show up.
1353 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1354 self.assertEqual(dt.year, 2002)
1355 self.assertEqual(dt.month, 3)
1356 self.assertEqual(dt.day, 1)
1357 self.assertEqual(dt.hour, 12)
1358 self.assertEqual(dt.minute, 59)
1359 self.assertEqual(dt.second, 59)
1360 self.assertEqual(dt.microsecond, 8000)
1361
1362 def test_roundtrip(self):
1363 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1364 self.theclass.now()):
1365 # Verify dt -> string -> datetime identity.
1366 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001367 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001368 s = s[9:]
1369 dt2 = eval(s)
1370 self.assertEqual(dt, dt2)
1371
1372 # Verify identity via reconstructing from pieces.
1373 dt2 = self.theclass(dt.year, dt.month, dt.day,
1374 dt.hour, dt.minute, dt.second,
1375 dt.microsecond)
1376 self.assertEqual(dt, dt2)
1377
1378 def test_isoformat(self):
1379 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1380 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1381 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1382 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001383 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001384 # str is ISO format with the separator forced to a blank.
1385 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1386
1387 t = self.theclass(2, 3, 2)
1388 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1389 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1390 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1391 # str is ISO format with the separator forced to a blank.
1392 self.assertEqual(str(t), "0002-03-02 00:00:00")
1393
Eric Smith1ba31142007-09-11 18:06:02 +00001394 def test_format(self):
1395 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001396 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001397
1398 # check that a derived class's __str__() gets called
1399 class A(self.theclass):
1400 def __str__(self):
1401 return 'A'
1402 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001403 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001404
1405 # check that a derived class's strftime gets called
1406 class B(self.theclass):
1407 def strftime(self, format_spec):
1408 return 'B'
1409 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001410 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001411
1412 for fmt in ["m:%m d:%d y:%y",
1413 "m:%m d:%d y:%y H:%H M:%M S:%S",
1414 "%z %Z",
1415 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001416 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1417 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1418 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001419
Tim Peters2a799bf2002-12-16 20:18:38 +00001420 def test_more_ctime(self):
1421 # Test fields that TestDate doesn't touch.
1422 import time
1423
1424 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1425 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1426 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1427 # out. The difference is that t.ctime() produces " 2" for the day,
1428 # but platform ctime() produces "02" for the day. According to
1429 # C99, t.ctime() is correct here.
1430 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1431
1432 # So test a case where that difference doesn't matter.
1433 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1434 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1435
1436 def test_tz_independent_comparing(self):
1437 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1438 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1439 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1440 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001441 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001442
1443 # Make sure comparison doesn't forget microseconds, and isn't done
1444 # via comparing a float timestamp (an IEEE double doesn't have enough
1445 # precision to span microsecond resolution across years 1 thru 9999,
1446 # so comparing via timestamp necessarily calls some distinct values
1447 # equal).
1448 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1449 us = timedelta(microseconds=1)
1450 dt2 = dt1 + us
1451 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001452 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001453
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001454 def test_strftime_with_bad_tzname_replace(self):
1455 # verify ok if tzinfo.tzname().replace() returns a non-string
1456 class MyTzInfo(FixedOffset):
1457 def tzname(self, dt):
1458 class MyStr(str):
1459 def replace(self, *args):
1460 return None
1461 return MyStr('name')
1462 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1463 self.assertRaises(TypeError, t.strftime, '%Z')
1464
Tim Peters2a799bf2002-12-16 20:18:38 +00001465 def test_bad_constructor_arguments(self):
1466 # bad years
1467 self.theclass(MINYEAR, 1, 1) # no exception
1468 self.theclass(MAXYEAR, 1, 1) # no exception
1469 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1470 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1471 # bad months
1472 self.theclass(2000, 1, 1) # no exception
1473 self.theclass(2000, 12, 1) # no exception
1474 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1475 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1476 # bad days
1477 self.theclass(2000, 2, 29) # no exception
1478 self.theclass(2004, 2, 29) # no exception
1479 self.theclass(2400, 2, 29) # no exception
1480 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1481 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1482 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1483 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1484 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1485 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1486 # bad hours
1487 self.theclass(2000, 1, 31, 0) # no exception
1488 self.theclass(2000, 1, 31, 23) # no exception
1489 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1490 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1491 # bad minutes
1492 self.theclass(2000, 1, 31, 23, 0) # no exception
1493 self.theclass(2000, 1, 31, 23, 59) # no exception
1494 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1495 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1496 # bad seconds
1497 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1498 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1499 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1500 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1501 # bad microseconds
1502 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1503 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1504 self.assertRaises(ValueError, self.theclass,
1505 2000, 1, 31, 23, 59, 59, -1)
1506 self.assertRaises(ValueError, self.theclass,
1507 2000, 1, 31, 23, 59, 59,
1508 1000000)
1509
1510 def test_hash_equality(self):
1511 d = self.theclass(2000, 12, 31, 23, 30, 17)
1512 e = self.theclass(2000, 12, 31, 23, 30, 17)
1513 self.assertEqual(d, e)
1514 self.assertEqual(hash(d), hash(e))
1515
1516 dic = {d: 1}
1517 dic[e] = 2
1518 self.assertEqual(len(dic), 1)
1519 self.assertEqual(dic[d], 2)
1520 self.assertEqual(dic[e], 2)
1521
1522 d = self.theclass(2001, 1, 1, 0, 5, 17)
1523 e = self.theclass(2001, 1, 1, 0, 5, 17)
1524 self.assertEqual(d, e)
1525 self.assertEqual(hash(d), hash(e))
1526
1527 dic = {d: 1}
1528 dic[e] = 2
1529 self.assertEqual(len(dic), 1)
1530 self.assertEqual(dic[d], 2)
1531 self.assertEqual(dic[e], 2)
1532
1533 def test_computations(self):
1534 a = self.theclass(2002, 1, 31)
1535 b = self.theclass(1956, 1, 31)
1536 diff = a-b
1537 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1538 self.assertEqual(diff.seconds, 0)
1539 self.assertEqual(diff.microseconds, 0)
1540 a = self.theclass(2002, 3, 2, 17, 6)
1541 millisec = timedelta(0, 0, 1000)
1542 hour = timedelta(0, 3600)
1543 day = timedelta(1)
1544 week = timedelta(7)
1545 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1546 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1547 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1548 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1549 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1550 self.assertEqual(a - hour, a + -hour)
1551 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1552 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1553 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1554 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1555 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1556 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1557 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1558 self.assertEqual((a + week) - a, week)
1559 self.assertEqual((a + day) - a, day)
1560 self.assertEqual((a + hour) - a, hour)
1561 self.assertEqual((a + millisec) - a, millisec)
1562 self.assertEqual((a - week) - a, -week)
1563 self.assertEqual((a - day) - a, -day)
1564 self.assertEqual((a - hour) - a, -hour)
1565 self.assertEqual((a - millisec) - a, -millisec)
1566 self.assertEqual(a - (a + week), -week)
1567 self.assertEqual(a - (a + day), -day)
1568 self.assertEqual(a - (a + hour), -hour)
1569 self.assertEqual(a - (a + millisec), -millisec)
1570 self.assertEqual(a - (a - week), week)
1571 self.assertEqual(a - (a - day), day)
1572 self.assertEqual(a - (a - hour), hour)
1573 self.assertEqual(a - (a - millisec), millisec)
1574 self.assertEqual(a + (week + day + hour + millisec),
1575 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1576 self.assertEqual(a + (week + day + hour + millisec),
1577 (((a + week) + day) + hour) + millisec)
1578 self.assertEqual(a - (week + day + hour + millisec),
1579 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1580 self.assertEqual(a - (week + day + hour + millisec),
1581 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001582 # Add/sub ints or floats should be illegal
1583 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001584 self.assertRaises(TypeError, lambda: a+i)
1585 self.assertRaises(TypeError, lambda: a-i)
1586 self.assertRaises(TypeError, lambda: i+a)
1587 self.assertRaises(TypeError, lambda: i-a)
1588
1589 # delta - datetime is senseless.
1590 self.assertRaises(TypeError, lambda: day - a)
1591 # mixing datetime and (delta or datetime) via * or // is senseless
1592 self.assertRaises(TypeError, lambda: day * a)
1593 self.assertRaises(TypeError, lambda: a * day)
1594 self.assertRaises(TypeError, lambda: day // a)
1595 self.assertRaises(TypeError, lambda: a // day)
1596 self.assertRaises(TypeError, lambda: a * a)
1597 self.assertRaises(TypeError, lambda: a // a)
1598 # datetime + datetime is senseless
1599 self.assertRaises(TypeError, lambda: a + a)
1600
1601 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001602 args = 6, 7, 23, 20, 59, 1, 64**2
1603 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001604 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001605 green = pickler.dumps(orig, proto)
1606 derived = unpickler.loads(green)
1607 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001608
Guido van Rossum275666f2003-02-07 21:49:01 +00001609 def test_more_pickling(self):
1610 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1611 s = pickle.dumps(a)
1612 b = pickle.loads(s)
1613 self.assertEqual(b.year, 2003)
1614 self.assertEqual(b.month, 2)
1615 self.assertEqual(b.day, 7)
1616
Tim Peters604c0132004-06-07 23:04:33 +00001617 def test_pickling_subclass_datetime(self):
1618 args = 6, 7, 23, 20, 59, 1, 64**2
1619 orig = SubclassDatetime(*args)
1620 for pickler, unpickler, proto in pickle_choices:
1621 green = pickler.dumps(orig, proto)
1622 derived = unpickler.loads(green)
1623 self.assertEqual(orig, derived)
1624
Tim Peters2a799bf2002-12-16 20:18:38 +00001625 def test_more_compare(self):
1626 # The test_compare() inherited from TestDate covers the error cases.
1627 # We just want to test lexicographic ordering on the members datetime
1628 # has that date lacks.
1629 args = [2000, 11, 29, 20, 58, 16, 999998]
1630 t1 = self.theclass(*args)
1631 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001632 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001633 self.assertTrue(t1 <= t2)
1634 self.assertTrue(t1 >= t2)
1635 self.assertTrue(not t1 != t2)
1636 self.assertTrue(not t1 < t2)
1637 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001638
1639 for i in range(len(args)):
1640 newargs = args[:]
1641 newargs[i] = args[i] + 1
1642 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001643 self.assertTrue(t1 < t2)
1644 self.assertTrue(t2 > t1)
1645 self.assertTrue(t1 <= t2)
1646 self.assertTrue(t2 >= t1)
1647 self.assertTrue(t1 != t2)
1648 self.assertTrue(t2 != t1)
1649 self.assertTrue(not t1 == t2)
1650 self.assertTrue(not t2 == t1)
1651 self.assertTrue(not t1 > t2)
1652 self.assertTrue(not t2 < t1)
1653 self.assertTrue(not t1 >= t2)
1654 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001655
1656
1657 # A helper for timestamp constructor tests.
1658 def verify_field_equality(self, expected, got):
1659 self.assertEqual(expected.tm_year, got.year)
1660 self.assertEqual(expected.tm_mon, got.month)
1661 self.assertEqual(expected.tm_mday, got.day)
1662 self.assertEqual(expected.tm_hour, got.hour)
1663 self.assertEqual(expected.tm_min, got.minute)
1664 self.assertEqual(expected.tm_sec, got.second)
1665
1666 def test_fromtimestamp(self):
1667 import time
1668
1669 ts = time.time()
1670 expected = time.localtime(ts)
1671 got = self.theclass.fromtimestamp(ts)
1672 self.verify_field_equality(expected, got)
1673
1674 def test_utcfromtimestamp(self):
1675 import time
1676
1677 ts = time.time()
1678 expected = time.gmtime(ts)
1679 got = self.theclass.utcfromtimestamp(ts)
1680 self.verify_field_equality(expected, got)
1681
Thomas Wouters477c8d52006-05-27 19:21:47 +00001682 def test_microsecond_rounding(self):
1683 # Test whether fromtimestamp "rounds up" floats that are less
1684 # than one microsecond smaller than an integer.
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001685 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1686 self.theclass.fromtimestamp(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001687
Tim Peters1b6f7a92004-06-20 02:50:16 +00001688 def test_insane_fromtimestamp(self):
1689 # It's possible that some platform maps time_t to double,
1690 # and that this test will fail there. This test should
1691 # exempt such platforms (provided they return reasonable
1692 # results!).
1693 for insane in -1e200, 1e200:
1694 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1695 insane)
1696
1697 def test_insane_utcfromtimestamp(self):
1698 # It's possible that some platform maps time_t to double,
1699 # and that this test will fail there. This test should
1700 # exempt such platforms (provided they return reasonable
1701 # results!).
1702 for insane in -1e200, 1e200:
1703 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1704 insane)
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001705 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001706 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001707 # The result is tz-dependent; at least test that this doesn't
1708 # fail (like it did before bug 1646728 was fixed).
1709 self.theclass.fromtimestamp(-1.05)
1710
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001711 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001712 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001713 d = self.theclass.utcfromtimestamp(-1.05)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001714 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001715
Tim Peters2a799bf2002-12-16 20:18:38 +00001716 def test_utcnow(self):
1717 import time
1718
1719 # Call it a success if utcnow() and utcfromtimestamp() are within
1720 # a second of each other.
1721 tolerance = timedelta(seconds=1)
1722 for dummy in range(3):
1723 from_now = self.theclass.utcnow()
1724 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1725 if abs(from_timestamp - from_now) <= tolerance:
1726 break
1727 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001728 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001729
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001730 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001731 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001732
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001733 string = '2004-12-01 13:02:47.197'
1734 format = '%Y-%m-%d %H:%M:%S.%f'
Alexander Belopolskyca94f552010-06-17 18:30:34 +00001735 expected = _strptime._strptime_datetime(string, format)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001736 got = self.theclass.strptime(string, format)
1737 self.assertEqual(expected, got)
1738
Alexander Belopolskyca94f552010-06-17 18:30:34 +00001739 strptime = self.theclass.strptime
1740 self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
1741 self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
1742 # Only local timezone and UTC are supported
1743 for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
1744 (-_time.timezone, _time.tzname[0])):
1745 if tzseconds < 0:
1746 sign = '-'
1747 seconds = -tzseconds
1748 else:
1749 sign ='+'
1750 seconds = tzseconds
1751 hours, minutes = divmod(seconds//60, 60)
1752 dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
1753 dt = strptime(dtstr, "%z %Z")
1754 self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
1755 self.assertEqual(dt.tzname(), tzname)
1756 # Can produce inconsistent datetime
1757 dtstr, fmt = "+1234 UTC", "%z %Z"
1758 dt = strptime(dtstr, fmt)
1759 self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
1760 self.assertEqual(dt.tzname(), 'UTC')
1761 # yet will roundtrip
1762 self.assertEqual(dt.strftime(fmt), dtstr)
1763
1764 # Produce naive datetime if no %z is provided
1765 self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
1766
1767 with self.assertRaises(ValueError): strptime("-2400", "%z")
1768 with self.assertRaises(ValueError): strptime("-000", "%z")
1769
Tim Peters2a799bf2002-12-16 20:18:38 +00001770 def test_more_timetuple(self):
1771 # This tests fields beyond those tested by the TestDate.test_timetuple.
1772 t = self.theclass(2004, 12, 31, 6, 22, 33)
1773 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1774 self.assertEqual(t.timetuple(),
1775 (t.year, t.month, t.day,
1776 t.hour, t.minute, t.second,
1777 t.weekday(),
1778 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1779 -1))
1780 tt = t.timetuple()
1781 self.assertEqual(tt.tm_year, t.year)
1782 self.assertEqual(tt.tm_mon, t.month)
1783 self.assertEqual(tt.tm_mday, t.day)
1784 self.assertEqual(tt.tm_hour, t.hour)
1785 self.assertEqual(tt.tm_min, t.minute)
1786 self.assertEqual(tt.tm_sec, t.second)
1787 self.assertEqual(tt.tm_wday, t.weekday())
1788 self.assertEqual(tt.tm_yday, t.toordinal() -
1789 date(t.year, 1, 1).toordinal() + 1)
1790 self.assertEqual(tt.tm_isdst, -1)
1791
1792 def test_more_strftime(self):
1793 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001794 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1795 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1796 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001797
1798 def test_extract(self):
1799 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1800 self.assertEqual(dt.date(), date(2002, 3, 4))
1801 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1802
1803 def test_combine(self):
1804 d = date(2002, 3, 4)
1805 t = time(18, 45, 3, 1234)
1806 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1807 combine = self.theclass.combine
1808 dt = combine(d, t)
1809 self.assertEqual(dt, expected)
1810
1811 dt = combine(time=t, date=d)
1812 self.assertEqual(dt, expected)
1813
1814 self.assertEqual(d, dt.date())
1815 self.assertEqual(t, dt.time())
1816 self.assertEqual(dt, combine(dt.date(), dt.time()))
1817
1818 self.assertRaises(TypeError, combine) # need an arg
1819 self.assertRaises(TypeError, combine, d) # need two args
1820 self.assertRaises(TypeError, combine, t, d) # args reversed
1821 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1822 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1823
Tim Peters12bf3392002-12-24 05:41:27 +00001824 def test_replace(self):
1825 cls = self.theclass
1826 args = [1, 2, 3, 4, 5, 6, 7]
1827 base = cls(*args)
1828 self.assertEqual(base, base.replace())
1829
1830 i = 0
1831 for name, newval in (("year", 2),
1832 ("month", 3),
1833 ("day", 4),
1834 ("hour", 5),
1835 ("minute", 6),
1836 ("second", 7),
1837 ("microsecond", 8)):
1838 newargs = args[:]
1839 newargs[i] = newval
1840 expected = cls(*newargs)
1841 got = base.replace(**{name: newval})
1842 self.assertEqual(expected, got)
1843 i += 1
1844
1845 # Out of bounds.
1846 base = cls(2000, 2, 29)
1847 self.assertRaises(ValueError, base.replace, year=2001)
1848
Tim Peters80475bb2002-12-25 07:40:55 +00001849 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001850 # Pretty boring! The TZ test is more interesting here. astimezone()
1851 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001852 dt = self.theclass.now()
1853 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001854 self.assertRaises(TypeError, dt.astimezone) # not enough args
1855 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1856 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001857 self.assertRaises(ValueError, dt.astimezone, f) # naive
1858 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001859
Tim Peters52dcce22003-01-23 16:36:11 +00001860 class Bogus(tzinfo):
1861 def utcoffset(self, dt): return None
1862 def dst(self, dt): return timedelta(0)
1863 bog = Bogus()
1864 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1865
1866 class AlsoBogus(tzinfo):
1867 def utcoffset(self, dt): return timedelta(0)
1868 def dst(self, dt): return None
1869 alsobog = AlsoBogus()
1870 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001871
Tim Petersa98924a2003-05-17 05:55:19 +00001872 def test_subclass_datetime(self):
1873
1874 class C(self.theclass):
1875 theAnswer = 42
1876
1877 def __new__(cls, *args, **kws):
1878 temp = kws.copy()
1879 extra = temp.pop('extra')
1880 result = self.theclass.__new__(cls, *args, **temp)
1881 result.extra = extra
1882 return result
1883
1884 def newmeth(self, start):
1885 return start + self.year + self.month + self.second
1886
1887 args = 2003, 4, 14, 12, 13, 41
1888
1889 dt1 = self.theclass(*args)
1890 dt2 = C(*args, **{'extra': 7})
1891
1892 self.assertEqual(dt2.__class__, C)
1893 self.assertEqual(dt2.theAnswer, 42)
1894 self.assertEqual(dt2.extra, 7)
1895 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1896 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1897 dt1.second - 7)
1898
Tim Peters604c0132004-06-07 23:04:33 +00001899class SubclassTime(time):
1900 sub_var = 1
1901
Guido van Rossumd8faa362007-04-27 19:54:29 +00001902class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001903
1904 theclass = time
1905
1906 def test_basic_attributes(self):
1907 t = self.theclass(12, 0)
1908 self.assertEqual(t.hour, 12)
1909 self.assertEqual(t.minute, 0)
1910 self.assertEqual(t.second, 0)
1911 self.assertEqual(t.microsecond, 0)
1912
1913 def test_basic_attributes_nonzero(self):
1914 # Make sure all attributes are non-zero so bugs in
1915 # bit-shifting access show up.
1916 t = self.theclass(12, 59, 59, 8000)
1917 self.assertEqual(t.hour, 12)
1918 self.assertEqual(t.minute, 59)
1919 self.assertEqual(t.second, 59)
1920 self.assertEqual(t.microsecond, 8000)
1921
1922 def test_roundtrip(self):
1923 t = self.theclass(1, 2, 3, 4)
1924
1925 # Verify t -> string -> time identity.
1926 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001927 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001928 s = s[9:]
1929 t2 = eval(s)
1930 self.assertEqual(t, t2)
1931
1932 # Verify identity via reconstructing from pieces.
1933 t2 = self.theclass(t.hour, t.minute, t.second,
1934 t.microsecond)
1935 self.assertEqual(t, t2)
1936
1937 def test_comparing(self):
1938 args = [1, 2, 3, 4]
1939 t1 = self.theclass(*args)
1940 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001941 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001942 self.assertTrue(t1 <= t2)
1943 self.assertTrue(t1 >= t2)
1944 self.assertTrue(not t1 != t2)
1945 self.assertTrue(not t1 < t2)
1946 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001947
1948 for i in range(len(args)):
1949 newargs = args[:]
1950 newargs[i] = args[i] + 1
1951 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001952 self.assertTrue(t1 < t2)
1953 self.assertTrue(t2 > t1)
1954 self.assertTrue(t1 <= t2)
1955 self.assertTrue(t2 >= t1)
1956 self.assertTrue(t1 != t2)
1957 self.assertTrue(t2 != t1)
1958 self.assertTrue(not t1 == t2)
1959 self.assertTrue(not t2 == t1)
1960 self.assertTrue(not t1 > t2)
1961 self.assertTrue(not t2 < t1)
1962 self.assertTrue(not t1 >= t2)
1963 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001964
Tim Peters68124bb2003-02-08 03:46:31 +00001965 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001966 self.assertEqual(t1 == badarg, False)
1967 self.assertEqual(t1 != badarg, True)
1968 self.assertEqual(badarg == t1, False)
1969 self.assertEqual(badarg != t1, True)
1970
Tim Peters2a799bf2002-12-16 20:18:38 +00001971 self.assertRaises(TypeError, lambda: t1 <= badarg)
1972 self.assertRaises(TypeError, lambda: t1 < badarg)
1973 self.assertRaises(TypeError, lambda: t1 > badarg)
1974 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001975 self.assertRaises(TypeError, lambda: badarg <= t1)
1976 self.assertRaises(TypeError, lambda: badarg < t1)
1977 self.assertRaises(TypeError, lambda: badarg > t1)
1978 self.assertRaises(TypeError, lambda: badarg >= t1)
1979
1980 def test_bad_constructor_arguments(self):
1981 # bad hours
1982 self.theclass(0, 0) # no exception
1983 self.theclass(23, 0) # no exception
1984 self.assertRaises(ValueError, self.theclass, -1, 0)
1985 self.assertRaises(ValueError, self.theclass, 24, 0)
1986 # bad minutes
1987 self.theclass(23, 0) # no exception
1988 self.theclass(23, 59) # no exception
1989 self.assertRaises(ValueError, self.theclass, 23, -1)
1990 self.assertRaises(ValueError, self.theclass, 23, 60)
1991 # bad seconds
1992 self.theclass(23, 59, 0) # no exception
1993 self.theclass(23, 59, 59) # no exception
1994 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
1995 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
1996 # bad microseconds
1997 self.theclass(23, 59, 59, 0) # no exception
1998 self.theclass(23, 59, 59, 999999) # no exception
1999 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
2000 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
2001
2002 def test_hash_equality(self):
2003 d = self.theclass(23, 30, 17)
2004 e = self.theclass(23, 30, 17)
2005 self.assertEqual(d, e)
2006 self.assertEqual(hash(d), hash(e))
2007
2008 dic = {d: 1}
2009 dic[e] = 2
2010 self.assertEqual(len(dic), 1)
2011 self.assertEqual(dic[d], 2)
2012 self.assertEqual(dic[e], 2)
2013
2014 d = self.theclass(0, 5, 17)
2015 e = self.theclass(0, 5, 17)
2016 self.assertEqual(d, e)
2017 self.assertEqual(hash(d), hash(e))
2018
2019 dic = {d: 1}
2020 dic[e] = 2
2021 self.assertEqual(len(dic), 1)
2022 self.assertEqual(dic[d], 2)
2023 self.assertEqual(dic[e], 2)
2024
2025 def test_isoformat(self):
2026 t = self.theclass(4, 5, 1, 123)
2027 self.assertEqual(t.isoformat(), "04:05:01.000123")
2028 self.assertEqual(t.isoformat(), str(t))
2029
2030 t = self.theclass()
2031 self.assertEqual(t.isoformat(), "00:00:00")
2032 self.assertEqual(t.isoformat(), str(t))
2033
2034 t = self.theclass(microsecond=1)
2035 self.assertEqual(t.isoformat(), "00:00:00.000001")
2036 self.assertEqual(t.isoformat(), str(t))
2037
2038 t = self.theclass(microsecond=10)
2039 self.assertEqual(t.isoformat(), "00:00:00.000010")
2040 self.assertEqual(t.isoformat(), str(t))
2041
2042 t = self.theclass(microsecond=100)
2043 self.assertEqual(t.isoformat(), "00:00:00.000100")
2044 self.assertEqual(t.isoformat(), str(t))
2045
2046 t = self.theclass(microsecond=1000)
2047 self.assertEqual(t.isoformat(), "00:00:00.001000")
2048 self.assertEqual(t.isoformat(), str(t))
2049
2050 t = self.theclass(microsecond=10000)
2051 self.assertEqual(t.isoformat(), "00:00:00.010000")
2052 self.assertEqual(t.isoformat(), str(t))
2053
2054 t = self.theclass(microsecond=100000)
2055 self.assertEqual(t.isoformat(), "00:00:00.100000")
2056 self.assertEqual(t.isoformat(), str(t))
2057
Thomas Wouterscf297e42007-02-23 15:07:44 +00002058 def test_1653736(self):
2059 # verify it doesn't accept extra keyword arguments
2060 t = self.theclass(second=1)
2061 self.assertRaises(TypeError, t.isoformat, foo=3)
2062
Tim Peters2a799bf2002-12-16 20:18:38 +00002063 def test_strftime(self):
2064 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00002065 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00002066 # A naive object replaces %z and %Z with empty strings.
2067 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2068
Eric Smith1ba31142007-09-11 18:06:02 +00002069 def test_format(self):
2070 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002071 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002072
2073 # check that a derived class's __str__() gets called
2074 class A(self.theclass):
2075 def __str__(self):
2076 return 'A'
2077 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002078 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00002079
2080 # check that a derived class's strftime gets called
2081 class B(self.theclass):
2082 def strftime(self, format_spec):
2083 return 'B'
2084 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002085 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002086
2087 for fmt in ['%H %M %S',
2088 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00002089 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2090 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2091 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00002092
Tim Peters2a799bf2002-12-16 20:18:38 +00002093 def test_str(self):
2094 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2095 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2096 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2097 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2098 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2099
2100 def test_repr(self):
2101 name = 'datetime.' + self.theclass.__name__
2102 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2103 "%s(1, 2, 3, 4)" % name)
2104 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2105 "%s(10, 2, 3, 4000)" % name)
2106 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2107 "%s(0, 2, 3, 400000)" % name)
2108 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2109 "%s(12, 2, 3)" % name)
2110 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2111 "%s(23, 15)" % name)
2112
2113 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00002114 self.assertIsInstance(self.theclass.min, self.theclass)
2115 self.assertIsInstance(self.theclass.max, self.theclass)
2116 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002117 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00002118
2119 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002120 args = 20, 59, 16, 64**2
2121 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002122 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002123 green = pickler.dumps(orig, proto)
2124 derived = unpickler.loads(green)
2125 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002126
Tim Peters604c0132004-06-07 23:04:33 +00002127 def test_pickling_subclass_time(self):
2128 args = 20, 59, 16, 64**2
2129 orig = SubclassTime(*args)
2130 for pickler, unpickler, proto in pickle_choices:
2131 green = pickler.dumps(orig, proto)
2132 derived = unpickler.loads(green)
2133 self.assertEqual(orig, derived)
2134
Tim Peters2a799bf2002-12-16 20:18:38 +00002135 def test_bool(self):
2136 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002137 self.assertTrue(cls(1))
2138 self.assertTrue(cls(0, 1))
2139 self.assertTrue(cls(0, 0, 1))
2140 self.assertTrue(cls(0, 0, 0, 1))
2141 self.assertTrue(not cls(0))
2142 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00002143
Tim Peters12bf3392002-12-24 05:41:27 +00002144 def test_replace(self):
2145 cls = self.theclass
2146 args = [1, 2, 3, 4]
2147 base = cls(*args)
2148 self.assertEqual(base, base.replace())
2149
2150 i = 0
2151 for name, newval in (("hour", 5),
2152 ("minute", 6),
2153 ("second", 7),
2154 ("microsecond", 8)):
2155 newargs = args[:]
2156 newargs[i] = newval
2157 expected = cls(*newargs)
2158 got = base.replace(**{name: newval})
2159 self.assertEqual(expected, got)
2160 i += 1
2161
2162 # Out of bounds.
2163 base = cls(1)
2164 self.assertRaises(ValueError, base.replace, hour=24)
2165 self.assertRaises(ValueError, base.replace, minute=-1)
2166 self.assertRaises(ValueError, base.replace, second=100)
2167 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2168
Tim Petersa98924a2003-05-17 05:55:19 +00002169 def test_subclass_time(self):
2170
2171 class C(self.theclass):
2172 theAnswer = 42
2173
2174 def __new__(cls, *args, **kws):
2175 temp = kws.copy()
2176 extra = temp.pop('extra')
2177 result = self.theclass.__new__(cls, *args, **temp)
2178 result.extra = extra
2179 return result
2180
2181 def newmeth(self, start):
2182 return start + self.hour + self.second
2183
2184 args = 4, 5, 6
2185
2186 dt1 = self.theclass(*args)
2187 dt2 = C(*args, **{'extra': 7})
2188
2189 self.assertEqual(dt2.__class__, C)
2190 self.assertEqual(dt2.theAnswer, 42)
2191 self.assertEqual(dt2.extra, 7)
2192 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2193 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2194
Armin Rigof4afb212005-11-07 07:15:48 +00002195 def test_backdoor_resistance(self):
2196 # see TestDate.test_backdoor_resistance().
2197 base = '2:59.0'
2198 for hour_byte in ' ', '9', chr(24), '\xff':
2199 self.assertRaises(TypeError, self.theclass,
2200 hour_byte + base[1:])
2201
Tim Peters855fe882002-12-22 03:43:39 +00002202# A mixin for classes with a tzinfo= argument. Subclasses must define
2203# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002204# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002205class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002206
Tim Petersbad8ff02002-12-30 20:52:32 +00002207 def test_argument_passing(self):
2208 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002209 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002210 class introspective(tzinfo):
2211 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002212 def utcoffset(self, dt):
2213 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002214 dst = utcoffset
2215
2216 obj = cls(1, 2, 3, tzinfo=introspective())
2217
Tim Peters0bf60bd2003-01-08 20:40:01 +00002218 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002219 self.assertEqual(obj.tzname(), expected)
2220
Tim Peters0bf60bd2003-01-08 20:40:01 +00002221 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002222 self.assertEqual(obj.utcoffset(), expected)
2223 self.assertEqual(obj.dst(), expected)
2224
Tim Peters855fe882002-12-22 03:43:39 +00002225 def test_bad_tzinfo_classes(self):
2226 cls = self.theclass
2227 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002228
Tim Peters855fe882002-12-22 03:43:39 +00002229 class NiceTry(object):
2230 def __init__(self): pass
2231 def utcoffset(self, dt): pass
2232 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2233
2234 class BetterTry(tzinfo):
2235 def __init__(self): pass
2236 def utcoffset(self, dt): pass
2237 b = BetterTry()
2238 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002239 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002240
2241 def test_utc_offset_out_of_bounds(self):
2242 class Edgy(tzinfo):
2243 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002244 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002245 def utcoffset(self, dt):
2246 return self.offset
2247
2248 cls = self.theclass
2249 for offset, legit in ((-1440, False),
2250 (-1439, True),
2251 (1439, True),
2252 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002253 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002254 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002255 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002256 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002257 else:
2258 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002259 if legit:
2260 aofs = abs(offset)
2261 h, m = divmod(aofs, 60)
2262 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002263 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002264 t = t.timetz()
2265 self.assertEqual(str(t), "01:02:03" + tag)
2266 else:
2267 self.assertRaises(ValueError, str, t)
2268
2269 def test_tzinfo_classes(self):
2270 cls = self.theclass
2271 class C1(tzinfo):
2272 def utcoffset(self, dt): return None
2273 def dst(self, dt): return None
2274 def tzname(self, dt): return None
2275 for t in (cls(1, 1, 1),
2276 cls(1, 1, 1, tzinfo=None),
2277 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002278 self.assertTrue(t.utcoffset() is None)
2279 self.assertTrue(t.dst() is None)
2280 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002281
Tim Peters855fe882002-12-22 03:43:39 +00002282 class C3(tzinfo):
2283 def utcoffset(self, dt): return timedelta(minutes=-1439)
2284 def dst(self, dt): return timedelta(minutes=1439)
2285 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002286 t = cls(1, 1, 1, tzinfo=C3())
2287 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2288 self.assertEqual(t.dst(), timedelta(minutes=1439))
2289 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002290
2291 # Wrong types.
2292 class C4(tzinfo):
2293 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002294 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002295 def tzname(self, dt): return 0
2296 t = cls(1, 1, 1, tzinfo=C4())
2297 self.assertRaises(TypeError, t.utcoffset)
2298 self.assertRaises(TypeError, t.dst)
2299 self.assertRaises(TypeError, t.tzname)
2300
2301 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002302 class C6(tzinfo):
2303 def utcoffset(self, dt): return timedelta(hours=-24)
2304 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002305 t = cls(1, 1, 1, tzinfo=C6())
2306 self.assertRaises(ValueError, t.utcoffset)
2307 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002308
2309 # Not a whole number of minutes.
2310 class C7(tzinfo):
2311 def utcoffset(self, dt): return timedelta(seconds=61)
2312 def dst(self, dt): return timedelta(microseconds=-81)
2313 t = cls(1, 1, 1, tzinfo=C7())
2314 self.assertRaises(ValueError, t.utcoffset)
2315 self.assertRaises(ValueError, t.dst)
2316
Tim Peters4c0db782002-12-26 05:01:19 +00002317 def test_aware_compare(self):
2318 cls = self.theclass
2319
Tim Peters60c76e42002-12-27 00:41:11 +00002320 # Ensure that utcoffset() gets ignored if the comparands have
2321 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002322 class OperandDependentOffset(tzinfo):
2323 def utcoffset(self, t):
2324 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002325 # d0 and d1 equal after adjustment
2326 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002327 else:
Tim Peters397301e2003-01-02 21:28:08 +00002328 # d2 off in the weeds
2329 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002330
2331 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2332 d0 = base.replace(minute=3)
2333 d1 = base.replace(minute=9)
2334 d2 = base.replace(minute=11)
2335 for x in d0, d1, d2:
2336 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002337 for op in lt, le, gt, ge, eq, ne:
2338 got = op(x, y)
2339 expected = op(x.minute, y.minute)
2340 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002341
2342 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002343 # Note that a time can't actually have an operand-depedent offset,
2344 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2345 # so skip this test for time.
2346 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002347 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2348 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2349 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2350 for x in d0, d1, d2:
2351 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002352 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002353 if (x is d0 or x is d1) and (y is d0 or y is d1):
2354 expected = 0
2355 elif x is y is d2:
2356 expected = 0
2357 elif x is d2:
2358 expected = -1
2359 else:
2360 assert y is d2
2361 expected = 1
2362 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002363
Tim Peters855fe882002-12-22 03:43:39 +00002364
Tim Peters0bf60bd2003-01-08 20:40:01 +00002365# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002366class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002367 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002368
2369 def test_empty(self):
2370 t = self.theclass()
2371 self.assertEqual(t.hour, 0)
2372 self.assertEqual(t.minute, 0)
2373 self.assertEqual(t.second, 0)
2374 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002375 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002376
Tim Peters2a799bf2002-12-16 20:18:38 +00002377 def test_zones(self):
2378 est = FixedOffset(-300, "EST", 1)
2379 utc = FixedOffset(0, "UTC", -2)
2380 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002381 t1 = time( 7, 47, tzinfo=est)
2382 t2 = time(12, 47, tzinfo=utc)
2383 t3 = time(13, 47, tzinfo=met)
2384 t4 = time(microsecond=40)
2385 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002386
2387 self.assertEqual(t1.tzinfo, est)
2388 self.assertEqual(t2.tzinfo, utc)
2389 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002390 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002391 self.assertEqual(t5.tzinfo, utc)
2392
Tim Peters855fe882002-12-22 03:43:39 +00002393 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2394 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2395 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002396 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002397 self.assertRaises(TypeError, t1.utcoffset, "no args")
2398
2399 self.assertEqual(t1.tzname(), "EST")
2400 self.assertEqual(t2.tzname(), "UTC")
2401 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002402 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002403 self.assertRaises(TypeError, t1.tzname, "no args")
2404
Tim Peters855fe882002-12-22 03:43:39 +00002405 self.assertEqual(t1.dst(), timedelta(minutes=1))
2406 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2407 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002408 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002409 self.assertRaises(TypeError, t1.dst, "no args")
2410
2411 self.assertEqual(hash(t1), hash(t2))
2412 self.assertEqual(hash(t1), hash(t3))
2413 self.assertEqual(hash(t2), hash(t3))
2414
2415 self.assertEqual(t1, t2)
2416 self.assertEqual(t1, t3)
2417 self.assertEqual(t2, t3)
2418 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2419 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2420 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2421
2422 self.assertEqual(str(t1), "07:47:00-05:00")
2423 self.assertEqual(str(t2), "12:47:00+00:00")
2424 self.assertEqual(str(t3), "13:47:00+01:00")
2425 self.assertEqual(str(t4), "00:00:00.000040")
2426 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2427
2428 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2429 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2430 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2431 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2432 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2433
Tim Peters0bf60bd2003-01-08 20:40:01 +00002434 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002435 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2436 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2437 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2438 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2439 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2440
2441 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2442 "07:47:00 %Z=EST %z=-0500")
2443 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2444 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2445
2446 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002447 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002448 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2449 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2450
Tim Petersb92bb712002-12-21 17:44:07 +00002451 # Check that an invalid tzname result raises an exception.
2452 class Badtzname(tzinfo):
2453 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002454 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002455 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2456 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002457
2458 def test_hash_edge_cases(self):
2459 # Offsets that overflow a basic time.
2460 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2461 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2462 self.assertEqual(hash(t1), hash(t2))
2463
2464 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2465 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2466 self.assertEqual(hash(t1), hash(t2))
2467
Tim Peters2a799bf2002-12-16 20:18:38 +00002468 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002469 # Try one without a tzinfo.
2470 args = 20, 59, 16, 64**2
2471 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002472 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002473 green = pickler.dumps(orig, proto)
2474 derived = unpickler.loads(green)
2475 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002476
2477 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002478 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002479 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002480 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002481 green = pickler.dumps(orig, proto)
2482 derived = unpickler.loads(green)
2483 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002484 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002485 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2486 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002487
2488 def test_more_bool(self):
2489 # Test cases with non-None tzinfo.
2490 cls = self.theclass
2491
2492 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002493 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002494
2495 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002496 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002497
2498 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002499 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002500
2501 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002502 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002503
2504 # Mostly ensuring this doesn't overflow internally.
2505 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002506 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002507
2508 # But this should yield a value error -- the utcoffset is bogus.
2509 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2510 self.assertRaises(ValueError, lambda: bool(t))
2511
2512 # Likewise.
2513 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2514 self.assertRaises(ValueError, lambda: bool(t))
2515
Tim Peters12bf3392002-12-24 05:41:27 +00002516 def test_replace(self):
2517 cls = self.theclass
2518 z100 = FixedOffset(100, "+100")
2519 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2520 args = [1, 2, 3, 4, z100]
2521 base = cls(*args)
2522 self.assertEqual(base, base.replace())
2523
2524 i = 0
2525 for name, newval in (("hour", 5),
2526 ("minute", 6),
2527 ("second", 7),
2528 ("microsecond", 8),
2529 ("tzinfo", zm200)):
2530 newargs = args[:]
2531 newargs[i] = newval
2532 expected = cls(*newargs)
2533 got = base.replace(**{name: newval})
2534 self.assertEqual(expected, got)
2535 i += 1
2536
2537 # Ensure we can get rid of a tzinfo.
2538 self.assertEqual(base.tzname(), "+100")
2539 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002540 self.assertTrue(base2.tzinfo is None)
2541 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002542
2543 # Ensure we can add one.
2544 base3 = base2.replace(tzinfo=z100)
2545 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002546 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002547
2548 # Out of bounds.
2549 base = cls(1)
2550 self.assertRaises(ValueError, base.replace, hour=24)
2551 self.assertRaises(ValueError, base.replace, minute=-1)
2552 self.assertRaises(ValueError, base.replace, second=100)
2553 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2554
Tim Peters60c76e42002-12-27 00:41:11 +00002555 def test_mixed_compare(self):
2556 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002557 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002558 self.assertEqual(t1, t2)
2559 t2 = t2.replace(tzinfo=None)
2560 self.assertEqual(t1, t2)
2561 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2562 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002563 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2564 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002565
Tim Peters0bf60bd2003-01-08 20:40:01 +00002566 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002567 class Varies(tzinfo):
2568 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002569 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002570 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002571 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002572 return self.offset
2573
2574 v = Varies()
2575 t1 = t2.replace(tzinfo=v)
2576 t2 = t2.replace(tzinfo=v)
2577 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2578 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2579 self.assertEqual(t1, t2)
2580
2581 # But if they're not identical, it isn't ignored.
2582 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002583 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002584
Tim Petersa98924a2003-05-17 05:55:19 +00002585 def test_subclass_timetz(self):
2586
2587 class C(self.theclass):
2588 theAnswer = 42
2589
2590 def __new__(cls, *args, **kws):
2591 temp = kws.copy()
2592 extra = temp.pop('extra')
2593 result = self.theclass.__new__(cls, *args, **temp)
2594 result.extra = extra
2595 return result
2596
2597 def newmeth(self, start):
2598 return start + self.hour + self.second
2599
2600 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2601
2602 dt1 = self.theclass(*args)
2603 dt2 = C(*args, **{'extra': 7})
2604
2605 self.assertEqual(dt2.__class__, C)
2606 self.assertEqual(dt2.theAnswer, 42)
2607 self.assertEqual(dt2.extra, 7)
2608 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2609 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2610
Tim Peters4c0db782002-12-26 05:01:19 +00002611
Tim Peters0bf60bd2003-01-08 20:40:01 +00002612# Testing datetime objects with a non-None tzinfo.
2613
Guido van Rossumd8faa362007-04-27 19:54:29 +00002614class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002615 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002616
2617 def test_trivial(self):
2618 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2619 self.assertEqual(dt.year, 1)
2620 self.assertEqual(dt.month, 2)
2621 self.assertEqual(dt.day, 3)
2622 self.assertEqual(dt.hour, 4)
2623 self.assertEqual(dt.minute, 5)
2624 self.assertEqual(dt.second, 6)
2625 self.assertEqual(dt.microsecond, 7)
2626 self.assertEqual(dt.tzinfo, None)
2627
2628 def test_even_more_compare(self):
2629 # The test_compare() and test_more_compare() inherited from TestDate
2630 # and TestDateTime covered non-tzinfo cases.
2631
2632 # Smallest possible after UTC adjustment.
2633 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2634 # Largest possible after UTC adjustment.
2635 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2636 tzinfo=FixedOffset(-1439, ""))
2637
2638 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002639 self.assertTrue(t1 < t2)
2640 self.assertTrue(t1 != t2)
2641 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002642
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002643 self.assertEqual(t1, t1)
2644 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002645
2646 # Equal afer adjustment.
2647 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2648 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2649 self.assertEqual(t1, t2)
2650
2651 # Change t1 not to subtract a minute, and t1 should be larger.
2652 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002653 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002654
2655 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2656 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002657 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002658
2659 # Back to the original t1, but make seconds resolve it.
2660 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2661 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002662 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002663
2664 # Likewise, but make microseconds resolve it.
2665 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2666 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002667 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002668
2669 # Make t2 naive and it should fail.
2670 t2 = self.theclass.min
2671 self.assertRaises(TypeError, lambda: t1 == t2)
2672 self.assertEqual(t2, t2)
2673
2674 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2675 class Naive(tzinfo):
2676 def utcoffset(self, dt): return None
2677 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2678 self.assertRaises(TypeError, lambda: t1 == t2)
2679 self.assertEqual(t2, t2)
2680
2681 # OTOH, it's OK to compare two of these mixing the two ways of being
2682 # naive.
2683 t1 = self.theclass(5, 6, 7)
2684 self.assertEqual(t1, t2)
2685
2686 # Try a bogus uctoffset.
2687 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002688 def utcoffset(self, dt):
2689 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002690 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2691 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002692 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002693
Tim Peters2a799bf2002-12-16 20:18:38 +00002694 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002695 # Try one without a tzinfo.
2696 args = 6, 7, 23, 20, 59, 1, 64**2
2697 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002698 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002699 green = pickler.dumps(orig, proto)
2700 derived = unpickler.loads(green)
2701 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002702
2703 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002704 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002705 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002706 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002707 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002708 green = pickler.dumps(orig, proto)
2709 derived = unpickler.loads(green)
2710 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002711 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002712 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2713 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002714
2715 def test_extreme_hashes(self):
2716 # If an attempt is made to hash these via subtracting the offset
2717 # then hashing a datetime object, OverflowError results. The
2718 # Python implementation used to blow up here.
2719 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2720 hash(t)
2721 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2722 tzinfo=FixedOffset(-1439, ""))
2723 hash(t)
2724
2725 # OTOH, an OOB offset should blow up.
2726 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2727 self.assertRaises(ValueError, hash, t)
2728
2729 def test_zones(self):
2730 est = FixedOffset(-300, "EST")
2731 utc = FixedOffset(0, "UTC")
2732 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002733 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2734 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2735 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002736 self.assertEqual(t1.tzinfo, est)
2737 self.assertEqual(t2.tzinfo, utc)
2738 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002739 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2740 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2741 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002742 self.assertEqual(t1.tzname(), "EST")
2743 self.assertEqual(t2.tzname(), "UTC")
2744 self.assertEqual(t3.tzname(), "MET")
2745 self.assertEqual(hash(t1), hash(t2))
2746 self.assertEqual(hash(t1), hash(t3))
2747 self.assertEqual(hash(t2), hash(t3))
2748 self.assertEqual(t1, t2)
2749 self.assertEqual(t1, t3)
2750 self.assertEqual(t2, t3)
2751 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2752 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2753 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002754 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002755 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2756 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2757 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2758
2759 def test_combine(self):
2760 met = FixedOffset(60, "MET")
2761 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002762 tz = time(18, 45, 3, 1234, tzinfo=met)
2763 dt = datetime.combine(d, tz)
2764 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002765 tzinfo=met))
2766
2767 def test_extract(self):
2768 met = FixedOffset(60, "MET")
2769 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2770 self.assertEqual(dt.date(), date(2002, 3, 4))
2771 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002772 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002773
2774 def test_tz_aware_arithmetic(self):
2775 import random
2776
2777 now = self.theclass.now()
2778 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002779 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002780 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002781 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002782 self.assertEqual(nowaware.timetz(), timeaware)
2783
2784 # Can't mix aware and non-aware.
2785 self.assertRaises(TypeError, lambda: now - nowaware)
2786 self.assertRaises(TypeError, lambda: nowaware - now)
2787
Tim Peters0bf60bd2003-01-08 20:40:01 +00002788 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002789 self.assertRaises(TypeError, lambda: now + nowaware)
2790 self.assertRaises(TypeError, lambda: nowaware + now)
2791 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2792
2793 # Subtracting should yield 0.
2794 self.assertEqual(now - now, timedelta(0))
2795 self.assertEqual(nowaware - nowaware, timedelta(0))
2796
2797 # Adding a delta should preserve tzinfo.
2798 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2799 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002800 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002801 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002802 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002803 self.assertEqual(nowawareplus, nowawareplus2)
2804
2805 # that - delta should be what we started with, and that - what we
2806 # started with should be delta.
2807 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002808 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002809 self.assertEqual(nowaware, diff)
2810 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2811 self.assertEqual(nowawareplus - nowaware, delta)
2812
2813 # Make up a random timezone.
2814 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002815 # Attach it to nowawareplus.
2816 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002817 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002818 # Make sure the difference takes the timezone adjustments into account.
2819 got = nowaware - nowawareplus
2820 # Expected: (nowaware base - nowaware offset) -
2821 # (nowawareplus base - nowawareplus offset) =
2822 # (nowaware base - nowawareplus base) +
2823 # (nowawareplus offset - nowaware offset) =
2824 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002825 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002826 self.assertEqual(got, expected)
2827
2828 # Try max possible difference.
2829 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2830 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2831 tzinfo=FixedOffset(-1439, "max"))
2832 maxdiff = max - min
2833 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2834 timedelta(minutes=2*1439))
2835
2836 def test_tzinfo_now(self):
2837 meth = self.theclass.now
2838 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2839 base = meth()
2840 # Try with and without naming the keyword.
2841 off42 = FixedOffset(42, "42")
2842 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002843 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002844 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002845 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002846 # Bad argument with and w/o naming the keyword.
2847 self.assertRaises(TypeError, meth, 16)
2848 self.assertRaises(TypeError, meth, tzinfo=16)
2849 # Bad keyword name.
2850 self.assertRaises(TypeError, meth, tinfo=off42)
2851 # Too many args.
2852 self.assertRaises(TypeError, meth, off42, off42)
2853
Tim Peters10cadce2003-01-23 19:58:02 +00002854 # We don't know which time zone we're in, and don't have a tzinfo
2855 # class to represent it, so seeing whether a tz argument actually
2856 # does a conversion is tricky.
Tim Peters10cadce2003-01-23 19:58:02 +00002857 utc = FixedOffset(0, "utc", 0)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +00002858 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
2859 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
2860 for dummy in range(3):
2861 now = datetime.now(weirdtz)
2862 self.assertTrue(now.tzinfo is weirdtz)
2863 utcnow = datetime.utcnow().replace(tzinfo=utc)
2864 now2 = utcnow.astimezone(weirdtz)
2865 if abs(now - now2) < timedelta(seconds=30):
2866 break
2867 # Else the code is broken, or more than 30 seconds passed between
2868 # calls; assuming the latter, just try again.
2869 else:
2870 # Three strikes and we're out.
2871 self.fail("utcnow(), now(tz), or astimezone() may be broken")
Tim Peters10cadce2003-01-23 19:58:02 +00002872
Tim Peters2a799bf2002-12-16 20:18:38 +00002873 def test_tzinfo_fromtimestamp(self):
2874 import time
2875 meth = self.theclass.fromtimestamp
2876 ts = time.time()
2877 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2878 base = meth(ts)
2879 # Try with and without naming the keyword.
2880 off42 = FixedOffset(42, "42")
2881 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002882 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002883 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002884 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002885 # Bad argument with and w/o naming the keyword.
2886 self.assertRaises(TypeError, meth, ts, 16)
2887 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2888 # Bad keyword name.
2889 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2890 # Too many args.
2891 self.assertRaises(TypeError, meth, ts, off42, off42)
2892 # Too few args.
2893 self.assertRaises(TypeError, meth)
2894
Tim Peters2a44a8d2003-01-23 20:53:10 +00002895 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002896 timestamp = 1000000000
2897 utcdatetime = datetime.utcfromtimestamp(timestamp)
2898 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2899 # But on some flavor of Mac, it's nowhere near that. So we can't have
2900 # any idea here what time that actually is, we can only test that
2901 # relative changes match.
2902 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2903 tz = FixedOffset(utcoffset, "tz", 0)
2904 expected = utcdatetime + utcoffset
2905 got = datetime.fromtimestamp(timestamp, tz)
2906 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002907
Tim Peters2a799bf2002-12-16 20:18:38 +00002908 def test_tzinfo_utcnow(self):
2909 meth = self.theclass.utcnow
2910 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2911 base = meth()
2912 # Try with and without naming the keyword; for whatever reason,
2913 # utcnow() doesn't accept a tzinfo argument.
2914 off42 = FixedOffset(42, "42")
2915 self.assertRaises(TypeError, meth, off42)
2916 self.assertRaises(TypeError, meth, tzinfo=off42)
2917
2918 def test_tzinfo_utcfromtimestamp(self):
2919 import time
2920 meth = self.theclass.utcfromtimestamp
2921 ts = time.time()
2922 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2923 base = meth(ts)
2924 # Try with and without naming the keyword; for whatever reason,
2925 # utcfromtimestamp() doesn't accept a tzinfo argument.
2926 off42 = FixedOffset(42, "42")
2927 self.assertRaises(TypeError, meth, ts, off42)
2928 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2929
2930 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002931 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002932 # DST flag.
2933 class DST(tzinfo):
2934 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002935 if isinstance(dstvalue, int):
2936 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002937 self.dstvalue = dstvalue
2938 def dst(self, dt):
2939 return self.dstvalue
2940
2941 cls = self.theclass
2942 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2943 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2944 t = d.timetuple()
2945 self.assertEqual(1, t.tm_year)
2946 self.assertEqual(1, t.tm_mon)
2947 self.assertEqual(1, t.tm_mday)
2948 self.assertEqual(10, t.tm_hour)
2949 self.assertEqual(20, t.tm_min)
2950 self.assertEqual(30, t.tm_sec)
2951 self.assertEqual(0, t.tm_wday)
2952 self.assertEqual(1, t.tm_yday)
2953 self.assertEqual(flag, t.tm_isdst)
2954
2955 # dst() returns wrong type.
2956 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2957
2958 # dst() at the edge.
2959 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2960 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2961
2962 # dst() out of range.
2963 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2964 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2965
2966 def test_utctimetuple(self):
2967 class DST(tzinfo):
2968 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002969 if isinstance(dstvalue, int):
2970 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002971 self.dstvalue = dstvalue
2972 def dst(self, dt):
2973 return self.dstvalue
2974
2975 cls = self.theclass
2976 # This can't work: DST didn't implement utcoffset.
2977 self.assertRaises(NotImplementedError,
2978 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2979
2980 class UOFS(DST):
2981 def __init__(self, uofs, dofs=None):
2982 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002983 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002984 def utcoffset(self, dt):
2985 return self.uofs
2986
2987 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2988 # in effect for a UTC time.
2989 for dstvalue in -33, 33, 0, None:
2990 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
2991 t = d.utctimetuple()
2992 self.assertEqual(d.year, t.tm_year)
2993 self.assertEqual(d.month, t.tm_mon)
2994 self.assertEqual(d.day, t.tm_mday)
2995 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
2996 self.assertEqual(13, t.tm_min)
2997 self.assertEqual(d.second, t.tm_sec)
2998 self.assertEqual(d.weekday(), t.tm_wday)
2999 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
3000 t.tm_yday)
3001 self.assertEqual(0, t.tm_isdst)
3002
3003 # At the edges, UTC adjustment can normalize into years out-of-range
3004 # for a datetime object. Ensure that a correct timetuple is
3005 # created anyway.
3006 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
3007 # That goes back 1 minute less than a full day.
3008 t = tiny.utctimetuple()
3009 self.assertEqual(t.tm_year, MINYEAR-1)
3010 self.assertEqual(t.tm_mon, 12)
3011 self.assertEqual(t.tm_mday, 31)
3012 self.assertEqual(t.tm_hour, 0)
3013 self.assertEqual(t.tm_min, 1)
3014 self.assertEqual(t.tm_sec, 37)
3015 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
3016 self.assertEqual(t.tm_isdst, 0)
3017
3018 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
3019 # That goes forward 1 minute less than a full day.
3020 t = huge.utctimetuple()
3021 self.assertEqual(t.tm_year, MAXYEAR+1)
3022 self.assertEqual(t.tm_mon, 1)
3023 self.assertEqual(t.tm_mday, 1)
3024 self.assertEqual(t.tm_hour, 23)
3025 self.assertEqual(t.tm_min, 58)
3026 self.assertEqual(t.tm_sec, 37)
3027 self.assertEqual(t.tm_yday, 1)
3028 self.assertEqual(t.tm_isdst, 0)
3029
3030 def test_tzinfo_isoformat(self):
3031 zero = FixedOffset(0, "+00:00")
3032 plus = FixedOffset(220, "+03:40")
3033 minus = FixedOffset(-231, "-03:51")
3034 unknown = FixedOffset(None, "")
3035
3036 cls = self.theclass
3037 datestr = '0001-02-03'
3038 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00003039 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00003040 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3041 timestr = '04:05:59' + (us and '.987001' or '')
3042 ofsstr = ofs is not None and d.tzname() or ''
3043 tailstr = timestr + ofsstr
3044 iso = d.isoformat()
3045 self.assertEqual(iso, datestr + 'T' + tailstr)
3046 self.assertEqual(iso, d.isoformat('T'))
3047 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00003048 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003049 self.assertEqual(str(d), datestr + ' ' + tailstr)
3050
Tim Peters12bf3392002-12-24 05:41:27 +00003051 def test_replace(self):
3052 cls = self.theclass
3053 z100 = FixedOffset(100, "+100")
3054 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3055 args = [1, 2, 3, 4, 5, 6, 7, z100]
3056 base = cls(*args)
3057 self.assertEqual(base, base.replace())
3058
3059 i = 0
3060 for name, newval in (("year", 2),
3061 ("month", 3),
3062 ("day", 4),
3063 ("hour", 5),
3064 ("minute", 6),
3065 ("second", 7),
3066 ("microsecond", 8),
3067 ("tzinfo", zm200)):
3068 newargs = args[:]
3069 newargs[i] = newval
3070 expected = cls(*newargs)
3071 got = base.replace(**{name: newval})
3072 self.assertEqual(expected, got)
3073 i += 1
3074
3075 # Ensure we can get rid of a tzinfo.
3076 self.assertEqual(base.tzname(), "+100")
3077 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003078 self.assertTrue(base2.tzinfo is None)
3079 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00003080
3081 # Ensure we can add one.
3082 base3 = base2.replace(tzinfo=z100)
3083 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003084 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00003085
3086 # Out of bounds.
3087 base = cls(2000, 2, 29)
3088 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00003089
Tim Peters80475bb2002-12-25 07:40:55 +00003090 def test_more_astimezone(self):
3091 # The inherited test_astimezone covered some trivial and error cases.
3092 fnone = FixedOffset(None, "None")
3093 f44m = FixedOffset(44, "44")
3094 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3095
Tim Peters10cadce2003-01-23 19:58:02 +00003096 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003097 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00003098 # Replacing with degenerate tzinfo raises an exception.
3099 self.assertRaises(ValueError, dt.astimezone, fnone)
3100 # Ditto with None tz.
3101 self.assertRaises(TypeError, dt.astimezone, None)
3102 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00003103 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003104 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00003105 self.assertEqual(x.date(), dt.date())
3106 self.assertEqual(x.time(), dt.time())
3107
3108 # Replacing with different tzinfo does adjust.
3109 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003110 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00003111 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3112 expected = dt - dt.utcoffset() # in effect, convert to UTC
3113 expected += fm5h.utcoffset(dt) # and from there to local time
3114 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3115 self.assertEqual(got.date(), expected.date())
3116 self.assertEqual(got.time(), expected.time())
3117 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003118 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00003119 self.assertEqual(got, expected)
3120
Tim Peters4c0db782002-12-26 05:01:19 +00003121 def test_aware_subtract(self):
3122 cls = self.theclass
3123
Tim Peters60c76e42002-12-27 00:41:11 +00003124 # Ensure that utcoffset() is ignored when the operands have the
3125 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00003126 class OperandDependentOffset(tzinfo):
3127 def utcoffset(self, t):
3128 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00003129 # d0 and d1 equal after adjustment
3130 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00003131 else:
Tim Peters397301e2003-01-02 21:28:08 +00003132 # d2 off in the weeds
3133 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00003134
3135 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3136 d0 = base.replace(minute=3)
3137 d1 = base.replace(minute=9)
3138 d2 = base.replace(minute=11)
3139 for x in d0, d1, d2:
3140 for y in d0, d1, d2:
3141 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00003142 expected = timedelta(minutes=x.minute - y.minute)
3143 self.assertEqual(got, expected)
3144
3145 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3146 # ignored.
3147 base = cls(8, 9, 10, 11, 12, 13, 14)
3148 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3149 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3150 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3151 for x in d0, d1, d2:
3152 for y in d0, d1, d2:
3153 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00003154 if (x is d0 or x is d1) and (y is d0 or y is d1):
3155 expected = timedelta(0)
3156 elif x is y is d2:
3157 expected = timedelta(0)
3158 elif x is d2:
3159 expected = timedelta(minutes=(11-59)-0)
3160 else:
3161 assert y is d2
3162 expected = timedelta(minutes=0-(11-59))
3163 self.assertEqual(got, expected)
3164
Tim Peters60c76e42002-12-27 00:41:11 +00003165 def test_mixed_compare(self):
3166 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00003167 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00003168 self.assertEqual(t1, t2)
3169 t2 = t2.replace(tzinfo=None)
3170 self.assertEqual(t1, t2)
3171 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3172 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003173 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3174 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003175
Tim Peters0bf60bd2003-01-08 20:40:01 +00003176 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003177 class Varies(tzinfo):
3178 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003179 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003180 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003181 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003182 return self.offset
3183
3184 v = Varies()
3185 t1 = t2.replace(tzinfo=v)
3186 t2 = t2.replace(tzinfo=v)
3187 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3188 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3189 self.assertEqual(t1, t2)
3190
3191 # But if they're not identical, it isn't ignored.
3192 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003193 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003194
Tim Petersa98924a2003-05-17 05:55:19 +00003195 def test_subclass_datetimetz(self):
3196
3197 class C(self.theclass):
3198 theAnswer = 42
3199
3200 def __new__(cls, *args, **kws):
3201 temp = kws.copy()
3202 extra = temp.pop('extra')
3203 result = self.theclass.__new__(cls, *args, **temp)
3204 result.extra = extra
3205 return result
3206
3207 def newmeth(self, start):
3208 return start + self.hour + self.year
3209
3210 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3211
3212 dt1 = self.theclass(*args)
3213 dt2 = C(*args, **{'extra': 7})
3214
3215 self.assertEqual(dt2.__class__, C)
3216 self.assertEqual(dt2.theAnswer, 42)
3217 self.assertEqual(dt2.extra, 7)
3218 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3219 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3220
Tim Peters621818b2002-12-29 23:44:49 +00003221# Pain to set up DST-aware tzinfo classes.
3222
3223def first_sunday_on_or_after(dt):
3224 days_to_go = 6 - dt.weekday()
3225 if days_to_go:
3226 dt += timedelta(days_to_go)
3227 return dt
3228
3229ZERO = timedelta(0)
Alexander Belopolskyca94f552010-06-17 18:30:34 +00003230MINUTE = timedelta(minutes=1)
Tim Peters621818b2002-12-29 23:44:49 +00003231HOUR = timedelta(hours=1)
3232DAY = timedelta(days=1)
3233# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3234DSTSTART = datetime(1, 4, 1, 2)
3235# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003236# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3237# being standard time on that day, there is no spelling in local time of
3238# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3239DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003240
3241class USTimeZone(tzinfo):
3242
3243 def __init__(self, hours, reprname, stdname, dstname):
3244 self.stdoffset = timedelta(hours=hours)
3245 self.reprname = reprname
3246 self.stdname = stdname
3247 self.dstname = dstname
3248
3249 def __repr__(self):
3250 return self.reprname
3251
3252 def tzname(self, dt):
3253 if self.dst(dt):
3254 return self.dstname
3255 else:
3256 return self.stdname
3257
3258 def utcoffset(self, dt):
3259 return self.stdoffset + self.dst(dt)
3260
3261 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003262 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003263 # An exception instead may be sensible here, in one or more of
3264 # the cases.
3265 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003266 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003267
3268 # Find first Sunday in April.
3269 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3270 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3271
3272 # Find last Sunday in October.
3273 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3274 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3275
Tim Peters621818b2002-12-29 23:44:49 +00003276 # Can't compare naive to aware objects, so strip the timezone from
3277 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003278 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003279 return HOUR
3280 else:
3281 return ZERO
3282
Tim Peters521fc152002-12-31 17:36:56 +00003283Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3284Central = USTimeZone(-6, "Central", "CST", "CDT")
3285Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3286Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003287utc_real = FixedOffset(0, "UTC", 0)
3288# For better test coverage, we want another flavor of UTC that's west of
3289# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003290utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003291
3292class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003293 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003294 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003295 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003296
Tim Peters0bf60bd2003-01-08 20:40:01 +00003297 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003298
Tim Peters521fc152002-12-31 17:36:56 +00003299 # Check a time that's inside DST.
3300 def checkinside(self, dt, tz, utc, dston, dstoff):
3301 self.assertEqual(dt.dst(), HOUR)
3302
3303 # Conversion to our own timezone is always an identity.
3304 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003305
3306 asutc = dt.astimezone(utc)
3307 there_and_back = asutc.astimezone(tz)
3308
3309 # Conversion to UTC and back isn't always an identity here,
3310 # because there are redundant spellings (in local time) of
3311 # UTC time when DST begins: the clock jumps from 1:59:59
3312 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3313 # make sense then. The classes above treat 2:MM:SS as
3314 # daylight time then (it's "after 2am"), really an alias
3315 # for 1:MM:SS standard time. The latter form is what
3316 # conversion back from UTC produces.
3317 if dt.date() == dston.date() and dt.hour == 2:
3318 # We're in the redundant hour, and coming back from
3319 # UTC gives the 1:MM:SS standard-time spelling.
3320 self.assertEqual(there_and_back + HOUR, dt)
3321 # Although during was considered to be in daylight
3322 # time, there_and_back is not.
3323 self.assertEqual(there_and_back.dst(), ZERO)
3324 # They're the same times in UTC.
3325 self.assertEqual(there_and_back.astimezone(utc),
3326 dt.astimezone(utc))
3327 else:
3328 # We're not in the redundant hour.
3329 self.assertEqual(dt, there_and_back)
3330
Tim Peters327098a2003-01-20 22:54:38 +00003331 # Because we have a redundant spelling when DST begins, there is
3332 # (unforunately) an hour when DST ends that can't be spelled at all in
3333 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3334 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3335 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3336 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3337 # expressed in local time. Nevertheless, we want conversion back
3338 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003339 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003340 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003341 if dt.date() == dstoff.date() and dt.hour == 0:
3342 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003343 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003344 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3345 nexthour_utc += HOUR
3346 nexthour_tz = nexthour_utc.astimezone(tz)
3347 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003348 else:
Tim Peters327098a2003-01-20 22:54:38 +00003349 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003350
3351 # Check a time that's outside DST.
3352 def checkoutside(self, dt, tz, utc):
3353 self.assertEqual(dt.dst(), ZERO)
3354
3355 # Conversion to our own timezone is always an identity.
3356 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003357
3358 # Converting to UTC and back is an identity too.
3359 asutc = dt.astimezone(utc)
3360 there_and_back = asutc.astimezone(tz)
3361 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003362
Tim Peters1024bf82002-12-30 17:09:40 +00003363 def convert_between_tz_and_utc(self, tz, utc):
3364 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003365 # Because 1:MM on the day DST ends is taken as being standard time,
3366 # there is no spelling in tz for the last hour of daylight time.
3367 # For purposes of the test, the last hour of DST is 0:MM, which is
3368 # taken as being daylight time (and 1:MM is taken as being standard
3369 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003370 dstoff = self.dstoff.replace(tzinfo=tz)
3371 for delta in (timedelta(weeks=13),
3372 DAY,
3373 HOUR,
3374 timedelta(minutes=1),
3375 timedelta(microseconds=1)):
3376
Tim Peters521fc152002-12-31 17:36:56 +00003377 self.checkinside(dston, tz, utc, dston, dstoff)
3378 for during in dston + delta, dstoff - delta:
3379 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003380
Tim Peters521fc152002-12-31 17:36:56 +00003381 self.checkoutside(dstoff, tz, utc)
3382 for outside in dston - delta, dstoff + delta:
3383 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003384
Tim Peters621818b2002-12-29 23:44:49 +00003385 def test_easy(self):
3386 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003387 self.convert_between_tz_and_utc(Eastern, utc_real)
3388 self.convert_between_tz_and_utc(Pacific, utc_real)
3389 self.convert_between_tz_and_utc(Eastern, utc_fake)
3390 self.convert_between_tz_and_utc(Pacific, utc_fake)
3391 # The next is really dancing near the edge. It works because
3392 # Pacific and Eastern are far enough apart that their "problem
3393 # hours" don't overlap.
3394 self.convert_between_tz_and_utc(Eastern, Pacific)
3395 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003396 # OTOH, these fail! Don't enable them. The difficulty is that
3397 # the edge case tests assume that every hour is representable in
3398 # the "utc" class. This is always true for a fixed-offset tzinfo
3399 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3400 # For these adjacent DST-aware time zones, the range of time offsets
3401 # tested ends up creating hours in the one that aren't representable
3402 # in the other. For the same reason, we would see failures in the
3403 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3404 # offset deltas in convert_between_tz_and_utc().
3405 #
3406 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3407 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003408
Tim Petersf3615152003-01-01 21:51:37 +00003409 def test_tricky(self):
3410 # 22:00 on day before daylight starts.
3411 fourback = self.dston - timedelta(hours=4)
3412 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003413 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003414 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3415 # 2", we should get the 3 spelling.
3416 # If we plug 22:00 the day before into Eastern, it "looks like std
3417 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3418 # to 22:00 lands on 2:00, which makes no sense in local time (the
3419 # local clock jumps from 1 to 3). The point here is to make sure we
3420 # get the 3 spelling.
3421 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003422 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003423 self.assertEqual(expected, got)
3424
3425 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3426 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003427 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003428 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3429 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3430 # spelling.
3431 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003432 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003433 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003434
Tim Petersadf64202003-01-04 06:03:15 +00003435 # Now on the day DST ends, we want "repeat an hour" behavior.
3436 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3437 # EST 23:MM 0:MM 1:MM 2:MM
3438 # EDT 0:MM 1:MM 2:MM 3:MM
3439 # wall 0:MM 1:MM 1:MM 2:MM against these
3440 for utc in utc_real, utc_fake:
3441 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003442 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003443 # Convert that to UTC.
3444 first_std_hour -= tz.utcoffset(None)
3445 # Adjust for possibly fake UTC.
3446 asutc = first_std_hour + utc.utcoffset(None)
3447 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3448 # tz=Eastern.
3449 asutcbase = asutc.replace(tzinfo=utc)
3450 for tzhour in (0, 1, 1, 2):
3451 expectedbase = self.dstoff.replace(hour=tzhour)
3452 for minute in 0, 30, 59:
3453 expected = expectedbase.replace(minute=minute)
3454 asutc = asutcbase.replace(minute=minute)
3455 astz = asutc.astimezone(tz)
3456 self.assertEqual(astz.replace(tzinfo=None), expected)
3457 asutcbase += HOUR
3458
3459
Tim Peters710fb152003-01-02 19:35:54 +00003460 def test_bogus_dst(self):
3461 class ok(tzinfo):
3462 def utcoffset(self, dt): return HOUR
3463 def dst(self, dt): return HOUR
3464
3465 now = self.theclass.now().replace(tzinfo=utc_real)
3466 # Doesn't blow up.
3467 now.astimezone(ok())
3468
3469 # Does blow up.
3470 class notok(ok):
3471 def dst(self, dt): return None
3472 self.assertRaises(ValueError, now.astimezone, notok())
3473
Tim Peters52dcce22003-01-23 16:36:11 +00003474 def test_fromutc(self):
3475 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3476 now = datetime.utcnow().replace(tzinfo=utc_real)
3477 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3478 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3479 enow = Eastern.fromutc(now) # doesn't blow up
3480 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3481 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3482 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3483
3484 # Always converts UTC to standard time.
3485 class FauxUSTimeZone(USTimeZone):
3486 def fromutc(self, dt):
3487 return dt + self.stdoffset
3488 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3489
3490 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3491 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3492 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3493
3494 # Check around DST start.
3495 start = self.dston.replace(hour=4, tzinfo=Eastern)
3496 fstart = start.replace(tzinfo=FEastern)
3497 for wall in 23, 0, 1, 3, 4, 5:
3498 expected = start.replace(hour=wall)
3499 if wall == 23:
3500 expected -= timedelta(days=1)
3501 got = Eastern.fromutc(start)
3502 self.assertEqual(expected, got)
3503
3504 expected = fstart + FEastern.stdoffset
3505 got = FEastern.fromutc(fstart)
3506 self.assertEqual(expected, got)
3507
3508 # Ensure astimezone() calls fromutc() too.
3509 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3510 self.assertEqual(expected, got)
3511
3512 start += HOUR
3513 fstart += HOUR
3514
3515 # Check around DST end.
3516 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3517 fstart = start.replace(tzinfo=FEastern)
3518 for wall in 0, 1, 1, 2, 3, 4:
3519 expected = start.replace(hour=wall)
3520 got = Eastern.fromutc(start)
3521 self.assertEqual(expected, got)
3522
3523 expected = fstart + FEastern.stdoffset
3524 got = FEastern.fromutc(fstart)
3525 self.assertEqual(expected, got)
3526
3527 # Ensure astimezone() calls fromutc() too.
3528 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3529 self.assertEqual(expected, got)
3530
3531 start += HOUR
3532 fstart += HOUR
3533
Tim Peters710fb152003-01-02 19:35:54 +00003534
Tim Peters528ca532004-09-16 01:30:50 +00003535#############################################################################
3536# oddballs
3537
3538class Oddballs(unittest.TestCase):
3539
3540 def test_bug_1028306(self):
3541 # Trying to compare a date to a datetime should act like a mixed-
3542 # type comparison, despite that datetime is a subclass of date.
3543 as_date = date.today()
3544 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003545 self.assertTrue(as_date != as_datetime)
3546 self.assertTrue(as_datetime != as_date)
3547 self.assertTrue(not as_date == as_datetime)
3548 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003549 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3550 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3551 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3552 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3553 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3554 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3555 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3556 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3557
3558 # Neverthelss, comparison should work with the base-class (date)
3559 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003560 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003561 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003562 as_different = as_datetime.replace(day= different_day)
3563 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003564
3565 # And date should compare with other subclasses of date. If a
3566 # subclass wants to stop this, it's up to the subclass to do so.
3567 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3568 self.assertEqual(as_date, date_sc)
3569 self.assertEqual(date_sc, as_date)
3570
3571 # Ditto for datetimes.
3572 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3573 as_date.day, 0, 0, 0)
3574 self.assertEqual(as_datetime, datetime_sc)
3575 self.assertEqual(datetime_sc, as_datetime)
3576
Tim Peters2a799bf2002-12-16 20:18:38 +00003577def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003578 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003579
3580if __name__ == "__main__":
3581 test_main()