blob: 42c18e695310ab2621a24e10ee07168570e7965f [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):
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001103 # XXX: Should min and max respect subclassing?
1104 if issubclass(self.theclass, datetime):
1105 expected_class = datetime
1106 else:
1107 expected_class = date
1108 self.assertIsInstance(self.theclass.min, expected_class)
1109 self.assertIsInstance(self.theclass.max, expected_class)
Ezio Melottie9615932010-01-24 19:26:24 +00001110 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001111 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001112
1113 def test_extreme_timedelta(self):
1114 big = self.theclass.max - self.theclass.min
1115 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1116 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1117 # n == 315537897599999999 ~= 2**58.13
1118 justasbig = timedelta(0, 0, n)
1119 self.assertEqual(big, justasbig)
1120 self.assertEqual(self.theclass.min + big, self.theclass.max)
1121 self.assertEqual(self.theclass.max - big, self.theclass.min)
1122
1123 def test_timetuple(self):
1124 for i in range(7):
1125 # January 2, 1956 is a Monday (0)
1126 d = self.theclass(1956, 1, 2+i)
1127 t = d.timetuple()
1128 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1129 # February 1, 1956 is a Wednesday (2)
1130 d = self.theclass(1956, 2, 1+i)
1131 t = d.timetuple()
1132 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1133 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1134 # of the year.
1135 d = self.theclass(1956, 3, 1+i)
1136 t = d.timetuple()
1137 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1138 self.assertEqual(t.tm_year, 1956)
1139 self.assertEqual(t.tm_mon, 3)
1140 self.assertEqual(t.tm_mday, 1+i)
1141 self.assertEqual(t.tm_hour, 0)
1142 self.assertEqual(t.tm_min, 0)
1143 self.assertEqual(t.tm_sec, 0)
1144 self.assertEqual(t.tm_wday, (3+i)%7)
1145 self.assertEqual(t.tm_yday, 61+i)
1146 self.assertEqual(t.tm_isdst, -1)
1147
1148 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001149 args = 6, 7, 23
1150 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001151 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001152 green = pickler.dumps(orig, proto)
1153 derived = unpickler.loads(green)
1154 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001155
1156 def test_compare(self):
1157 t1 = self.theclass(2, 3, 4)
1158 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001159 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001160 self.assertTrue(t1 <= t2)
1161 self.assertTrue(t1 >= t2)
1162 self.assertTrue(not t1 != t2)
1163 self.assertTrue(not t1 < t2)
1164 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001165
1166 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1167 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001168 self.assertTrue(t1 < t2)
1169 self.assertTrue(t2 > t1)
1170 self.assertTrue(t1 <= t2)
1171 self.assertTrue(t2 >= t1)
1172 self.assertTrue(t1 != t2)
1173 self.assertTrue(t2 != t1)
1174 self.assertTrue(not t1 == t2)
1175 self.assertTrue(not t2 == t1)
1176 self.assertTrue(not t1 > t2)
1177 self.assertTrue(not t2 < t1)
1178 self.assertTrue(not t1 >= t2)
1179 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001180
Tim Peters68124bb2003-02-08 03:46:31 +00001181 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001182 self.assertEqual(t1 == badarg, False)
1183 self.assertEqual(t1 != badarg, True)
1184 self.assertEqual(badarg == t1, False)
1185 self.assertEqual(badarg != t1, True)
1186
Tim Peters2a799bf2002-12-16 20:18:38 +00001187 self.assertRaises(TypeError, lambda: t1 < badarg)
1188 self.assertRaises(TypeError, lambda: t1 > badarg)
1189 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001190 self.assertRaises(TypeError, lambda: badarg <= t1)
1191 self.assertRaises(TypeError, lambda: badarg < t1)
1192 self.assertRaises(TypeError, lambda: badarg > t1)
1193 self.assertRaises(TypeError, lambda: badarg >= t1)
1194
Tim Peters8d81a012003-01-24 22:36:34 +00001195 def test_mixed_compare(self):
1196 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001197
1198 # Our class can be compared for equality to other classes
1199 self.assertEqual(our == 1, False)
1200 self.assertEqual(1 == our, False)
1201 self.assertEqual(our != 1, True)
1202 self.assertEqual(1 != our, True)
1203
1204 # But the ordering is undefined
1205 self.assertRaises(TypeError, lambda: our < 1)
1206 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001207
Guido van Rossum19960592006-08-24 17:29:38 +00001208 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001209
Guido van Rossum19960592006-08-24 17:29:38 +00001210 class SomeClass:
1211 pass
1212
1213 their = SomeClass()
1214 self.assertEqual(our == their, False)
1215 self.assertEqual(their == our, False)
1216 self.assertEqual(our != their, True)
1217 self.assertEqual(their != our, True)
1218 self.assertRaises(TypeError, lambda: our < their)
1219 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001220
Guido van Rossum19960592006-08-24 17:29:38 +00001221 # However, if the other class explicitly defines ordering
1222 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001223
Guido van Rossum19960592006-08-24 17:29:38 +00001224 class LargerThanAnything:
1225 def __lt__(self, other):
1226 return False
1227 def __le__(self, other):
1228 return isinstance(other, LargerThanAnything)
1229 def __eq__(self, other):
1230 return isinstance(other, LargerThanAnything)
1231 def __ne__(self, other):
1232 return not isinstance(other, LargerThanAnything)
1233 def __gt__(self, other):
1234 return not isinstance(other, LargerThanAnything)
1235 def __ge__(self, other):
1236 return True
1237
1238 their = LargerThanAnything()
1239 self.assertEqual(our == their, False)
1240 self.assertEqual(their == our, False)
1241 self.assertEqual(our != their, True)
1242 self.assertEqual(their != our, True)
1243 self.assertEqual(our < their, True)
1244 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001245
Tim Peters2a799bf2002-12-16 20:18:38 +00001246 def test_bool(self):
1247 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001248 self.assertTrue(self.theclass.min)
1249 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001250
Guido van Rossum04110fb2007-08-24 16:32:05 +00001251 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001252 # For nasty technical reasons, we can't handle years before 1900.
1253 cls = self.theclass
1254 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1255 for y in 1, 49, 51, 99, 100, 1000, 1899:
1256 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001257
1258 def test_replace(self):
1259 cls = self.theclass
1260 args = [1, 2, 3]
1261 base = cls(*args)
1262 self.assertEqual(base, base.replace())
1263
1264 i = 0
1265 for name, newval in (("year", 2),
1266 ("month", 3),
1267 ("day", 4)):
1268 newargs = args[:]
1269 newargs[i] = newval
1270 expected = cls(*newargs)
1271 got = base.replace(**{name: newval})
1272 self.assertEqual(expected, got)
1273 i += 1
1274
1275 # Out of bounds.
1276 base = cls(2000, 2, 29)
1277 self.assertRaises(ValueError, base.replace, year=2001)
1278
Tim Petersa98924a2003-05-17 05:55:19 +00001279 def test_subclass_date(self):
1280
1281 class C(self.theclass):
1282 theAnswer = 42
1283
1284 def __new__(cls, *args, **kws):
1285 temp = kws.copy()
1286 extra = temp.pop('extra')
1287 result = self.theclass.__new__(cls, *args, **temp)
1288 result.extra = extra
1289 return result
1290
1291 def newmeth(self, start):
1292 return start + self.year + self.month
1293
1294 args = 2003, 4, 14
1295
1296 dt1 = self.theclass(*args)
1297 dt2 = C(*args, **{'extra': 7})
1298
1299 self.assertEqual(dt2.__class__, C)
1300 self.assertEqual(dt2.theAnswer, 42)
1301 self.assertEqual(dt2.extra, 7)
1302 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1303 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1304
Tim Peters604c0132004-06-07 23:04:33 +00001305 def test_pickling_subclass_date(self):
1306
1307 args = 6, 7, 23
1308 orig = SubclassDate(*args)
1309 for pickler, unpickler, proto in pickle_choices:
1310 green = pickler.dumps(orig, proto)
1311 derived = unpickler.loads(green)
1312 self.assertEqual(orig, derived)
1313
Tim Peters3f606292004-03-21 23:38:41 +00001314 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001315 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001316 # This is a low-overhead backdoor. A user can (by intent or
1317 # mistake) pass a string directly, which (if it's the right length)
1318 # will get treated like a pickle, and bypass the normal sanity
1319 # checks in the constructor. This can create insane objects.
1320 # The constructor doesn't want to burn the time to validate all
1321 # fields, but does check the month field. This stops, e.g.,
1322 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001323 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001324 if not issubclass(self.theclass, datetime):
1325 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001326 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001327 self.assertRaises(TypeError, self.theclass,
1328 base[:2] + month_byte + base[3:])
1329 for ord_byte in range(1, 13):
1330 # This shouldn't blow up because of the month byte alone. If
1331 # the implementation changes to do more-careful checking, it may
1332 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001333 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001334
Tim Peters2a799bf2002-12-16 20:18:38 +00001335#############################################################################
1336# datetime tests
1337
Tim Peters604c0132004-06-07 23:04:33 +00001338class SubclassDatetime(datetime):
1339 sub_var = 1
1340
Tim Peters2a799bf2002-12-16 20:18:38 +00001341class TestDateTime(TestDate):
1342
1343 theclass = datetime
1344
1345 def test_basic_attributes(self):
1346 dt = self.theclass(2002, 3, 1, 12, 0)
1347 self.assertEqual(dt.year, 2002)
1348 self.assertEqual(dt.month, 3)
1349 self.assertEqual(dt.day, 1)
1350 self.assertEqual(dt.hour, 12)
1351 self.assertEqual(dt.minute, 0)
1352 self.assertEqual(dt.second, 0)
1353 self.assertEqual(dt.microsecond, 0)
1354
1355 def test_basic_attributes_nonzero(self):
1356 # Make sure all attributes are non-zero so bugs in
1357 # bit-shifting access show up.
1358 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1359 self.assertEqual(dt.year, 2002)
1360 self.assertEqual(dt.month, 3)
1361 self.assertEqual(dt.day, 1)
1362 self.assertEqual(dt.hour, 12)
1363 self.assertEqual(dt.minute, 59)
1364 self.assertEqual(dt.second, 59)
1365 self.assertEqual(dt.microsecond, 8000)
1366
1367 def test_roundtrip(self):
1368 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1369 self.theclass.now()):
1370 # Verify dt -> string -> datetime identity.
1371 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001372 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001373 s = s[9:]
1374 dt2 = eval(s)
1375 self.assertEqual(dt, dt2)
1376
1377 # Verify identity via reconstructing from pieces.
1378 dt2 = self.theclass(dt.year, dt.month, dt.day,
1379 dt.hour, dt.minute, dt.second,
1380 dt.microsecond)
1381 self.assertEqual(dt, dt2)
1382
1383 def test_isoformat(self):
1384 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1385 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1386 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1387 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001388 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001389 # str is ISO format with the separator forced to a blank.
1390 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1391
1392 t = self.theclass(2, 3, 2)
1393 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1394 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1395 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1396 # str is ISO format with the separator forced to a blank.
1397 self.assertEqual(str(t), "0002-03-02 00:00:00")
1398
Eric Smith1ba31142007-09-11 18:06:02 +00001399 def test_format(self):
1400 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001401 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001402
1403 # check that a derived class's __str__() gets called
1404 class A(self.theclass):
1405 def __str__(self):
1406 return 'A'
1407 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001408 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001409
1410 # check that a derived class's strftime gets called
1411 class B(self.theclass):
1412 def strftime(self, format_spec):
1413 return 'B'
1414 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001415 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001416
1417 for fmt in ["m:%m d:%d y:%y",
1418 "m:%m d:%d y:%y H:%H M:%M S:%S",
1419 "%z %Z",
1420 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001421 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1422 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1423 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001424
Tim Peters2a799bf2002-12-16 20:18:38 +00001425 def test_more_ctime(self):
1426 # Test fields that TestDate doesn't touch.
1427 import time
1428
1429 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1430 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1431 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1432 # out. The difference is that t.ctime() produces " 2" for the day,
1433 # but platform ctime() produces "02" for the day. According to
1434 # C99, t.ctime() is correct here.
1435 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1436
1437 # So test a case where that difference doesn't matter.
1438 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1439 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1440
1441 def test_tz_independent_comparing(self):
1442 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1443 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1444 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1445 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001446 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001447
1448 # Make sure comparison doesn't forget microseconds, and isn't done
1449 # via comparing a float timestamp (an IEEE double doesn't have enough
1450 # precision to span microsecond resolution across years 1 thru 9999,
1451 # so comparing via timestamp necessarily calls some distinct values
1452 # equal).
1453 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1454 us = timedelta(microseconds=1)
1455 dt2 = dt1 + us
1456 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001457 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001458
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001459 def test_strftime_with_bad_tzname_replace(self):
1460 # verify ok if tzinfo.tzname().replace() returns a non-string
1461 class MyTzInfo(FixedOffset):
1462 def tzname(self, dt):
1463 class MyStr(str):
1464 def replace(self, *args):
1465 return None
1466 return MyStr('name')
1467 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1468 self.assertRaises(TypeError, t.strftime, '%Z')
1469
Tim Peters2a799bf2002-12-16 20:18:38 +00001470 def test_bad_constructor_arguments(self):
1471 # bad years
1472 self.theclass(MINYEAR, 1, 1) # no exception
1473 self.theclass(MAXYEAR, 1, 1) # no exception
1474 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1475 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1476 # bad months
1477 self.theclass(2000, 1, 1) # no exception
1478 self.theclass(2000, 12, 1) # no exception
1479 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1480 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1481 # bad days
1482 self.theclass(2000, 2, 29) # no exception
1483 self.theclass(2004, 2, 29) # no exception
1484 self.theclass(2400, 2, 29) # no exception
1485 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1486 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1487 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1488 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1489 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1490 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1491 # bad hours
1492 self.theclass(2000, 1, 31, 0) # no exception
1493 self.theclass(2000, 1, 31, 23) # no exception
1494 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1495 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1496 # bad minutes
1497 self.theclass(2000, 1, 31, 23, 0) # no exception
1498 self.theclass(2000, 1, 31, 23, 59) # no exception
1499 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1500 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1501 # bad seconds
1502 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1503 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1504 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1505 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1506 # bad microseconds
1507 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1508 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1509 self.assertRaises(ValueError, self.theclass,
1510 2000, 1, 31, 23, 59, 59, -1)
1511 self.assertRaises(ValueError, self.theclass,
1512 2000, 1, 31, 23, 59, 59,
1513 1000000)
1514
1515 def test_hash_equality(self):
1516 d = self.theclass(2000, 12, 31, 23, 30, 17)
1517 e = self.theclass(2000, 12, 31, 23, 30, 17)
1518 self.assertEqual(d, e)
1519 self.assertEqual(hash(d), hash(e))
1520
1521 dic = {d: 1}
1522 dic[e] = 2
1523 self.assertEqual(len(dic), 1)
1524 self.assertEqual(dic[d], 2)
1525 self.assertEqual(dic[e], 2)
1526
1527 d = self.theclass(2001, 1, 1, 0, 5, 17)
1528 e = self.theclass(2001, 1, 1, 0, 5, 17)
1529 self.assertEqual(d, e)
1530 self.assertEqual(hash(d), hash(e))
1531
1532 dic = {d: 1}
1533 dic[e] = 2
1534 self.assertEqual(len(dic), 1)
1535 self.assertEqual(dic[d], 2)
1536 self.assertEqual(dic[e], 2)
1537
1538 def test_computations(self):
1539 a = self.theclass(2002, 1, 31)
1540 b = self.theclass(1956, 1, 31)
1541 diff = a-b
1542 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1543 self.assertEqual(diff.seconds, 0)
1544 self.assertEqual(diff.microseconds, 0)
1545 a = self.theclass(2002, 3, 2, 17, 6)
1546 millisec = timedelta(0, 0, 1000)
1547 hour = timedelta(0, 3600)
1548 day = timedelta(1)
1549 week = timedelta(7)
1550 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1551 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1552 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1553 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1554 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1555 self.assertEqual(a - hour, a + -hour)
1556 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1557 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1558 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1559 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1560 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1561 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1562 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1563 self.assertEqual((a + week) - a, week)
1564 self.assertEqual((a + day) - a, day)
1565 self.assertEqual((a + hour) - a, hour)
1566 self.assertEqual((a + millisec) - a, millisec)
1567 self.assertEqual((a - week) - a, -week)
1568 self.assertEqual((a - day) - a, -day)
1569 self.assertEqual((a - hour) - a, -hour)
1570 self.assertEqual((a - millisec) - a, -millisec)
1571 self.assertEqual(a - (a + week), -week)
1572 self.assertEqual(a - (a + day), -day)
1573 self.assertEqual(a - (a + hour), -hour)
1574 self.assertEqual(a - (a + millisec), -millisec)
1575 self.assertEqual(a - (a - week), week)
1576 self.assertEqual(a - (a - day), day)
1577 self.assertEqual(a - (a - hour), hour)
1578 self.assertEqual(a - (a - millisec), millisec)
1579 self.assertEqual(a + (week + day + hour + millisec),
1580 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1581 self.assertEqual(a + (week + day + hour + millisec),
1582 (((a + week) + day) + hour) + millisec)
1583 self.assertEqual(a - (week + day + hour + millisec),
1584 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1585 self.assertEqual(a - (week + day + hour + millisec),
1586 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001587 # Add/sub ints or floats should be illegal
1588 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001589 self.assertRaises(TypeError, lambda: a+i)
1590 self.assertRaises(TypeError, lambda: a-i)
1591 self.assertRaises(TypeError, lambda: i+a)
1592 self.assertRaises(TypeError, lambda: i-a)
1593
1594 # delta - datetime is senseless.
1595 self.assertRaises(TypeError, lambda: day - a)
1596 # mixing datetime and (delta or datetime) via * or // is senseless
1597 self.assertRaises(TypeError, lambda: day * a)
1598 self.assertRaises(TypeError, lambda: a * day)
1599 self.assertRaises(TypeError, lambda: day // a)
1600 self.assertRaises(TypeError, lambda: a // day)
1601 self.assertRaises(TypeError, lambda: a * a)
1602 self.assertRaises(TypeError, lambda: a // a)
1603 # datetime + datetime is senseless
1604 self.assertRaises(TypeError, lambda: a + a)
1605
1606 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001607 args = 6, 7, 23, 20, 59, 1, 64**2
1608 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001609 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001610 green = pickler.dumps(orig, proto)
1611 derived = unpickler.loads(green)
1612 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001613
Guido van Rossum275666f2003-02-07 21:49:01 +00001614 def test_more_pickling(self):
1615 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1616 s = pickle.dumps(a)
1617 b = pickle.loads(s)
1618 self.assertEqual(b.year, 2003)
1619 self.assertEqual(b.month, 2)
1620 self.assertEqual(b.day, 7)
1621
Tim Peters604c0132004-06-07 23:04:33 +00001622 def test_pickling_subclass_datetime(self):
1623 args = 6, 7, 23, 20, 59, 1, 64**2
1624 orig = SubclassDatetime(*args)
1625 for pickler, unpickler, proto in pickle_choices:
1626 green = pickler.dumps(orig, proto)
1627 derived = unpickler.loads(green)
1628 self.assertEqual(orig, derived)
1629
Tim Peters2a799bf2002-12-16 20:18:38 +00001630 def test_more_compare(self):
1631 # The test_compare() inherited from TestDate covers the error cases.
1632 # We just want to test lexicographic ordering on the members datetime
1633 # has that date lacks.
1634 args = [2000, 11, 29, 20, 58, 16, 999998]
1635 t1 = self.theclass(*args)
1636 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001637 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001638 self.assertTrue(t1 <= t2)
1639 self.assertTrue(t1 >= t2)
1640 self.assertTrue(not t1 != t2)
1641 self.assertTrue(not t1 < t2)
1642 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001643
1644 for i in range(len(args)):
1645 newargs = args[:]
1646 newargs[i] = args[i] + 1
1647 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001648 self.assertTrue(t1 < t2)
1649 self.assertTrue(t2 > t1)
1650 self.assertTrue(t1 <= t2)
1651 self.assertTrue(t2 >= t1)
1652 self.assertTrue(t1 != t2)
1653 self.assertTrue(t2 != t1)
1654 self.assertTrue(not t1 == t2)
1655 self.assertTrue(not t2 == t1)
1656 self.assertTrue(not t1 > t2)
1657 self.assertTrue(not t2 < t1)
1658 self.assertTrue(not t1 >= t2)
1659 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001660
1661
1662 # A helper for timestamp constructor tests.
1663 def verify_field_equality(self, expected, got):
1664 self.assertEqual(expected.tm_year, got.year)
1665 self.assertEqual(expected.tm_mon, got.month)
1666 self.assertEqual(expected.tm_mday, got.day)
1667 self.assertEqual(expected.tm_hour, got.hour)
1668 self.assertEqual(expected.tm_min, got.minute)
1669 self.assertEqual(expected.tm_sec, got.second)
1670
1671 def test_fromtimestamp(self):
1672 import time
1673
1674 ts = time.time()
1675 expected = time.localtime(ts)
1676 got = self.theclass.fromtimestamp(ts)
1677 self.verify_field_equality(expected, got)
1678
1679 def test_utcfromtimestamp(self):
1680 import time
1681
1682 ts = time.time()
1683 expected = time.gmtime(ts)
1684 got = self.theclass.utcfromtimestamp(ts)
1685 self.verify_field_equality(expected, got)
1686
Thomas Wouters477c8d52006-05-27 19:21:47 +00001687 def test_microsecond_rounding(self):
1688 # Test whether fromtimestamp "rounds up" floats that are less
1689 # than one microsecond smaller than an integer.
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001690 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1691 self.theclass.fromtimestamp(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001692
Tim Peters1b6f7a92004-06-20 02:50:16 +00001693 def test_insane_fromtimestamp(self):
1694 # It's possible that some platform maps time_t to double,
1695 # and that this test will fail there. This test should
1696 # exempt such platforms (provided they return reasonable
1697 # results!).
1698 for insane in -1e200, 1e200:
1699 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1700 insane)
1701
1702 def test_insane_utcfromtimestamp(self):
1703 # It's possible that some platform maps time_t to double,
1704 # and that this test will fail there. This test should
1705 # exempt such platforms (provided they return reasonable
1706 # results!).
1707 for insane in -1e200, 1e200:
1708 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1709 insane)
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001710 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001711 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001712 # The result is tz-dependent; at least test that this doesn't
1713 # fail (like it did before bug 1646728 was fixed).
1714 self.theclass.fromtimestamp(-1.05)
1715
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001716 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001717 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001718 d = self.theclass.utcfromtimestamp(-1.05)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001719 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001720
Tim Peters2a799bf2002-12-16 20:18:38 +00001721 def test_utcnow(self):
1722 import time
1723
1724 # Call it a success if utcnow() and utcfromtimestamp() are within
1725 # a second of each other.
1726 tolerance = timedelta(seconds=1)
1727 for dummy in range(3):
1728 from_now = self.theclass.utcnow()
1729 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1730 if abs(from_timestamp - from_now) <= tolerance:
1731 break
1732 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001733 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001734
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001735 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001736 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001737
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001738 string = '2004-12-01 13:02:47.197'
1739 format = '%Y-%m-%d %H:%M:%S.%f'
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001740 expected = _strptime._strptime_datetime(self.theclass, string, format)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001741 got = self.theclass.strptime(string, format)
1742 self.assertEqual(expected, got)
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001743 self.assertIs(type(expected), self.theclass)
1744 self.assertIs(type(got), self.theclass)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001745
Alexander Belopolskyca94f552010-06-17 18:30:34 +00001746 strptime = self.theclass.strptime
1747 self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
1748 self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
1749 # Only local timezone and UTC are supported
1750 for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
1751 (-_time.timezone, _time.tzname[0])):
1752 if tzseconds < 0:
1753 sign = '-'
1754 seconds = -tzseconds
1755 else:
1756 sign ='+'
1757 seconds = tzseconds
1758 hours, minutes = divmod(seconds//60, 60)
1759 dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
1760 dt = strptime(dtstr, "%z %Z")
1761 self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
1762 self.assertEqual(dt.tzname(), tzname)
1763 # Can produce inconsistent datetime
1764 dtstr, fmt = "+1234 UTC", "%z %Z"
1765 dt = strptime(dtstr, fmt)
1766 self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
1767 self.assertEqual(dt.tzname(), 'UTC')
1768 # yet will roundtrip
1769 self.assertEqual(dt.strftime(fmt), dtstr)
1770
1771 # Produce naive datetime if no %z is provided
1772 self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
1773
1774 with self.assertRaises(ValueError): strptime("-2400", "%z")
1775 with self.assertRaises(ValueError): strptime("-000", "%z")
1776
Tim Peters2a799bf2002-12-16 20:18:38 +00001777 def test_more_timetuple(self):
1778 # This tests fields beyond those tested by the TestDate.test_timetuple.
1779 t = self.theclass(2004, 12, 31, 6, 22, 33)
1780 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1781 self.assertEqual(t.timetuple(),
1782 (t.year, t.month, t.day,
1783 t.hour, t.minute, t.second,
1784 t.weekday(),
1785 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1786 -1))
1787 tt = t.timetuple()
1788 self.assertEqual(tt.tm_year, t.year)
1789 self.assertEqual(tt.tm_mon, t.month)
1790 self.assertEqual(tt.tm_mday, t.day)
1791 self.assertEqual(tt.tm_hour, t.hour)
1792 self.assertEqual(tt.tm_min, t.minute)
1793 self.assertEqual(tt.tm_sec, t.second)
1794 self.assertEqual(tt.tm_wday, t.weekday())
1795 self.assertEqual(tt.tm_yday, t.toordinal() -
1796 date(t.year, 1, 1).toordinal() + 1)
1797 self.assertEqual(tt.tm_isdst, -1)
1798
1799 def test_more_strftime(self):
1800 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001801 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1802 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1803 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001804
1805 def test_extract(self):
1806 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1807 self.assertEqual(dt.date(), date(2002, 3, 4))
1808 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1809
1810 def test_combine(self):
1811 d = date(2002, 3, 4)
1812 t = time(18, 45, 3, 1234)
1813 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1814 combine = self.theclass.combine
1815 dt = combine(d, t)
1816 self.assertEqual(dt, expected)
1817
1818 dt = combine(time=t, date=d)
1819 self.assertEqual(dt, expected)
1820
1821 self.assertEqual(d, dt.date())
1822 self.assertEqual(t, dt.time())
1823 self.assertEqual(dt, combine(dt.date(), dt.time()))
1824
1825 self.assertRaises(TypeError, combine) # need an arg
1826 self.assertRaises(TypeError, combine, d) # need two args
1827 self.assertRaises(TypeError, combine, t, d) # args reversed
1828 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1829 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1830
Tim Peters12bf3392002-12-24 05:41:27 +00001831 def test_replace(self):
1832 cls = self.theclass
1833 args = [1, 2, 3, 4, 5, 6, 7]
1834 base = cls(*args)
1835 self.assertEqual(base, base.replace())
1836
1837 i = 0
1838 for name, newval in (("year", 2),
1839 ("month", 3),
1840 ("day", 4),
1841 ("hour", 5),
1842 ("minute", 6),
1843 ("second", 7),
1844 ("microsecond", 8)):
1845 newargs = args[:]
1846 newargs[i] = newval
1847 expected = cls(*newargs)
1848 got = base.replace(**{name: newval})
1849 self.assertEqual(expected, got)
1850 i += 1
1851
1852 # Out of bounds.
1853 base = cls(2000, 2, 29)
1854 self.assertRaises(ValueError, base.replace, year=2001)
1855
Tim Peters80475bb2002-12-25 07:40:55 +00001856 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001857 # Pretty boring! The TZ test is more interesting here. astimezone()
1858 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001859 dt = self.theclass.now()
1860 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001861 self.assertRaises(TypeError, dt.astimezone) # not enough args
1862 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1863 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001864 self.assertRaises(ValueError, dt.astimezone, f) # naive
1865 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001866
Tim Peters52dcce22003-01-23 16:36:11 +00001867 class Bogus(tzinfo):
1868 def utcoffset(self, dt): return None
1869 def dst(self, dt): return timedelta(0)
1870 bog = Bogus()
1871 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1872
1873 class AlsoBogus(tzinfo):
1874 def utcoffset(self, dt): return timedelta(0)
1875 def dst(self, dt): return None
1876 alsobog = AlsoBogus()
1877 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001878
Tim Petersa98924a2003-05-17 05:55:19 +00001879 def test_subclass_datetime(self):
1880
1881 class C(self.theclass):
1882 theAnswer = 42
1883
1884 def __new__(cls, *args, **kws):
1885 temp = kws.copy()
1886 extra = temp.pop('extra')
1887 result = self.theclass.__new__(cls, *args, **temp)
1888 result.extra = extra
1889 return result
1890
1891 def newmeth(self, start):
1892 return start + self.year + self.month + self.second
1893
1894 args = 2003, 4, 14, 12, 13, 41
1895
1896 dt1 = self.theclass(*args)
1897 dt2 = C(*args, **{'extra': 7})
1898
1899 self.assertEqual(dt2.__class__, C)
1900 self.assertEqual(dt2.theAnswer, 42)
1901 self.assertEqual(dt2.extra, 7)
1902 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1903 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1904 dt1.second - 7)
1905
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001906class TestSubclassDateTime(TestDateTime):
1907 theclass = SubclassDatetime
1908 # Override tests not designed for subclass
1909 def test_roundtrip(self):
1910 pass
1911
Tim Peters604c0132004-06-07 23:04:33 +00001912class SubclassTime(time):
1913 sub_var = 1
1914
Guido van Rossumd8faa362007-04-27 19:54:29 +00001915class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001916
1917 theclass = time
1918
1919 def test_basic_attributes(self):
1920 t = self.theclass(12, 0)
1921 self.assertEqual(t.hour, 12)
1922 self.assertEqual(t.minute, 0)
1923 self.assertEqual(t.second, 0)
1924 self.assertEqual(t.microsecond, 0)
1925
1926 def test_basic_attributes_nonzero(self):
1927 # Make sure all attributes are non-zero so bugs in
1928 # bit-shifting access show up.
1929 t = self.theclass(12, 59, 59, 8000)
1930 self.assertEqual(t.hour, 12)
1931 self.assertEqual(t.minute, 59)
1932 self.assertEqual(t.second, 59)
1933 self.assertEqual(t.microsecond, 8000)
1934
1935 def test_roundtrip(self):
1936 t = self.theclass(1, 2, 3, 4)
1937
1938 # Verify t -> string -> time identity.
1939 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001940 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001941 s = s[9:]
1942 t2 = eval(s)
1943 self.assertEqual(t, t2)
1944
1945 # Verify identity via reconstructing from pieces.
1946 t2 = self.theclass(t.hour, t.minute, t.second,
1947 t.microsecond)
1948 self.assertEqual(t, t2)
1949
1950 def test_comparing(self):
1951 args = [1, 2, 3, 4]
1952 t1 = self.theclass(*args)
1953 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001954 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001955 self.assertTrue(t1 <= t2)
1956 self.assertTrue(t1 >= t2)
1957 self.assertTrue(not t1 != t2)
1958 self.assertTrue(not t1 < t2)
1959 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001960
1961 for i in range(len(args)):
1962 newargs = args[:]
1963 newargs[i] = args[i] + 1
1964 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001965 self.assertTrue(t1 < t2)
1966 self.assertTrue(t2 > t1)
1967 self.assertTrue(t1 <= t2)
1968 self.assertTrue(t2 >= t1)
1969 self.assertTrue(t1 != t2)
1970 self.assertTrue(t2 != t1)
1971 self.assertTrue(not t1 == t2)
1972 self.assertTrue(not t2 == t1)
1973 self.assertTrue(not t1 > t2)
1974 self.assertTrue(not t2 < t1)
1975 self.assertTrue(not t1 >= t2)
1976 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001977
Tim Peters68124bb2003-02-08 03:46:31 +00001978 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001979 self.assertEqual(t1 == badarg, False)
1980 self.assertEqual(t1 != badarg, True)
1981 self.assertEqual(badarg == t1, False)
1982 self.assertEqual(badarg != t1, True)
1983
Tim Peters2a799bf2002-12-16 20:18:38 +00001984 self.assertRaises(TypeError, lambda: t1 <= badarg)
1985 self.assertRaises(TypeError, lambda: t1 < badarg)
1986 self.assertRaises(TypeError, lambda: t1 > badarg)
1987 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001988 self.assertRaises(TypeError, lambda: badarg <= t1)
1989 self.assertRaises(TypeError, lambda: badarg < t1)
1990 self.assertRaises(TypeError, lambda: badarg > t1)
1991 self.assertRaises(TypeError, lambda: badarg >= t1)
1992
1993 def test_bad_constructor_arguments(self):
1994 # bad hours
1995 self.theclass(0, 0) # no exception
1996 self.theclass(23, 0) # no exception
1997 self.assertRaises(ValueError, self.theclass, -1, 0)
1998 self.assertRaises(ValueError, self.theclass, 24, 0)
1999 # bad minutes
2000 self.theclass(23, 0) # no exception
2001 self.theclass(23, 59) # no exception
2002 self.assertRaises(ValueError, self.theclass, 23, -1)
2003 self.assertRaises(ValueError, self.theclass, 23, 60)
2004 # bad seconds
2005 self.theclass(23, 59, 0) # no exception
2006 self.theclass(23, 59, 59) # no exception
2007 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
2008 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
2009 # bad microseconds
2010 self.theclass(23, 59, 59, 0) # no exception
2011 self.theclass(23, 59, 59, 999999) # no exception
2012 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
2013 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
2014
2015 def test_hash_equality(self):
2016 d = self.theclass(23, 30, 17)
2017 e = self.theclass(23, 30, 17)
2018 self.assertEqual(d, e)
2019 self.assertEqual(hash(d), hash(e))
2020
2021 dic = {d: 1}
2022 dic[e] = 2
2023 self.assertEqual(len(dic), 1)
2024 self.assertEqual(dic[d], 2)
2025 self.assertEqual(dic[e], 2)
2026
2027 d = self.theclass(0, 5, 17)
2028 e = self.theclass(0, 5, 17)
2029 self.assertEqual(d, e)
2030 self.assertEqual(hash(d), hash(e))
2031
2032 dic = {d: 1}
2033 dic[e] = 2
2034 self.assertEqual(len(dic), 1)
2035 self.assertEqual(dic[d], 2)
2036 self.assertEqual(dic[e], 2)
2037
2038 def test_isoformat(self):
2039 t = self.theclass(4, 5, 1, 123)
2040 self.assertEqual(t.isoformat(), "04:05:01.000123")
2041 self.assertEqual(t.isoformat(), str(t))
2042
2043 t = self.theclass()
2044 self.assertEqual(t.isoformat(), "00:00:00")
2045 self.assertEqual(t.isoformat(), str(t))
2046
2047 t = self.theclass(microsecond=1)
2048 self.assertEqual(t.isoformat(), "00:00:00.000001")
2049 self.assertEqual(t.isoformat(), str(t))
2050
2051 t = self.theclass(microsecond=10)
2052 self.assertEqual(t.isoformat(), "00:00:00.000010")
2053 self.assertEqual(t.isoformat(), str(t))
2054
2055 t = self.theclass(microsecond=100)
2056 self.assertEqual(t.isoformat(), "00:00:00.000100")
2057 self.assertEqual(t.isoformat(), str(t))
2058
2059 t = self.theclass(microsecond=1000)
2060 self.assertEqual(t.isoformat(), "00:00:00.001000")
2061 self.assertEqual(t.isoformat(), str(t))
2062
2063 t = self.theclass(microsecond=10000)
2064 self.assertEqual(t.isoformat(), "00:00:00.010000")
2065 self.assertEqual(t.isoformat(), str(t))
2066
2067 t = self.theclass(microsecond=100000)
2068 self.assertEqual(t.isoformat(), "00:00:00.100000")
2069 self.assertEqual(t.isoformat(), str(t))
2070
Thomas Wouterscf297e42007-02-23 15:07:44 +00002071 def test_1653736(self):
2072 # verify it doesn't accept extra keyword arguments
2073 t = self.theclass(second=1)
2074 self.assertRaises(TypeError, t.isoformat, foo=3)
2075
Tim Peters2a799bf2002-12-16 20:18:38 +00002076 def test_strftime(self):
2077 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00002078 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00002079 # A naive object replaces %z and %Z with empty strings.
2080 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2081
Eric Smith1ba31142007-09-11 18:06:02 +00002082 def test_format(self):
2083 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002084 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002085
2086 # check that a derived class's __str__() gets called
2087 class A(self.theclass):
2088 def __str__(self):
2089 return 'A'
2090 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002091 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00002092
2093 # check that a derived class's strftime gets called
2094 class B(self.theclass):
2095 def strftime(self, format_spec):
2096 return 'B'
2097 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002098 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002099
2100 for fmt in ['%H %M %S',
2101 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00002102 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2103 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2104 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00002105
Tim Peters2a799bf2002-12-16 20:18:38 +00002106 def test_str(self):
2107 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2108 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2109 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2110 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2111 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2112
2113 def test_repr(self):
2114 name = 'datetime.' + self.theclass.__name__
2115 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2116 "%s(1, 2, 3, 4)" % name)
2117 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2118 "%s(10, 2, 3, 4000)" % name)
2119 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2120 "%s(0, 2, 3, 400000)" % name)
2121 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2122 "%s(12, 2, 3)" % name)
2123 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2124 "%s(23, 15)" % name)
2125
2126 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00002127 self.assertIsInstance(self.theclass.min, self.theclass)
2128 self.assertIsInstance(self.theclass.max, self.theclass)
2129 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002130 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00002131
2132 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002133 args = 20, 59, 16, 64**2
2134 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002135 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002136 green = pickler.dumps(orig, proto)
2137 derived = unpickler.loads(green)
2138 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002139
Tim Peters604c0132004-06-07 23:04:33 +00002140 def test_pickling_subclass_time(self):
2141 args = 20, 59, 16, 64**2
2142 orig = SubclassTime(*args)
2143 for pickler, unpickler, proto in pickle_choices:
2144 green = pickler.dumps(orig, proto)
2145 derived = unpickler.loads(green)
2146 self.assertEqual(orig, derived)
2147
Tim Peters2a799bf2002-12-16 20:18:38 +00002148 def test_bool(self):
2149 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002150 self.assertTrue(cls(1))
2151 self.assertTrue(cls(0, 1))
2152 self.assertTrue(cls(0, 0, 1))
2153 self.assertTrue(cls(0, 0, 0, 1))
2154 self.assertTrue(not cls(0))
2155 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00002156
Tim Peters12bf3392002-12-24 05:41:27 +00002157 def test_replace(self):
2158 cls = self.theclass
2159 args = [1, 2, 3, 4]
2160 base = cls(*args)
2161 self.assertEqual(base, base.replace())
2162
2163 i = 0
2164 for name, newval in (("hour", 5),
2165 ("minute", 6),
2166 ("second", 7),
2167 ("microsecond", 8)):
2168 newargs = args[:]
2169 newargs[i] = newval
2170 expected = cls(*newargs)
2171 got = base.replace(**{name: newval})
2172 self.assertEqual(expected, got)
2173 i += 1
2174
2175 # Out of bounds.
2176 base = cls(1)
2177 self.assertRaises(ValueError, base.replace, hour=24)
2178 self.assertRaises(ValueError, base.replace, minute=-1)
2179 self.assertRaises(ValueError, base.replace, second=100)
2180 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2181
Tim Petersa98924a2003-05-17 05:55:19 +00002182 def test_subclass_time(self):
2183
2184 class C(self.theclass):
2185 theAnswer = 42
2186
2187 def __new__(cls, *args, **kws):
2188 temp = kws.copy()
2189 extra = temp.pop('extra')
2190 result = self.theclass.__new__(cls, *args, **temp)
2191 result.extra = extra
2192 return result
2193
2194 def newmeth(self, start):
2195 return start + self.hour + self.second
2196
2197 args = 4, 5, 6
2198
2199 dt1 = self.theclass(*args)
2200 dt2 = C(*args, **{'extra': 7})
2201
2202 self.assertEqual(dt2.__class__, C)
2203 self.assertEqual(dt2.theAnswer, 42)
2204 self.assertEqual(dt2.extra, 7)
2205 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2206 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2207
Armin Rigof4afb212005-11-07 07:15:48 +00002208 def test_backdoor_resistance(self):
2209 # see TestDate.test_backdoor_resistance().
2210 base = '2:59.0'
2211 for hour_byte in ' ', '9', chr(24), '\xff':
2212 self.assertRaises(TypeError, self.theclass,
2213 hour_byte + base[1:])
2214
Tim Peters855fe882002-12-22 03:43:39 +00002215# A mixin for classes with a tzinfo= argument. Subclasses must define
2216# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002217# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002218class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002219
Tim Petersbad8ff02002-12-30 20:52:32 +00002220 def test_argument_passing(self):
2221 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002222 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002223 class introspective(tzinfo):
2224 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002225 def utcoffset(self, dt):
2226 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002227 dst = utcoffset
2228
2229 obj = cls(1, 2, 3, tzinfo=introspective())
2230
Tim Peters0bf60bd2003-01-08 20:40:01 +00002231 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002232 self.assertEqual(obj.tzname(), expected)
2233
Tim Peters0bf60bd2003-01-08 20:40:01 +00002234 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002235 self.assertEqual(obj.utcoffset(), expected)
2236 self.assertEqual(obj.dst(), expected)
2237
Tim Peters855fe882002-12-22 03:43:39 +00002238 def test_bad_tzinfo_classes(self):
2239 cls = self.theclass
2240 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002241
Tim Peters855fe882002-12-22 03:43:39 +00002242 class NiceTry(object):
2243 def __init__(self): pass
2244 def utcoffset(self, dt): pass
2245 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2246
2247 class BetterTry(tzinfo):
2248 def __init__(self): pass
2249 def utcoffset(self, dt): pass
2250 b = BetterTry()
2251 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002252 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002253
2254 def test_utc_offset_out_of_bounds(self):
2255 class Edgy(tzinfo):
2256 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002257 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002258 def utcoffset(self, dt):
2259 return self.offset
2260
2261 cls = self.theclass
2262 for offset, legit in ((-1440, False),
2263 (-1439, True),
2264 (1439, True),
2265 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002266 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002267 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002268 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002269 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002270 else:
2271 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002272 if legit:
2273 aofs = abs(offset)
2274 h, m = divmod(aofs, 60)
2275 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002276 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002277 t = t.timetz()
2278 self.assertEqual(str(t), "01:02:03" + tag)
2279 else:
2280 self.assertRaises(ValueError, str, t)
2281
2282 def test_tzinfo_classes(self):
2283 cls = self.theclass
2284 class C1(tzinfo):
2285 def utcoffset(self, dt): return None
2286 def dst(self, dt): return None
2287 def tzname(self, dt): return None
2288 for t in (cls(1, 1, 1),
2289 cls(1, 1, 1, tzinfo=None),
2290 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002291 self.assertTrue(t.utcoffset() is None)
2292 self.assertTrue(t.dst() is None)
2293 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002294
Tim Peters855fe882002-12-22 03:43:39 +00002295 class C3(tzinfo):
2296 def utcoffset(self, dt): return timedelta(minutes=-1439)
2297 def dst(self, dt): return timedelta(minutes=1439)
2298 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002299 t = cls(1, 1, 1, tzinfo=C3())
2300 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2301 self.assertEqual(t.dst(), timedelta(minutes=1439))
2302 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002303
2304 # Wrong types.
2305 class C4(tzinfo):
2306 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002307 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002308 def tzname(self, dt): return 0
2309 t = cls(1, 1, 1, tzinfo=C4())
2310 self.assertRaises(TypeError, t.utcoffset)
2311 self.assertRaises(TypeError, t.dst)
2312 self.assertRaises(TypeError, t.tzname)
2313
2314 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002315 class C6(tzinfo):
2316 def utcoffset(self, dt): return timedelta(hours=-24)
2317 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002318 t = cls(1, 1, 1, tzinfo=C6())
2319 self.assertRaises(ValueError, t.utcoffset)
2320 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002321
2322 # Not a whole number of minutes.
2323 class C7(tzinfo):
2324 def utcoffset(self, dt): return timedelta(seconds=61)
2325 def dst(self, dt): return timedelta(microseconds=-81)
2326 t = cls(1, 1, 1, tzinfo=C7())
2327 self.assertRaises(ValueError, t.utcoffset)
2328 self.assertRaises(ValueError, t.dst)
2329
Tim Peters4c0db782002-12-26 05:01:19 +00002330 def test_aware_compare(self):
2331 cls = self.theclass
2332
Tim Peters60c76e42002-12-27 00:41:11 +00002333 # Ensure that utcoffset() gets ignored if the comparands have
2334 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002335 class OperandDependentOffset(tzinfo):
2336 def utcoffset(self, t):
2337 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002338 # d0 and d1 equal after adjustment
2339 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002340 else:
Tim Peters397301e2003-01-02 21:28:08 +00002341 # d2 off in the weeds
2342 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002343
2344 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2345 d0 = base.replace(minute=3)
2346 d1 = base.replace(minute=9)
2347 d2 = base.replace(minute=11)
2348 for x in d0, d1, d2:
2349 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002350 for op in lt, le, gt, ge, eq, ne:
2351 got = op(x, y)
2352 expected = op(x.minute, y.minute)
2353 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002354
2355 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002356 # Note that a time can't actually have an operand-depedent offset,
2357 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2358 # so skip this test for time.
2359 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002360 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2361 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2362 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2363 for x in d0, d1, d2:
2364 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002365 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002366 if (x is d0 or x is d1) and (y is d0 or y is d1):
2367 expected = 0
2368 elif x is y is d2:
2369 expected = 0
2370 elif x is d2:
2371 expected = -1
2372 else:
2373 assert y is d2
2374 expected = 1
2375 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002376
Tim Peters855fe882002-12-22 03:43:39 +00002377
Tim Peters0bf60bd2003-01-08 20:40:01 +00002378# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002379class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002380 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002381
2382 def test_empty(self):
2383 t = self.theclass()
2384 self.assertEqual(t.hour, 0)
2385 self.assertEqual(t.minute, 0)
2386 self.assertEqual(t.second, 0)
2387 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002388 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002389
Tim Peters2a799bf2002-12-16 20:18:38 +00002390 def test_zones(self):
2391 est = FixedOffset(-300, "EST", 1)
2392 utc = FixedOffset(0, "UTC", -2)
2393 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002394 t1 = time( 7, 47, tzinfo=est)
2395 t2 = time(12, 47, tzinfo=utc)
2396 t3 = time(13, 47, tzinfo=met)
2397 t4 = time(microsecond=40)
2398 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002399
2400 self.assertEqual(t1.tzinfo, est)
2401 self.assertEqual(t2.tzinfo, utc)
2402 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002403 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002404 self.assertEqual(t5.tzinfo, utc)
2405
Tim Peters855fe882002-12-22 03:43:39 +00002406 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2407 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2408 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002409 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002410 self.assertRaises(TypeError, t1.utcoffset, "no args")
2411
2412 self.assertEqual(t1.tzname(), "EST")
2413 self.assertEqual(t2.tzname(), "UTC")
2414 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002415 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002416 self.assertRaises(TypeError, t1.tzname, "no args")
2417
Tim Peters855fe882002-12-22 03:43:39 +00002418 self.assertEqual(t1.dst(), timedelta(minutes=1))
2419 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2420 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002421 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002422 self.assertRaises(TypeError, t1.dst, "no args")
2423
2424 self.assertEqual(hash(t1), hash(t2))
2425 self.assertEqual(hash(t1), hash(t3))
2426 self.assertEqual(hash(t2), hash(t3))
2427
2428 self.assertEqual(t1, t2)
2429 self.assertEqual(t1, t3)
2430 self.assertEqual(t2, t3)
2431 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2432 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2433 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2434
2435 self.assertEqual(str(t1), "07:47:00-05:00")
2436 self.assertEqual(str(t2), "12:47:00+00:00")
2437 self.assertEqual(str(t3), "13:47:00+01:00")
2438 self.assertEqual(str(t4), "00:00:00.000040")
2439 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2440
2441 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2442 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2443 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2444 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2445 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2446
Tim Peters0bf60bd2003-01-08 20:40:01 +00002447 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002448 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2449 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2450 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2451 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2452 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2453
2454 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2455 "07:47:00 %Z=EST %z=-0500")
2456 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2457 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2458
2459 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002460 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002461 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2462 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2463
Tim Petersb92bb712002-12-21 17:44:07 +00002464 # Check that an invalid tzname result raises an exception.
2465 class Badtzname(tzinfo):
2466 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002467 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002468 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2469 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002470
2471 def test_hash_edge_cases(self):
2472 # Offsets that overflow a basic time.
2473 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2474 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2475 self.assertEqual(hash(t1), hash(t2))
2476
2477 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2478 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2479 self.assertEqual(hash(t1), hash(t2))
2480
Tim Peters2a799bf2002-12-16 20:18:38 +00002481 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002482 # Try one without a tzinfo.
2483 args = 20, 59, 16, 64**2
2484 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002485 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002486 green = pickler.dumps(orig, proto)
2487 derived = unpickler.loads(green)
2488 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002489
2490 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002491 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002492 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002493 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002494 green = pickler.dumps(orig, proto)
2495 derived = unpickler.loads(green)
2496 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002497 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002498 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2499 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002500
2501 def test_more_bool(self):
2502 # Test cases with non-None tzinfo.
2503 cls = self.theclass
2504
2505 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002506 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002507
2508 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002509 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002510
2511 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002512 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002513
2514 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002515 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002516
2517 # Mostly ensuring this doesn't overflow internally.
2518 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002519 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002520
2521 # But this should yield a value error -- the utcoffset is bogus.
2522 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2523 self.assertRaises(ValueError, lambda: bool(t))
2524
2525 # Likewise.
2526 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2527 self.assertRaises(ValueError, lambda: bool(t))
2528
Tim Peters12bf3392002-12-24 05:41:27 +00002529 def test_replace(self):
2530 cls = self.theclass
2531 z100 = FixedOffset(100, "+100")
2532 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2533 args = [1, 2, 3, 4, z100]
2534 base = cls(*args)
2535 self.assertEqual(base, base.replace())
2536
2537 i = 0
2538 for name, newval in (("hour", 5),
2539 ("minute", 6),
2540 ("second", 7),
2541 ("microsecond", 8),
2542 ("tzinfo", zm200)):
2543 newargs = args[:]
2544 newargs[i] = newval
2545 expected = cls(*newargs)
2546 got = base.replace(**{name: newval})
2547 self.assertEqual(expected, got)
2548 i += 1
2549
2550 # Ensure we can get rid of a tzinfo.
2551 self.assertEqual(base.tzname(), "+100")
2552 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002553 self.assertTrue(base2.tzinfo is None)
2554 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002555
2556 # Ensure we can add one.
2557 base3 = base2.replace(tzinfo=z100)
2558 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002559 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002560
2561 # Out of bounds.
2562 base = cls(1)
2563 self.assertRaises(ValueError, base.replace, hour=24)
2564 self.assertRaises(ValueError, base.replace, minute=-1)
2565 self.assertRaises(ValueError, base.replace, second=100)
2566 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2567
Tim Peters60c76e42002-12-27 00:41:11 +00002568 def test_mixed_compare(self):
2569 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002570 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002571 self.assertEqual(t1, t2)
2572 t2 = t2.replace(tzinfo=None)
2573 self.assertEqual(t1, t2)
2574 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2575 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002576 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2577 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002578
Tim Peters0bf60bd2003-01-08 20:40:01 +00002579 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002580 class Varies(tzinfo):
2581 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002582 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002583 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002584 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002585 return self.offset
2586
2587 v = Varies()
2588 t1 = t2.replace(tzinfo=v)
2589 t2 = t2.replace(tzinfo=v)
2590 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2591 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2592 self.assertEqual(t1, t2)
2593
2594 # But if they're not identical, it isn't ignored.
2595 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002596 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002597
Tim Petersa98924a2003-05-17 05:55:19 +00002598 def test_subclass_timetz(self):
2599
2600 class C(self.theclass):
2601 theAnswer = 42
2602
2603 def __new__(cls, *args, **kws):
2604 temp = kws.copy()
2605 extra = temp.pop('extra')
2606 result = self.theclass.__new__(cls, *args, **temp)
2607 result.extra = extra
2608 return result
2609
2610 def newmeth(self, start):
2611 return start + self.hour + self.second
2612
2613 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2614
2615 dt1 = self.theclass(*args)
2616 dt2 = C(*args, **{'extra': 7})
2617
2618 self.assertEqual(dt2.__class__, C)
2619 self.assertEqual(dt2.theAnswer, 42)
2620 self.assertEqual(dt2.extra, 7)
2621 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2622 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2623
Tim Peters4c0db782002-12-26 05:01:19 +00002624
Tim Peters0bf60bd2003-01-08 20:40:01 +00002625# Testing datetime objects with a non-None tzinfo.
2626
Guido van Rossumd8faa362007-04-27 19:54:29 +00002627class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002628 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002629
2630 def test_trivial(self):
2631 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2632 self.assertEqual(dt.year, 1)
2633 self.assertEqual(dt.month, 2)
2634 self.assertEqual(dt.day, 3)
2635 self.assertEqual(dt.hour, 4)
2636 self.assertEqual(dt.minute, 5)
2637 self.assertEqual(dt.second, 6)
2638 self.assertEqual(dt.microsecond, 7)
2639 self.assertEqual(dt.tzinfo, None)
2640
2641 def test_even_more_compare(self):
2642 # The test_compare() and test_more_compare() inherited from TestDate
2643 # and TestDateTime covered non-tzinfo cases.
2644
2645 # Smallest possible after UTC adjustment.
2646 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2647 # Largest possible after UTC adjustment.
2648 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2649 tzinfo=FixedOffset(-1439, ""))
2650
2651 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002652 self.assertTrue(t1 < t2)
2653 self.assertTrue(t1 != t2)
2654 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002655
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002656 self.assertEqual(t1, t1)
2657 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002658
2659 # Equal afer adjustment.
2660 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2661 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2662 self.assertEqual(t1, t2)
2663
2664 # Change t1 not to subtract a minute, and t1 should be larger.
2665 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002666 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002667
2668 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2669 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002670 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002671
2672 # Back to the original t1, but make seconds resolve it.
2673 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2674 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002675 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002676
2677 # Likewise, but make microseconds resolve it.
2678 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2679 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002680 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002681
2682 # Make t2 naive and it should fail.
2683 t2 = self.theclass.min
2684 self.assertRaises(TypeError, lambda: t1 == t2)
2685 self.assertEqual(t2, t2)
2686
2687 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2688 class Naive(tzinfo):
2689 def utcoffset(self, dt): return None
2690 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2691 self.assertRaises(TypeError, lambda: t1 == t2)
2692 self.assertEqual(t2, t2)
2693
2694 # OTOH, it's OK to compare two of these mixing the two ways of being
2695 # naive.
2696 t1 = self.theclass(5, 6, 7)
2697 self.assertEqual(t1, t2)
2698
2699 # Try a bogus uctoffset.
2700 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002701 def utcoffset(self, dt):
2702 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002703 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2704 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002705 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002706
Tim Peters2a799bf2002-12-16 20:18:38 +00002707 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002708 # Try one without a tzinfo.
2709 args = 6, 7, 23, 20, 59, 1, 64**2
2710 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002711 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002712 green = pickler.dumps(orig, proto)
2713 derived = unpickler.loads(green)
2714 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002715
2716 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002717 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002718 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002719 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002720 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002721 green = pickler.dumps(orig, proto)
2722 derived = unpickler.loads(green)
2723 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002724 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002725 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2726 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002727
2728 def test_extreme_hashes(self):
2729 # If an attempt is made to hash these via subtracting the offset
2730 # then hashing a datetime object, OverflowError results. The
2731 # Python implementation used to blow up here.
2732 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2733 hash(t)
2734 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2735 tzinfo=FixedOffset(-1439, ""))
2736 hash(t)
2737
2738 # OTOH, an OOB offset should blow up.
2739 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2740 self.assertRaises(ValueError, hash, t)
2741
2742 def test_zones(self):
2743 est = FixedOffset(-300, "EST")
2744 utc = FixedOffset(0, "UTC")
2745 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002746 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2747 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2748 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002749 self.assertEqual(t1.tzinfo, est)
2750 self.assertEqual(t2.tzinfo, utc)
2751 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002752 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2753 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2754 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002755 self.assertEqual(t1.tzname(), "EST")
2756 self.assertEqual(t2.tzname(), "UTC")
2757 self.assertEqual(t3.tzname(), "MET")
2758 self.assertEqual(hash(t1), hash(t2))
2759 self.assertEqual(hash(t1), hash(t3))
2760 self.assertEqual(hash(t2), hash(t3))
2761 self.assertEqual(t1, t2)
2762 self.assertEqual(t1, t3)
2763 self.assertEqual(t2, t3)
2764 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2765 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2766 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002767 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002768 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2769 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2770 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2771
2772 def test_combine(self):
2773 met = FixedOffset(60, "MET")
2774 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002775 tz = time(18, 45, 3, 1234, tzinfo=met)
2776 dt = datetime.combine(d, tz)
2777 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002778 tzinfo=met))
2779
2780 def test_extract(self):
2781 met = FixedOffset(60, "MET")
2782 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2783 self.assertEqual(dt.date(), date(2002, 3, 4))
2784 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002785 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002786
2787 def test_tz_aware_arithmetic(self):
2788 import random
2789
2790 now = self.theclass.now()
2791 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002792 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002793 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002794 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002795 self.assertEqual(nowaware.timetz(), timeaware)
2796
2797 # Can't mix aware and non-aware.
2798 self.assertRaises(TypeError, lambda: now - nowaware)
2799 self.assertRaises(TypeError, lambda: nowaware - now)
2800
Tim Peters0bf60bd2003-01-08 20:40:01 +00002801 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002802 self.assertRaises(TypeError, lambda: now + nowaware)
2803 self.assertRaises(TypeError, lambda: nowaware + now)
2804 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2805
2806 # Subtracting should yield 0.
2807 self.assertEqual(now - now, timedelta(0))
2808 self.assertEqual(nowaware - nowaware, timedelta(0))
2809
2810 # Adding a delta should preserve tzinfo.
2811 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2812 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002813 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002814 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002815 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002816 self.assertEqual(nowawareplus, nowawareplus2)
2817
2818 # that - delta should be what we started with, and that - what we
2819 # started with should be delta.
2820 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002821 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002822 self.assertEqual(nowaware, diff)
2823 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2824 self.assertEqual(nowawareplus - nowaware, delta)
2825
2826 # Make up a random timezone.
2827 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002828 # Attach it to nowawareplus.
2829 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002830 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002831 # Make sure the difference takes the timezone adjustments into account.
2832 got = nowaware - nowawareplus
2833 # Expected: (nowaware base - nowaware offset) -
2834 # (nowawareplus base - nowawareplus offset) =
2835 # (nowaware base - nowawareplus base) +
2836 # (nowawareplus offset - nowaware offset) =
2837 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002838 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002839 self.assertEqual(got, expected)
2840
2841 # Try max possible difference.
2842 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2843 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2844 tzinfo=FixedOffset(-1439, "max"))
2845 maxdiff = max - min
2846 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2847 timedelta(minutes=2*1439))
2848
2849 def test_tzinfo_now(self):
2850 meth = self.theclass.now
2851 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2852 base = meth()
2853 # Try with and without naming the keyword.
2854 off42 = FixedOffset(42, "42")
2855 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002856 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002857 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002858 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002859 # Bad argument with and w/o naming the keyword.
2860 self.assertRaises(TypeError, meth, 16)
2861 self.assertRaises(TypeError, meth, tzinfo=16)
2862 # Bad keyword name.
2863 self.assertRaises(TypeError, meth, tinfo=off42)
2864 # Too many args.
2865 self.assertRaises(TypeError, meth, off42, off42)
2866
Tim Peters10cadce2003-01-23 19:58:02 +00002867 # We don't know which time zone we're in, and don't have a tzinfo
2868 # class to represent it, so seeing whether a tz argument actually
2869 # does a conversion is tricky.
Tim Peters10cadce2003-01-23 19:58:02 +00002870 utc = FixedOffset(0, "utc", 0)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +00002871 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
2872 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
2873 for dummy in range(3):
2874 now = datetime.now(weirdtz)
2875 self.assertTrue(now.tzinfo is weirdtz)
2876 utcnow = datetime.utcnow().replace(tzinfo=utc)
2877 now2 = utcnow.astimezone(weirdtz)
2878 if abs(now - now2) < timedelta(seconds=30):
2879 break
2880 # Else the code is broken, or more than 30 seconds passed between
2881 # calls; assuming the latter, just try again.
2882 else:
2883 # Three strikes and we're out.
2884 self.fail("utcnow(), now(tz), or astimezone() may be broken")
Tim Peters10cadce2003-01-23 19:58:02 +00002885
Tim Peters2a799bf2002-12-16 20:18:38 +00002886 def test_tzinfo_fromtimestamp(self):
2887 import time
2888 meth = self.theclass.fromtimestamp
2889 ts = time.time()
2890 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2891 base = meth(ts)
2892 # Try with and without naming the keyword.
2893 off42 = FixedOffset(42, "42")
2894 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002895 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002896 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002897 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002898 # Bad argument with and w/o naming the keyword.
2899 self.assertRaises(TypeError, meth, ts, 16)
2900 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2901 # Bad keyword name.
2902 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2903 # Too many args.
2904 self.assertRaises(TypeError, meth, ts, off42, off42)
2905 # Too few args.
2906 self.assertRaises(TypeError, meth)
2907
Tim Peters2a44a8d2003-01-23 20:53:10 +00002908 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002909 timestamp = 1000000000
2910 utcdatetime = datetime.utcfromtimestamp(timestamp)
2911 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2912 # But on some flavor of Mac, it's nowhere near that. So we can't have
2913 # any idea here what time that actually is, we can only test that
2914 # relative changes match.
2915 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2916 tz = FixedOffset(utcoffset, "tz", 0)
2917 expected = utcdatetime + utcoffset
2918 got = datetime.fromtimestamp(timestamp, tz)
2919 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002920
Tim Peters2a799bf2002-12-16 20:18:38 +00002921 def test_tzinfo_utcnow(self):
2922 meth = self.theclass.utcnow
2923 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2924 base = meth()
2925 # Try with and without naming the keyword; for whatever reason,
2926 # utcnow() doesn't accept a tzinfo argument.
2927 off42 = FixedOffset(42, "42")
2928 self.assertRaises(TypeError, meth, off42)
2929 self.assertRaises(TypeError, meth, tzinfo=off42)
2930
2931 def test_tzinfo_utcfromtimestamp(self):
2932 import time
2933 meth = self.theclass.utcfromtimestamp
2934 ts = time.time()
2935 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2936 base = meth(ts)
2937 # Try with and without naming the keyword; for whatever reason,
2938 # utcfromtimestamp() doesn't accept a tzinfo argument.
2939 off42 = FixedOffset(42, "42")
2940 self.assertRaises(TypeError, meth, ts, off42)
2941 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2942
2943 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002944 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002945 # DST flag.
2946 class DST(tzinfo):
2947 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002948 if isinstance(dstvalue, int):
2949 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002950 self.dstvalue = dstvalue
2951 def dst(self, dt):
2952 return self.dstvalue
2953
2954 cls = self.theclass
2955 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2956 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2957 t = d.timetuple()
2958 self.assertEqual(1, t.tm_year)
2959 self.assertEqual(1, t.tm_mon)
2960 self.assertEqual(1, t.tm_mday)
2961 self.assertEqual(10, t.tm_hour)
2962 self.assertEqual(20, t.tm_min)
2963 self.assertEqual(30, t.tm_sec)
2964 self.assertEqual(0, t.tm_wday)
2965 self.assertEqual(1, t.tm_yday)
2966 self.assertEqual(flag, t.tm_isdst)
2967
2968 # dst() returns wrong type.
2969 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2970
2971 # dst() at the edge.
2972 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2973 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2974
2975 # dst() out of range.
2976 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2977 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2978
2979 def test_utctimetuple(self):
2980 class DST(tzinfo):
2981 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002982 if isinstance(dstvalue, int):
2983 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002984 self.dstvalue = dstvalue
2985 def dst(self, dt):
2986 return self.dstvalue
2987
2988 cls = self.theclass
2989 # This can't work: DST didn't implement utcoffset.
2990 self.assertRaises(NotImplementedError,
2991 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2992
2993 class UOFS(DST):
2994 def __init__(self, uofs, dofs=None):
2995 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002996 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002997 def utcoffset(self, dt):
2998 return self.uofs
2999
3000 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
3001 # in effect for a UTC time.
3002 for dstvalue in -33, 33, 0, None:
3003 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
3004 t = d.utctimetuple()
3005 self.assertEqual(d.year, t.tm_year)
3006 self.assertEqual(d.month, t.tm_mon)
3007 self.assertEqual(d.day, t.tm_mday)
3008 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
3009 self.assertEqual(13, t.tm_min)
3010 self.assertEqual(d.second, t.tm_sec)
3011 self.assertEqual(d.weekday(), t.tm_wday)
3012 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
3013 t.tm_yday)
3014 self.assertEqual(0, t.tm_isdst)
3015
3016 # At the edges, UTC adjustment can normalize into years out-of-range
3017 # for a datetime object. Ensure that a correct timetuple is
3018 # created anyway.
3019 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
3020 # That goes back 1 minute less than a full day.
3021 t = tiny.utctimetuple()
3022 self.assertEqual(t.tm_year, MINYEAR-1)
3023 self.assertEqual(t.tm_mon, 12)
3024 self.assertEqual(t.tm_mday, 31)
3025 self.assertEqual(t.tm_hour, 0)
3026 self.assertEqual(t.tm_min, 1)
3027 self.assertEqual(t.tm_sec, 37)
3028 self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
3029 self.assertEqual(t.tm_isdst, 0)
3030
3031 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
3032 # That goes forward 1 minute less than a full day.
3033 t = huge.utctimetuple()
3034 self.assertEqual(t.tm_year, MAXYEAR+1)
3035 self.assertEqual(t.tm_mon, 1)
3036 self.assertEqual(t.tm_mday, 1)
3037 self.assertEqual(t.tm_hour, 23)
3038 self.assertEqual(t.tm_min, 58)
3039 self.assertEqual(t.tm_sec, 37)
3040 self.assertEqual(t.tm_yday, 1)
3041 self.assertEqual(t.tm_isdst, 0)
3042
3043 def test_tzinfo_isoformat(self):
3044 zero = FixedOffset(0, "+00:00")
3045 plus = FixedOffset(220, "+03:40")
3046 minus = FixedOffset(-231, "-03:51")
3047 unknown = FixedOffset(None, "")
3048
3049 cls = self.theclass
3050 datestr = '0001-02-03'
3051 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00003052 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00003053 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3054 timestr = '04:05:59' + (us and '.987001' or '')
3055 ofsstr = ofs is not None and d.tzname() or ''
3056 tailstr = timestr + ofsstr
3057 iso = d.isoformat()
3058 self.assertEqual(iso, datestr + 'T' + tailstr)
3059 self.assertEqual(iso, d.isoformat('T'))
3060 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00003061 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003062 self.assertEqual(str(d), datestr + ' ' + tailstr)
3063
Tim Peters12bf3392002-12-24 05:41:27 +00003064 def test_replace(self):
3065 cls = self.theclass
3066 z100 = FixedOffset(100, "+100")
3067 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3068 args = [1, 2, 3, 4, 5, 6, 7, z100]
3069 base = cls(*args)
3070 self.assertEqual(base, base.replace())
3071
3072 i = 0
3073 for name, newval in (("year", 2),
3074 ("month", 3),
3075 ("day", 4),
3076 ("hour", 5),
3077 ("minute", 6),
3078 ("second", 7),
3079 ("microsecond", 8),
3080 ("tzinfo", zm200)):
3081 newargs = args[:]
3082 newargs[i] = newval
3083 expected = cls(*newargs)
3084 got = base.replace(**{name: newval})
3085 self.assertEqual(expected, got)
3086 i += 1
3087
3088 # Ensure we can get rid of a tzinfo.
3089 self.assertEqual(base.tzname(), "+100")
3090 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003091 self.assertTrue(base2.tzinfo is None)
3092 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00003093
3094 # Ensure we can add one.
3095 base3 = base2.replace(tzinfo=z100)
3096 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003097 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00003098
3099 # Out of bounds.
3100 base = cls(2000, 2, 29)
3101 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00003102
Tim Peters80475bb2002-12-25 07:40:55 +00003103 def test_more_astimezone(self):
3104 # The inherited test_astimezone covered some trivial and error cases.
3105 fnone = FixedOffset(None, "None")
3106 f44m = FixedOffset(44, "44")
3107 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3108
Tim Peters10cadce2003-01-23 19:58:02 +00003109 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003110 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00003111 # Replacing with degenerate tzinfo raises an exception.
3112 self.assertRaises(ValueError, dt.astimezone, fnone)
3113 # Ditto with None tz.
3114 self.assertRaises(TypeError, dt.astimezone, None)
3115 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00003116 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003117 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00003118 self.assertEqual(x.date(), dt.date())
3119 self.assertEqual(x.time(), dt.time())
3120
3121 # Replacing with different tzinfo does adjust.
3122 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003123 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00003124 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3125 expected = dt - dt.utcoffset() # in effect, convert to UTC
3126 expected += fm5h.utcoffset(dt) # and from there to local time
3127 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3128 self.assertEqual(got.date(), expected.date())
3129 self.assertEqual(got.time(), expected.time())
3130 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003131 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00003132 self.assertEqual(got, expected)
3133
Tim Peters4c0db782002-12-26 05:01:19 +00003134 def test_aware_subtract(self):
3135 cls = self.theclass
3136
Tim Peters60c76e42002-12-27 00:41:11 +00003137 # Ensure that utcoffset() is ignored when the operands have the
3138 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00003139 class OperandDependentOffset(tzinfo):
3140 def utcoffset(self, t):
3141 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00003142 # d0 and d1 equal after adjustment
3143 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00003144 else:
Tim Peters397301e2003-01-02 21:28:08 +00003145 # d2 off in the weeds
3146 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00003147
3148 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3149 d0 = base.replace(minute=3)
3150 d1 = base.replace(minute=9)
3151 d2 = base.replace(minute=11)
3152 for x in d0, d1, d2:
3153 for y in d0, d1, d2:
3154 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00003155 expected = timedelta(minutes=x.minute - y.minute)
3156 self.assertEqual(got, expected)
3157
3158 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3159 # ignored.
3160 base = cls(8, 9, 10, 11, 12, 13, 14)
3161 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3162 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3163 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3164 for x in d0, d1, d2:
3165 for y in d0, d1, d2:
3166 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00003167 if (x is d0 or x is d1) and (y is d0 or y is d1):
3168 expected = timedelta(0)
3169 elif x is y is d2:
3170 expected = timedelta(0)
3171 elif x is d2:
3172 expected = timedelta(minutes=(11-59)-0)
3173 else:
3174 assert y is d2
3175 expected = timedelta(minutes=0-(11-59))
3176 self.assertEqual(got, expected)
3177
Tim Peters60c76e42002-12-27 00:41:11 +00003178 def test_mixed_compare(self):
3179 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00003180 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00003181 self.assertEqual(t1, t2)
3182 t2 = t2.replace(tzinfo=None)
3183 self.assertEqual(t1, t2)
3184 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3185 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003186 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3187 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003188
Tim Peters0bf60bd2003-01-08 20:40:01 +00003189 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003190 class Varies(tzinfo):
3191 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003192 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003193 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003194 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003195 return self.offset
3196
3197 v = Varies()
3198 t1 = t2.replace(tzinfo=v)
3199 t2 = t2.replace(tzinfo=v)
3200 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3201 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3202 self.assertEqual(t1, t2)
3203
3204 # But if they're not identical, it isn't ignored.
3205 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003206 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003207
Tim Petersa98924a2003-05-17 05:55:19 +00003208 def test_subclass_datetimetz(self):
3209
3210 class C(self.theclass):
3211 theAnswer = 42
3212
3213 def __new__(cls, *args, **kws):
3214 temp = kws.copy()
3215 extra = temp.pop('extra')
3216 result = self.theclass.__new__(cls, *args, **temp)
3217 result.extra = extra
3218 return result
3219
3220 def newmeth(self, start):
3221 return start + self.hour + self.year
3222
3223 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3224
3225 dt1 = self.theclass(*args)
3226 dt2 = C(*args, **{'extra': 7})
3227
3228 self.assertEqual(dt2.__class__, C)
3229 self.assertEqual(dt2.theAnswer, 42)
3230 self.assertEqual(dt2.extra, 7)
3231 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3232 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3233
Tim Peters621818b2002-12-29 23:44:49 +00003234# Pain to set up DST-aware tzinfo classes.
3235
3236def first_sunday_on_or_after(dt):
3237 days_to_go = 6 - dt.weekday()
3238 if days_to_go:
3239 dt += timedelta(days_to_go)
3240 return dt
3241
3242ZERO = timedelta(0)
Alexander Belopolskyca94f552010-06-17 18:30:34 +00003243MINUTE = timedelta(minutes=1)
Tim Peters621818b2002-12-29 23:44:49 +00003244HOUR = timedelta(hours=1)
3245DAY = timedelta(days=1)
3246# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3247DSTSTART = datetime(1, 4, 1, 2)
3248# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003249# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3250# being standard time on that day, there is no spelling in local time of
3251# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3252DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003253
3254class USTimeZone(tzinfo):
3255
3256 def __init__(self, hours, reprname, stdname, dstname):
3257 self.stdoffset = timedelta(hours=hours)
3258 self.reprname = reprname
3259 self.stdname = stdname
3260 self.dstname = dstname
3261
3262 def __repr__(self):
3263 return self.reprname
3264
3265 def tzname(self, dt):
3266 if self.dst(dt):
3267 return self.dstname
3268 else:
3269 return self.stdname
3270
3271 def utcoffset(self, dt):
3272 return self.stdoffset + self.dst(dt)
3273
3274 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003275 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003276 # An exception instead may be sensible here, in one or more of
3277 # the cases.
3278 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003279 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003280
3281 # Find first Sunday in April.
3282 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3283 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3284
3285 # Find last Sunday in October.
3286 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3287 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3288
Tim Peters621818b2002-12-29 23:44:49 +00003289 # Can't compare naive to aware objects, so strip the timezone from
3290 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003291 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003292 return HOUR
3293 else:
3294 return ZERO
3295
Tim Peters521fc152002-12-31 17:36:56 +00003296Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3297Central = USTimeZone(-6, "Central", "CST", "CDT")
3298Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3299Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003300utc_real = FixedOffset(0, "UTC", 0)
3301# For better test coverage, we want another flavor of UTC that's west of
3302# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003303utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003304
3305class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003306 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003307 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003308 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003309
Tim Peters0bf60bd2003-01-08 20:40:01 +00003310 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003311
Tim Peters521fc152002-12-31 17:36:56 +00003312 # Check a time that's inside DST.
3313 def checkinside(self, dt, tz, utc, dston, dstoff):
3314 self.assertEqual(dt.dst(), HOUR)
3315
3316 # Conversion to our own timezone is always an identity.
3317 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003318
3319 asutc = dt.astimezone(utc)
3320 there_and_back = asutc.astimezone(tz)
3321
3322 # Conversion to UTC and back isn't always an identity here,
3323 # because there are redundant spellings (in local time) of
3324 # UTC time when DST begins: the clock jumps from 1:59:59
3325 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3326 # make sense then. The classes above treat 2:MM:SS as
3327 # daylight time then (it's "after 2am"), really an alias
3328 # for 1:MM:SS standard time. The latter form is what
3329 # conversion back from UTC produces.
3330 if dt.date() == dston.date() and dt.hour == 2:
3331 # We're in the redundant hour, and coming back from
3332 # UTC gives the 1:MM:SS standard-time spelling.
3333 self.assertEqual(there_and_back + HOUR, dt)
3334 # Although during was considered to be in daylight
3335 # time, there_and_back is not.
3336 self.assertEqual(there_and_back.dst(), ZERO)
3337 # They're the same times in UTC.
3338 self.assertEqual(there_and_back.astimezone(utc),
3339 dt.astimezone(utc))
3340 else:
3341 # We're not in the redundant hour.
3342 self.assertEqual(dt, there_and_back)
3343
Tim Peters327098a2003-01-20 22:54:38 +00003344 # Because we have a redundant spelling when DST begins, there is
3345 # (unforunately) an hour when DST ends that can't be spelled at all in
3346 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3347 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3348 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3349 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3350 # expressed in local time. Nevertheless, we want conversion back
3351 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003352 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003353 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003354 if dt.date() == dstoff.date() and dt.hour == 0:
3355 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003356 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003357 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3358 nexthour_utc += HOUR
3359 nexthour_tz = nexthour_utc.astimezone(tz)
3360 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003361 else:
Tim Peters327098a2003-01-20 22:54:38 +00003362 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003363
3364 # Check a time that's outside DST.
3365 def checkoutside(self, dt, tz, utc):
3366 self.assertEqual(dt.dst(), ZERO)
3367
3368 # Conversion to our own timezone is always an identity.
3369 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003370
3371 # Converting to UTC and back is an identity too.
3372 asutc = dt.astimezone(utc)
3373 there_and_back = asutc.astimezone(tz)
3374 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003375
Tim Peters1024bf82002-12-30 17:09:40 +00003376 def convert_between_tz_and_utc(self, tz, utc):
3377 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003378 # Because 1:MM on the day DST ends is taken as being standard time,
3379 # there is no spelling in tz for the last hour of daylight time.
3380 # For purposes of the test, the last hour of DST is 0:MM, which is
3381 # taken as being daylight time (and 1:MM is taken as being standard
3382 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003383 dstoff = self.dstoff.replace(tzinfo=tz)
3384 for delta in (timedelta(weeks=13),
3385 DAY,
3386 HOUR,
3387 timedelta(minutes=1),
3388 timedelta(microseconds=1)):
3389
Tim Peters521fc152002-12-31 17:36:56 +00003390 self.checkinside(dston, tz, utc, dston, dstoff)
3391 for during in dston + delta, dstoff - delta:
3392 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003393
Tim Peters521fc152002-12-31 17:36:56 +00003394 self.checkoutside(dstoff, tz, utc)
3395 for outside in dston - delta, dstoff + delta:
3396 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003397
Tim Peters621818b2002-12-29 23:44:49 +00003398 def test_easy(self):
3399 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003400 self.convert_between_tz_and_utc(Eastern, utc_real)
3401 self.convert_between_tz_and_utc(Pacific, utc_real)
3402 self.convert_between_tz_and_utc(Eastern, utc_fake)
3403 self.convert_between_tz_and_utc(Pacific, utc_fake)
3404 # The next is really dancing near the edge. It works because
3405 # Pacific and Eastern are far enough apart that their "problem
3406 # hours" don't overlap.
3407 self.convert_between_tz_and_utc(Eastern, Pacific)
3408 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003409 # OTOH, these fail! Don't enable them. The difficulty is that
3410 # the edge case tests assume that every hour is representable in
3411 # the "utc" class. This is always true for a fixed-offset tzinfo
3412 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3413 # For these adjacent DST-aware time zones, the range of time offsets
3414 # tested ends up creating hours in the one that aren't representable
3415 # in the other. For the same reason, we would see failures in the
3416 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3417 # offset deltas in convert_between_tz_and_utc().
3418 #
3419 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3420 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003421
Tim Petersf3615152003-01-01 21:51:37 +00003422 def test_tricky(self):
3423 # 22:00 on day before daylight starts.
3424 fourback = self.dston - timedelta(hours=4)
3425 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003426 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003427 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3428 # 2", we should get the 3 spelling.
3429 # If we plug 22:00 the day before into Eastern, it "looks like std
3430 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3431 # to 22:00 lands on 2:00, which makes no sense in local time (the
3432 # local clock jumps from 1 to 3). The point here is to make sure we
3433 # get the 3 spelling.
3434 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003435 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003436 self.assertEqual(expected, got)
3437
3438 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3439 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003440 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003441 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3442 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3443 # spelling.
3444 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003445 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003446 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003447
Tim Petersadf64202003-01-04 06:03:15 +00003448 # Now on the day DST ends, we want "repeat an hour" behavior.
3449 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3450 # EST 23:MM 0:MM 1:MM 2:MM
3451 # EDT 0:MM 1:MM 2:MM 3:MM
3452 # wall 0:MM 1:MM 1:MM 2:MM against these
3453 for utc in utc_real, utc_fake:
3454 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003455 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003456 # Convert that to UTC.
3457 first_std_hour -= tz.utcoffset(None)
3458 # Adjust for possibly fake UTC.
3459 asutc = first_std_hour + utc.utcoffset(None)
3460 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3461 # tz=Eastern.
3462 asutcbase = asutc.replace(tzinfo=utc)
3463 for tzhour in (0, 1, 1, 2):
3464 expectedbase = self.dstoff.replace(hour=tzhour)
3465 for minute in 0, 30, 59:
3466 expected = expectedbase.replace(minute=minute)
3467 asutc = asutcbase.replace(minute=minute)
3468 astz = asutc.astimezone(tz)
3469 self.assertEqual(astz.replace(tzinfo=None), expected)
3470 asutcbase += HOUR
3471
3472
Tim Peters710fb152003-01-02 19:35:54 +00003473 def test_bogus_dst(self):
3474 class ok(tzinfo):
3475 def utcoffset(self, dt): return HOUR
3476 def dst(self, dt): return HOUR
3477
3478 now = self.theclass.now().replace(tzinfo=utc_real)
3479 # Doesn't blow up.
3480 now.astimezone(ok())
3481
3482 # Does blow up.
3483 class notok(ok):
3484 def dst(self, dt): return None
3485 self.assertRaises(ValueError, now.astimezone, notok())
3486
Tim Peters52dcce22003-01-23 16:36:11 +00003487 def test_fromutc(self):
3488 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3489 now = datetime.utcnow().replace(tzinfo=utc_real)
3490 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3491 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3492 enow = Eastern.fromutc(now) # doesn't blow up
3493 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3494 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3495 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3496
3497 # Always converts UTC to standard time.
3498 class FauxUSTimeZone(USTimeZone):
3499 def fromutc(self, dt):
3500 return dt + self.stdoffset
3501 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3502
3503 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3504 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3505 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3506
3507 # Check around DST start.
3508 start = self.dston.replace(hour=4, tzinfo=Eastern)
3509 fstart = start.replace(tzinfo=FEastern)
3510 for wall in 23, 0, 1, 3, 4, 5:
3511 expected = start.replace(hour=wall)
3512 if wall == 23:
3513 expected -= timedelta(days=1)
3514 got = Eastern.fromutc(start)
3515 self.assertEqual(expected, got)
3516
3517 expected = fstart + FEastern.stdoffset
3518 got = FEastern.fromutc(fstart)
3519 self.assertEqual(expected, got)
3520
3521 # Ensure astimezone() calls fromutc() too.
3522 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3523 self.assertEqual(expected, got)
3524
3525 start += HOUR
3526 fstart += HOUR
3527
3528 # Check around DST end.
3529 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3530 fstart = start.replace(tzinfo=FEastern)
3531 for wall in 0, 1, 1, 2, 3, 4:
3532 expected = start.replace(hour=wall)
3533 got = Eastern.fromutc(start)
3534 self.assertEqual(expected, got)
3535
3536 expected = fstart + FEastern.stdoffset
3537 got = FEastern.fromutc(fstart)
3538 self.assertEqual(expected, got)
3539
3540 # Ensure astimezone() calls fromutc() too.
3541 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3542 self.assertEqual(expected, got)
3543
3544 start += HOUR
3545 fstart += HOUR
3546
Tim Peters710fb152003-01-02 19:35:54 +00003547
Tim Peters528ca532004-09-16 01:30:50 +00003548#############################################################################
3549# oddballs
3550
3551class Oddballs(unittest.TestCase):
3552
3553 def test_bug_1028306(self):
3554 # Trying to compare a date to a datetime should act like a mixed-
3555 # type comparison, despite that datetime is a subclass of date.
3556 as_date = date.today()
3557 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003558 self.assertTrue(as_date != as_datetime)
3559 self.assertTrue(as_datetime != as_date)
3560 self.assertTrue(not as_date == as_datetime)
3561 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003562 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3563 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3564 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3565 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3566 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3567 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3568 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3569 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3570
3571 # Neverthelss, comparison should work with the base-class (date)
3572 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003573 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003574 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003575 as_different = as_datetime.replace(day= different_day)
3576 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003577
3578 # And date should compare with other subclasses of date. If a
3579 # subclass wants to stop this, it's up to the subclass to do so.
3580 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3581 self.assertEqual(as_date, date_sc)
3582 self.assertEqual(date_sc, as_date)
3583
3584 # Ditto for datetimes.
3585 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3586 as_date.day, 0, 0, 0)
3587 self.assertEqual(as_datetime, datetime_sc)
3588 self.assertEqual(datetime_sc, as_datetime)
3589
Tim Peters2a799bf2002-12-16 20:18:38 +00003590def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003591 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003592
3593if __name__ == "__main__":
3594 test_main()